2
// RBSplitView.h version 1.2
5
// Created by Rainer Brockerhoff on 24/09/2004.
6
// Copyright 2004-2009 Rainer Brockerhoff.
7
// Some Rights Reserved under the Creative Commons Attribution License, version 2.5, and/or the MIT License.
10
#import "RBSplitSubview.h"
12
// These values are used to handle the various cursor types.
14
RBSVHorizontalCursor=0, // appears over horizontal dividers
15
RBSVVerticalCursor, // appears over vertical dividers
16
RBSV2WayCursor, // appears over two-way thumbs
17
RBSVDragCursor, // appears while dragging
21
@interface RBSplitView : RBSplitSubview {
22
// Subclasses normally should use setter methods instead of changing instance variables by assignment.
23
// Most getter methods simply return the corresponding instance variable, so with some care, subclasses
24
// could reference them directly.
25
IBOutlet id delegate; // The delegate (may be nil).
26
NSString* autosaveName; // This name is used for storing subview proportions in user defaults.
27
NSColor* background; // The color used to paint the view's background (may be nil).
28
NSImage* divider; // The image used for the divider "dimple".
29
NSRect* dividers; // A C array of NSRects, one for each divider.
30
CGFloat dividerThickness; // Actual divider width; should be an integer and at least 1.0.
31
BOOL mustAdjust; // Set internally if the subviews need to be adjusted.
32
BOOL mustClearFractions; // Set internally if fractions should be cleared before adjusting.
33
BOOL isHorizontal; // The divider's orientation; default is vertical.
34
BOOL canSaveState; // Set internally to allow saving subview state.
35
BOOL isCoupled; // If YES, take some parameters from the containing RBSplitView, if any.
36
BOOL isAdjusting; // Set internally while the subviews are being adjusted.
37
BOOL isDragging; // Set internally while in a drag loop.
38
BOOL isInScrollView; // Set internally if directly contained in an NSScrollView.
41
// These class methods get and set the cursor used for each type.
42
// Pass in nil to reset to the default cursor for that type.
43
+ (NSCursor*)cursor:(RBSVCursorType)type;
44
+ (void)setCursor:(RBSVCursorType)type toCursor:(NSCursor*)cursor;
46
// This class method clears the saved state for a given autosave name from the defaults.
47
+ (void)removeStateUsingName:(NSString*)name;
49
// This class method returns the actual key used to store autosave data in the defaults.
50
+ (NSString*)defaultsKeyForName:(NSString*)name isHorizontal:(BOOL)orientation;
52
// Sets and gets the autosaveName; this will be the key used to store the subviews' proportions
53
// in the user defaults. Default is @"", which doesn't save anything. Set flag to YES to set
54
// unique names for nested subviews. You are responsible for avoiding duplicates.
55
- (void)setAutosaveName:(NSString*)aString recursively:(BOOL)flag;
56
- (NSString*)autosaveName;
58
// Saves the current state of the subviews if there's a valid autosave name set. If the argument
59
// is YES, it's then also called recursively for nested RBSplitViews. Returns YES if successful.
60
- (BOOL)saveState:(BOOL)recurse;
62
// Restores the saved state of the subviews if there's a valid autosave name set. If the argument
63
// is YES, it's first called recursively for nested RBSplitViews. Returns YES if successful.
64
// You need to call adjustSubviews after calling this.
65
- (BOOL)restoreState:(BOOL)recurse;
67
// Returns a string encoding the current state of all direct subviews. Does not check for nesting.
68
- (NSString*)stringWithSavedState;
70
// Readjusts all direct subviews according to the encoded string parameter. The number of subviews
71
// must match. Returns YES if successful. Does not check for nesting.
72
- (BOOL)setStateFromString:(NSString*)aString;
74
// Returns an array with complete state information for the receiver and all subviews, taking
75
// nesting into account. Don't store this array in a file, as its format might change in the
76
// future; this is for taking a state snapshot and later restoring it with setStatesFromArray.
77
- (NSArray*)arrayWithStates;
79
// Restores the state of the receiver and all subviews. The array must have been produced by a
80
// previous call to arrayWithStates. Returns YES if successful. This will fail if you have
81
// added or removed subviews in the meantime!
82
// You need to call adjustSubviews after calling this.
83
- (BOOL)setStatesFromArray:(NSArray*)array;
85
// This is the designated initializer for creating RBSplitViews programmatically.
86
- (id)initWithFrame:(NSRect)frame;
88
// This convenience initializer adds any number of subviews and adjusts them proportionally.
89
- (id)initWithFrame:(NSRect)frame andSubviews:(NSUInteger)count;
91
// Sets and gets the delegate. (Delegates aren't retained.) See further down for delegate methods.
92
- (void)setDelegate:(id)anObject;
95
// Returns a subview which has a certain identifier string, or nil if there's none
96
- (RBSplitSubview*)subviewWithIdentifier:(NSString*)anIdentifier;
98
// Returns the subview at a certain position. Returns nil if the position is invalid.
99
- (RBSplitSubview*)subviewAtPosition:(NSUInteger)position;
101
// Adds a subview at a certain position.
102
- (void)addSubview:(NSView*)aView atPosition:(NSUInteger)position;
104
// Sets and gets the divider thickness, which should be a positive integer or zero.
105
// Setting the divider image also resets this automatically, so you would call this
106
// only if you want the divider to be larger or smaller than the image. Zero means that
107
// the image dimensions will be used.
108
- (void)setDividerThickness:(CGFloat)thickness;
109
- (CGFloat)dividerThickness;
111
// Sets and gets the divider image. The default image can also be set in Interface Builder, so usually
112
// there's no need to call this. Passing in nil means that the default divider thickness will be zero,
113
// and no mouse events will be processed, so that the dividers can be moved only programmatically.
114
- (void)setDivider:(NSImage*)image;
117
// Sets and gets the view background. The default is nil, meaning no background is
118
// drawn and the view and its subviews are considered transparent.
119
- (void)setBackground:(NSColor*)color;
120
- (NSColor*)background;
122
// Sets and gets the orientation. This uses the same convention as NSSplitView: vertical means the
123
// dividers are vertical, but the subviews are in a horizontal row. Sort of counter-intuitive, yes.
124
- (void)setVertical:(BOOL)flag;
126
- (void)setHorizontal:(BOOL)flag;
127
- (BOOL)isHorizontal;
129
// Call this to force adjusting the subviews before display. Called automatically if anything
130
// relevant is changed.
131
- (void)setMustAdjust;
133
// Returns YES if there's a pending adjustment.
137
// Returns YES if we're in a dragging loop.
140
// Returns YES if the view is directly contained in an NSScrollView.
141
- (BOOL)isInScrollView;
143
// Call this to recalculate all subview dimensions. Normally this is done automatically whenever
144
// something relevant is changed, so you rarely will need to call this explicitly.
145
- (void)adjustSubviews;
147
// This method should be called only from within the splitView:wasResizedFrom:to: delegate method
148
// to keep some specific subview the same size.
149
- (void)adjustSubviewsExcepting:(RBSplitSubview*)excepting;
151
// This method draws dividers. You should never call it directly but you can override it when
152
// subclassing, if you need custom dividers.
153
- (void)drawDivider:(NSImage*)anImage inRect:(NSRect)rect betweenView:(RBSplitSubview*)leading andView:(RBSplitSubview*)trailing;
157
// The following methods are optionally implemented by the delegate.
159
@protocol RBSplitViewDelegate<NSObject>
162
// The delegate can override a subview's ability to collapse by implementing this method.
163
// Return YES to allow collapsing. If this is implemented, the subviews' built-in
164
// 'collapsed' flags are ignored.
165
- (BOOL)splitView:(RBSplitView*)sender canCollapse:(RBSplitSubview*)subview;
167
// The delegate can alter the divider's appearance by implementing this method.
168
// Before calling this, the divider is filled with the background, and afterwards
169
// the divider image is drawn into the returned rect. If imageRect is empty, no
170
// divider image will be drawn, because there are nested RBSplitViews. Return
171
// NSZeroRect to suppress the divider image. Return imageRect to use the default
172
// location for the image, or change its origin to place the image elsewhere.
173
// You could also draw the divider yourself at this point and return NSZeroRect.
174
- (NSRect)splitView:(RBSplitView*)sender willDrawDividerInRect:(NSRect)dividerRect betweenView:(RBSplitSubview*)leading andView:(RBSplitSubview*)trailing withProposedRect:(NSRect)imageRect;
176
// These methods are called after a subview is completely collapsed or expanded. adjustSubviews may or may not
177
// have been called, however.
178
- (void)splitView:(RBSplitView*)sender didCollapse:(RBSplitSubview*)subview;
179
- (void)splitView:(RBSplitView*)sender didExpand:(RBSplitSubview*)subview;
181
// These methods are called just before and after adjusting subviews.
182
- (void)willAdjustSubviews:(RBSplitView*)sender;
183
- (void)didAdjustSubviews:(RBSplitView*)sender;
185
// This method will be called after a RBSplitView is resized with setFrameSize: but before
186
// adjustSubviews is called on it.
187
- (void)splitView:(RBSplitView*)sender wasResizedFrom:(CGFloat)oldDimension to:(CGFloat)newDimension;
189
// This method will be called when a divider is double-clicked and both leading and trailing
190
// subviews can be collapsed. Return either of the parameters to collapse that subview, or nil
191
// to collapse neither. If not implemented, the smaller subview will be collapsed.
192
- (RBSplitSubview*)splitView:(RBSplitView*)sender collapseLeading:(RBSplitSubview*)leading orTrailing:(RBSplitSubview*)trailing;
194
// This method will be called when a cursor rect is being set (inside resetCursorRects). The
195
// proposed rect is passed in. Return the actual rect, or NSZeroRect to suppress cursor setting
196
// for this divider. This won't be called for two-axis thumbs, however. The rects are in
197
// sender's local coordinates.
198
- (NSRect)splitView:(RBSplitView*)sender cursorRect:(NSRect)rect forDivider:(NSUInteger)divider;
200
// This method will be called whenever a mouse-down event is received in a divider. Return YES to have
201
// the event handled by the split view, NO if you wish to ignore it or handle it in the delegate.
202
- (BOOL)splitView:(RBSplitView*)sender shouldHandleEvent:(NSEvent*)theEvent inDivider:(NSUInteger)divider betweenView:(RBSplitSubview*)leading andView:(RBSplitSubview*)trailing;
204
// This method will be called just before a subview will be collapsed or expanded with animation.
205
// Return the approximate time the animation should take, or 0.0 to disallow animation.
206
// If not implemented, it will use the default of 0.2 seconds per 150 pixels.
207
- (NSTimeInterval)splitView:(RBSplitView*)sender willAnimateSubview:(RBSplitSubview*)subview withDimension:(CGFloat)dimension;
209
// This method will be called whenever a subview's frame is changed, usually from inside adjustSubviews' final loop.
210
// You'd normally use this to move some auxiliary view to keep it aligned with the subview.
211
- (void)splitView:(RBSplitView*)sender changedFrameOfSubview:(RBSplitSubview*)subview from:(NSRect)fromRect to:(NSRect)toRect;
213
// This method is called whenever the event handlers want to check if some point within the RBSplitSubview
214
// should act as an alternate drag view. Usually, the delegate will check the point (which is in sender's
215
// local coordinates) against the frame of one or several auxiliary views, and return a valid divider number.
216
// Returning NSNotFound means the point is not valid.
217
- (NSUInteger)splitView:(RBSplitView*)sender dividerForPoint:(NSPoint)point inSubview:(RBSplitSubview*)subview;
219
// This method is called continuously while a divider is dragged, just before the leading subview is resized.
220
// Return NO to resize the trailing view by the same amount, YES to resize the containing window by the same amount.
221
- (BOOL)splitView:(RBSplitView*)sender shouldResizeWindowForDivider:(NSUInteger)divider betweenView:(RBSplitSubview*)leading andView:(RBSplitSubview*)trailing willGrow:(BOOL)grow;
223
// This method is called by each subview's drawRect: method, just after filling it with the background color but
224
// before the contained subviews are drawn. Usually you would use this to draw a frame inside the subview.
225
- (void)splitView:(RBSplitView*)sender willDrawSubview:(RBSplitSubview*)subview inRect:(NSRect)rect;