// // RBSplitView.h version 1.0.4 // RBSplitView // // Created by Rainer Brockerhoff on 24/09/2004. // Copyright 2004,2005 Rainer Brockerhoff. All rights reserved. // #import "RBSplitSubview.h" // RBSplitView is an alternative to NSSplitView. There are some serious limitations with NSSplitView if // you need to limit subview's sizes, expand or collapse subviews programmatically or by double-clicking, // or resize the split view frequently. // IMHO the general structure and delegate calls of NSSplitView, a legacy from NeXTstep, are at the root // of these problems. So RBSplitView is not a drop-in replacement for NSSplitView, although some methods // are similar. // RBSplitView makes special content views Ñ RBSplitSubviews Ñ handle details of subview limitations and // properties. So there's less or no work to be done by the delegate. // RBSplitViews use flipped coordinates, but this shouldn't be a hassle as the subviews do not. // RBSplitView is actually a subclass of RBSplitSubview. This allows direct nesting of RBSplitViews and // makes implementing coupled dividers much easier. Coupling can be disabled. @interface RBSplitView : RBSplitSubview { // Subclasses normally should use setter methods instead of changing instance variables by assignment. // Most getter methods simply return the corresponding instance variable, so with some care, subclasses // could reference them directly. IBOutlet id delegate; // The delegate (may be nil). NSString* autosaveName; // This name is used for storing subview proportions in user defaults. NSColor* background; // The color used to paint the view's background (may be nil). NSImage* divider; // The image used for the divider "dimple". NSRect* dividers; // A C array of NSRects, one for each divider. float dividerThickness; // Actual divider width; should be an integer and at least 1.0. BOOL mustAdjust; // Set internally if the subviews need to be adjusted. BOOL mustClearFractions; // Set internally if fractions should be cleared before adjusting. BOOL isHorizontal; // The divider's orientation; default is vertical. BOOL canSaveState; // Set internally to allow saving subview state. BOOL isCoupled; // If YES, take some parameters from the containing RBSplitView, if any. } // This class method clears the saved state for a given autosave name from the defaults. + (void)removeStateUsingName:(NSString*)name; // This class method returns the actual key used to store autosave data in the defaults. + (NSString*)defaultsKeyForName:(NSString*)name isHorizontal:(BOOL)orientation; // Sets and gets the autosaveName; this will be the key used to store the subviews' proportions // in the user defaults. Default is @"", which doesn't save anything. Set flag to YES to set // unique names for nested subviews. You are responsible for avoiding duplicates. - (void)setAutosaveName:(NSString*)aString recursively:(BOOL)flag; - (NSString*)autosaveName; // Saves the current state of the subviews if there's a valid autosave name set. If the argument // is YES, it's then also called recursively for nested RBSplitViews. Returns YES if successful. - (BOOL)saveState:(BOOL)recurse; // Restores the saved state of the subviews if there's a valid autosave name set. If the argument // is YES, it's first called recursively for nested RBSplitViews. Returns YES if successful. // You need to call adjustSubviews after calling this. - (BOOL)restoreState:(BOOL)recurse; // Returns a string encoding the current state of all direct subviews. Does not check for nesting. - (NSString*)stringWithSavedState; // Readjusts all direct subviews according to the encoded string parameter. The number of subviews // must match. Returns YES if successful. Does not check for nesting. - (BOOL)setStateFromString:(NSString*)aString; // This is the designated initializer for creating RBSplitViews programmatically. - (id)initWithFrame:(NSRect)frame; // This convenience initializer adds any number of subviews and adjusts them proportionally. - (id)initWithFrame:(NSRect)frame andSubviews:(unsigned)count; // Sets and gets the delegate. (Delegates aren't retained.) See further down for delegate methods. - (void)setDelegate:(id)anObject; - (id)delegate; // Returns a subview which has a certain identifier string, or nil if there's none - (RBSplitSubview*)subviewWithIdentifier:(NSString*)anIdentifier; // Returns the subview at a certain position. Returns nil if the position is invalid. - (RBSplitSubview*)subviewAtPosition:(unsigned)position; // Sets and gets the divider thickness, which should be a positive integer or zero. // Setting the divider image also resets this automatically, so you would call this // only if you want the divider to be larger or smaller than the image. Zero means that // the image dimensions will be used. - (void)setDividerThickness:(float)thickness; - (float)dividerThickness; // Sets and gets the divider image. The default image can also be set in Interface Builder, so usually // there's no need to call this. Passing in nil means that the default divider thickness will be zero, // and no mouse events will be processed, so that the dividers can be moved only programmatically. - (void)setDivider:(NSImage*)image; - (NSImage*)divider; // Sets and gets the view background. The default is nil, meaning no background is // drawn and the view and its subviews are considered transparent. - (void)setBackground:(NSColor*)color; - (NSColor*)background; // Sets and gets the orientation. This uses the same convention as NSSplitView: vertical means the // dividers are vertical, but the subviews are in a horizontal row. Sort of counter-intuitive, yes. - (void)setVertical:(BOOL)flag; - (BOOL)isVertical; - (BOOL)isHorizontal; // Call this to force adjusting the subviews before display. Called automatically if anything // relevant is changed. - (void)setMustAdjust; // Returns YES if there's a pending adjustment. - (BOOL)mustAdjust; // Call this to recalculate all subview dimensions. Normally this is done automatically whenever // something relevant is changed, so you rarely will need to call this explicitly. - (void)adjustSubviews; // This method should be called only from within the splitView:wasResizedFrom:to: delegate method // to keep some specific subview the same size. - (void)adjustSubviewsExcepting:(RBSplitSubview*)exception; @end // The following methods are optionally implemented by the delegate. @interface NSObject(RBSplitViewDelegate) // The delegate can override a subview's ability to collapse by implementing this method. // Return YES to allow collapsing. If this is implemented, the subviews' built-in // 'collapsed' flags are ignored. - (BOOL)splitView:(RBSplitView*)sender canCollapse:(RBSplitSubview*)subview; // The delegate can alter the divider's appearance by implementing this method. // Before calling this, the divider is filled with the background, and afterwards // the divider image is drawn into the returned rect. If imageRect is empty, no // divider image will be drawn, because there are nested RBSplitViews. Return // NSZeroRect to suppress the divider image. Return imageRect to use the default // location for the image, or change its coordinates to place the image elsewhere. - (NSRect)splitView:(RBSplitView*)sender willDrawDividerInRect:(NSRect)dividerRect betweenView:(RBSplitSubview*)leading andView:(RBSplitSubview*)trailing withProposedRect:(NSRect)imageRect; // These methods are called after a subview is completely collapsed or expanded. - (void)splitView:(RBSplitView*)sender didCollapse:(RBSplitSubview*)subview; - (void)splitView:(RBSplitView*)sender didExpand:(RBSplitSubview*)subview; // This method will be called after a RBSplitView is resized with setFrameSize: but before // adjustSubviews is called on it. - (void)splitView:(RBSplitView*)sender wasResizedFrom:(float)oldDimension to:(float)newDimension; // This method will be called when a divider is double-clicked and both leading and trailing // subviews can be collapsed. Return either of the parameters to collapse that subview, or nil // to collapse neither. If not implemented, the smaller subview will be collapsed. - (RBSplitSubview*)splitView:(RBSplitView*)sender collapseLeading:(RBSplitSubview*)leading orTrailing:(RBSplitSubview*)trailing; // This method will be called just before a subview will be collapsed or expanded with animation. // Return the approximate time the animation should take, or 0.0 to disallow animation. // If not implemented, it will use the default of 0.2 seconds per 150 pixels. - (NSTimeInterval)splitView:(RBSplitView*)sender willAnimateSubview:(RBSplitSubview*)subview withDimension:(float)dimension; @end