2 // RBSplitView.h version 1.1.4
5 // Created by Rainer Brockerhoff on 24/09/2004.
6 // Copyright 2004-2006 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 float 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
:(unsigned)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
:(unsigned)position
;
101 // Adds a subview at a certain position.
102 - (void)addSubview
:(NSView
*)aView atPosition
:(unsigned)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
:(float)thickness
;
109 - (float)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 - (BOOL
)isHorizontal
;
128 // Call this to force adjusting the subviews before display. Called automatically if anything
129 // relevant is changed.
130 - (void)setMustAdjust
;
132 // Returns YES if there's a pending adjustment.
135 // Returns YES if we're in a dragging loop.
138 // Returns YES if the view is directly contained in an NSScrollView.
139 - (BOOL
)isInScrollView
;
141 // Call this to recalculate all subview dimensions. Normally this is done automatically whenever
142 // something relevant is changed, so you rarely will need to call this explicitly.
143 - (void)adjustSubviews
;
145 // This method should be called only from within the splitView:wasResizedFrom:to: delegate method
146 // to keep some specific subview the same size.
147 - (void)adjustSubviewsExcepting
:(RBSplitSubview
*)excepting
;
149 // This method draws dividers. You should never call it directly but you can override it when
150 // subclassing, if you need custom dividers.
151 - (void)drawDivider
:(NSImage
*)anImage inRect
:(NSRect
)rect betweenView
:(RBSplitSubview
*)leading andView
:(RBSplitSubview
*)trailing
;
155 // The following methods are optionally implemented by the delegate.
157 @interface
NSObject(RBSplitViewDelegate
)
159 // The delegate can override a subview's ability to collapse by implementing this method.
160 // Return YES to allow collapsing. If this is implemented, the subviews' built-in
161 // 'collapsed' flags are ignored.
162 - (BOOL
)splitView
:(RBSplitView
*)sender canCollapse
:(RBSplitSubview
*)subview
;
164 // The delegate can alter the divider's appearance by implementing this method.
165 // Before calling this, the divider is filled with the background, and afterwards
166 // the divider image is drawn into the returned rect. If imageRect is empty, no
167 // divider image will be drawn, because there are nested RBSplitViews. Return
168 // NSZeroRect to suppress the divider image. Return imageRect to use the default
169 // location for the image, or change its origin to place the image elsewhere.
170 // You could also draw the divider yourself at this point and return NSZeroRect.
171 - (NSRect
)splitView
:(RBSplitView
*)sender willDrawDividerInRect
:(NSRect
)dividerRect betweenView
:(RBSplitSubview
*)leading andView
:(RBSplitSubview
*)trailing withProposedRect
:(NSRect
)imageRect
;
173 // These methods are called after a subview is completely collapsed or expanded. adjustSubviews may or may not
174 // have been called, however.
175 - (void)splitView
:(RBSplitView
*)sender didCollapse
:(RBSplitSubview
*)subview
;
176 - (void)splitView
:(RBSplitView
*)sender didExpand
:(RBSplitSubview
*)subview
;
178 // These methods are called just before and after adjusting subviews.
179 - (void)willAdjustSubviews
:(RBSplitView
*)sender
;
180 - (void)didAdjustSubviews
:(RBSplitView
*)sender
;
182 // This method will be called after a RBSplitView is resized with setFrameSize: but before
183 // adjustSubviews is called on it.
184 - (void)splitView
:(RBSplitView
*)sender wasResizedFrom
:(float)oldDimension to
:(float)newDimension
;
186 // This method will be called when a divider is double-clicked and both leading and trailing
187 // subviews can be collapsed. Return either of the parameters to collapse that subview, or nil
188 // to collapse neither. If not implemented, the smaller subview will be collapsed.
189 - (RBSplitSubview
*)splitView
:(RBSplitView
*)sender collapseLeading
:(RBSplitSubview
*)leading orTrailing
:(RBSplitSubview
*)trailing
;
191 // This method will be called when a cursor rect is being set (inside resetCursorRects). The
192 // proposed rect is passed in. Return the actual rect, or NSZeroRect to suppress cursor setting
193 // for this divider. This won't be called for two-axis thumbs, however. The rects are in
194 // sender's local coordinates.
195 - (NSRect
)splitView
:(RBSplitView
*)sender cursorRect
:(NSRect
)rect forDivider
:(unsigned int)divider
;
197 // This method will be called whenever a mouse-down event is received in a divider. Return YES to have
198 // the event handled by the split view, NO if you wish to ignore it or handle it in the delegate.
199 - (BOOL
)splitView
:(RBSplitView
*)sender shouldHandleEvent
:(NSEvent
*)theEvent inDivider
:(unsigned int)divider betweenView
:(RBSplitSubview
*)leading andView
:(RBSplitSubview
*)trailing
;
201 // This method will be called just before a subview will be collapsed or expanded with animation.
202 // Return the approximate time the animation should take, or 0.0 to disallow animation.
203 // If not implemented, it will use the default of 0.2 seconds per 150 pixels.
204 - (NSTimeInterval
)splitView
:(RBSplitView
*)sender willAnimateSubview
:(RBSplitSubview
*)subview withDimension
:(float)dimension
;
206 // This method will be called whenever a subview's frame is changed, usually from inside adjustSubviews' final loop.
207 // You'd normally use this to move some auxiliary view to keep it aligned with the subview.
208 - (void)splitView
:(RBSplitView
*)sender changedFrameOfSubview
:(RBSplitSubview
*)subview from
:(NSRect
)fromRect to
:(NSRect
)toRect
;
210 // This method is called whenever the event handlers want to check if some point within the RBSplitSubview
211 // should act as an alternate drag view. Usually, the delegate will check the point (which is in sender's
212 // local coordinates) against the frame of one or several auxiliary views, and return a valid divider number.
213 // Returning NSNotFound means the point is not valid.
214 - (unsigned int)splitView
:(RBSplitView
*)sender dividerForPoint
:(NSPoint
)point inSubview
:(RBSplitSubview
*)subview
;
216 // This method is called continuously while a divider is dragged, just before the leading subview is resized.
217 // Return NO to resize the trailing view by the same amount, YES to resize the containing window by the same amount.
218 - (BOOL
)splitView
:(RBSplitView
*)sender shouldResizeWindowForDivider
:(unsigned int)divider betweenView
:(RBSplitSubview
*)leading andView
:(RBSplitSubview
*)trailing willGrow
:(BOOL
)grow
;
220 // This method is called by each subview's drawRect: method, just after filling it with the background color but
221 // before the contained subviews are drawn. Usually you would use this to draw a frame inside the subview.
222 - (void)splitView
:(RBSplitView
*)sender willDrawSubview
:(RBSplitSubview
*)subview inRect
:(NSRect
)rect
;