On 26 Aug 2009, at 16:48, Oftenwrong Soong wrote:

Hi all,

I have a window containing a NSSplitView. When the window is resized, both panes of the split view resize proportionally. I would like one pane to remain fixed while the other resizes. Xcode does this, as well as iCal and other apps.

Is there a delegate method that does this or must I subclass NSSplitView?

My approach has been to define a category on NSSplitView and call this from the delegate. The category implements a behaviour based implementation of resizeSubviewsWithOldSize: that supports min view sizes and fixed view identification. Works with two and three view NSSplitViews. Might not quite cover all orientations.

Delegate:
/*

 size splitview subviews as required

 */
- (void)splitView:(NSSplitView *)sender resizeSubviewsWithOldSize: (NSSize)oldSize
{       
        MGSSplitviewBehaviour behaviour;
        
        // note that a view does not provide a -setTag method only -tag
        // so views cannot be easily tagged without subclassing.
        // NSControl implements -setTag;
        //
        switch ([[sender subviews] count]) {
                case 2:
                        behaviour = MGSSplitviewBehaviourOf2ViewsFirstFixed;
                        break;
                        
                case 3:
                        behaviour = 
MGSSplitviewBehaviourOf3ViewsFirstAndSecondFixed;
                        break;
                        
                default:
                        NSAssert(NO, @"invalid number of views in splitview");
                        break;
        }

        // see the NSSplitView_Mugginsoft category
        [sender resizeSubviewsWithOldSize:oldSize withBehaviour:behaviour];
}

Category:

//
//  NSSplitView_Mugginsoft.h
//

#import <Cocoa/Cocoa.h>


typedef enum _MGSSplitviewBehaviour {
        MGSSplitviewBehaviourNone = -1,
MGSSplitviewBehaviourOf2ViewsFirstFixed = 0, // 2 views - first fixed width MGSSplitviewBehaviourOf2ViewsSecondFixed, // 2 views - second fixed width MGSSplitviewBehaviourOf3ViewsFirstAndSecondFixed, // 3 views - first and second fixed width MGSSplitviewBehaviourOf3ViewsFirstAndThirdFixed, // 3 views - first and third fixed width
} MGSSplitviewBehaviour;

@interface NSSplitView(Mugginsoft)
- (void)resizeSubviewsWithOldSize: (NSSize)oldSize withBehaviour: (MGSSplitviewBehaviour)behaviour; - (void)resizeSubviewsWithOldSize:(NSSize)oldSize withBehaviour: (MGSSplitviewBehaviour)behaviour minSizes:(NSArray *)minSizes;
- (void)logSubviewFrames;
//- (void)replaceSubview:(NSView *)oldView withViewSizedAsOld:(NSView *)newView;
@end

//
//  NSSplitView_Mugginsoft.m
//

#import "NSSplitView_Mugginsoft.h"

@implementation NSSplitView(Mugginsoft)

/*

 resize subviews with old size, with behaviour

 */
- (void)resizeSubviewsWithOldSize:(NSSize)oldSize withBehaviour: (MGSSplitviewBehaviour)behaviour
{
[self resizeSubviewsWithOldSize:oldSize withBehaviour:behaviour minSizes:nil];
}

/*
 resize subviews with old size, with behaviour and min sizes

 apply splitview behaviour

 note that NSSplitView uses flipped coordinates.

 subview at index 0 is leftmost or topmost
 x,y 0,0, is top left

 */
- (void)resizeSubviewsWithOldSize:(NSSize)oldSize withBehaviour: (MGSSplitviewBehaviour)behaviour minSizes:(NSArray *)minSizes
{
        if (behaviour == MGSSplitviewBehaviourNone) {
                return;
        }
        
        NSRect newFrame = [self frame];
        NSSize newSize = newFrame.size;
        CGFloat heightDelta = newSize.height - oldSize.height;
        CGFloat dividerThickness = [self dividerThickness];

        int subviewCount = [[self subviews] count];
        
        NSRect rect0 = [[[self subviews] objectAtIndex:0] frame];
        NSRect rect1 = [[[self subviews] objectAtIndex:1] frame];
        NSRect rect2;
        NSString *assertMsg = @"invalid splitview behaviour";
        CGFloat minSize0 = -1, minSize1 = -1, minSize2 = -1;
        CGFloat height0 = 0, height1= 0, height2 = 0, height1Delta = 0;
        CGFloat width0 = 0, width1= 0;
        
        // if minsizes defined
        if (minSizes) {
                // min sizes are widths or heights depending on context
if ([minSizes count] >= 1) minSize0 = [[minSizes objectAtIndex:0] doubleValue]; if ([minSizes count] >= 2) minSize1 = [[minSizes objectAtIndex:1] doubleValue]; if ([minSizes count] >= 3) minSize2 = [[minSizes objectAtIndex:2] doubleValue];
        }
        
        switch (subviewCount) {

                case 2:
                        if ([self isVertical]) {        // views are side by 
side
                                rect0.origin = NSMakePoint(0, 0);
                                rect0.size.height = newFrame.size.height;
                                rect1.size.height = newFrame.size.height;
                                
                                if (behaviour == 
MGSSplitviewBehaviourOf2ViewsFirstFixed) {
                                        
                                        // keep view at index 0 at current width
                                        // adjust view at index 1 accordingly
                                        width1 = newFrame.size.width - 
rect0.size.width - dividerThickness;
                                        if (width1 < minSize1) {
                                                width1 = minSize1;
                                        }

// if rect0 is below its min size but rect1 can accomodate it at its min size then allow this
                                        if (rect0.size.width < minSize0) {
if (newFrame.size.width - minSize0 - dividerThickness >= minSize1) {
                                                        width1 = 
newFrame.size.width - minSize0 - dividerThickness;
                                                }
                                        }
                                        
                                        rect1.size.width = width1;
rect0.size.width = newFrame.size.width - rect1.size.width - dividerThickness;
                                        rect1.origin = 
NSMakePoint(rect0.size.width + dividerThickness, 0);
                                        
                                } else if (behaviour == 
MGSSplitviewBehaviourOf2ViewsSecondFixed) {
                                        
                                        // keep view at index 1 at current width
                                        // adjust view at index 0 accordingly
                                        width0 = newFrame.size.width - 
rect1.size.width - dividerThickness;
                                        if (width0 < minSize0) {
                                                width0 = minSize0;
                                        }
                                        
// if rect1 is below its min size but rect0 can accomodate it at its min size then allow this
                                        if (rect1.size.width < minSize1) {
if (newFrame.size.width - minSize1 - dividerThickness >= minSize0) {
                                                        width0 = 
newFrame.size.width - minSize1 - dividerThickness;
                                                }
                                        }
                                        
                                        rect0.size.width = width0;
rect1.size.width = newFrame.size.width - rect0.size.width - dividerThickness; rect1.origin = NSMakePoint(rect0.origin.x + rect0.size.width + dividerThickness, 0);

                                } else {
                                        NSAssert(NO, assertMsg);
                                }
                                
                        } else {
                                
                                // views one above the other
rect0.origin = NSMakePoint(0, 0); // this reqd in cases where number of views has decreased from 3 to 2
                                rect0.size.width = newFrame.size.width;
                                rect1.size.width = newFrame.size.width;
                                
                                if (behaviour == 
MGSSplitviewBehaviourOf2ViewsFirstFixed) {
                                        
                                        // keep view at index 0 at current 
height
                                        // adjust view at index 1 accordingly
height1 = newFrame.size.height - rect0.size.height - dividerThickness;
                                        if (height1 < minSize1) {
                                                rect1.size.height = minSize1;
rect0.size.height = newFrame.size.height - rect1.size.height - dividerThickness;
                                        } else {
                                                rect1.size.height = height1;
                                        }
                                } else if (behaviour == 
MGSSplitviewBehaviourOf2ViewsSecondFixed) {     
                                        
                                        // keep view at index 1 at current 
height
                                        // adjust view at index 0 accordingly
height0 = newFrame.size.height - rect1.size.height - dividerThickness;
                                        if (height0 < minSize0) {
                                                rect0.size.height = minSize0;
rect1.size.height = newFrame.size.height - rect0.size.height - dividerThickness;
                                        } else {
                                                rect0.size.height = height0;
                                        }
                                        
                                } else {
                                        NSAssert(NO, assertMsg);
                                }
                                
                                rect1.origin = NSMakePoint(0, rect0.size.height 
+ dividerThickness);
                        }
                        
                        [[[self subviews] objectAtIndex:0] setFrame:rect0];
                        [[[self subviews] objectAtIndex:1] setFrame:rect1];
                        
                        break;
                        
                case 3:
                        rect2 = [[[self subviews] objectAtIndex:2] frame];
                        
                        if ([self isVertical]) {        // views are side by 
side
                                
                                if (behaviour == 
MGSSplitviewBehaviourOf3ViewsFirstAndSecondFixed) {
                                        
                                        // keep views at index 0 and 1 at 
current width
                                        // adjust view at index 2 accordingly
                                        
                                        rect0.origin = NSMakePoint(0, 0);
                                        rect0.size.height = 
newFrame.size.height;
                                        
                                        rect1.origin = 
NSMakePoint(rect0.size.width + dividerThickness, 0);
                                        rect1.size.height = 
newFrame.size.height;

rect2.origin = NSMakePoint(rect1.origin.x + rect1.size.width + dividerThickness, 0); rect2.size.width = newFrame.size.width - rect0.size.width - rect1.size.width - 2 * dividerThickness;
                                        rect2.size.height = 
newFrame.size.height;
                                        
                                } else {
                                        NSAssert(NO, assertMsg);
                                }
                                
                        } else {        // views are on top of each other
                                
                                if (behaviour == 
MGSSplitviewBehaviourOf3ViewsFirstAndThirdFixed) {

                                        rect0.origin = NSMakePoint(0, 0);
                                        rect0.size.width = newFrame.size.width;
                                        rect1.size.width = newFrame.size.width;
                                        rect2.size.width = newSize.width;

                                        height1 = [[[self subviews] 
objectAtIndex:1] frame].size.height;
                                        height1 += heightDelta;

                                        if (height1 >= minSize1) {

                                                // keep views at index 0 and 2 
at current height
                                                // adjust view at index 1 
accordingly
rect1.origin = NSMakePoint(0, rect0.size.height + dividerThickness); height1 = newFrame.size.height - rect0.size.height - rect2.size.height - 2 * dividerThickness;
                                                
// revalidate our height as this method is also called whenever subviews are added to the splitview // in which case we need to ensure that even though the frame size has not changed that the views
                                                // do not go beneath then 
minimum sizes
                                                if (height1 >= minSize1) {
                                                        rect1.size.height = 
height1;                                            
rect2.origin = NSMakePoint(0, rect1.origin.y + rect1.size.height + dividerThickness);
                                                }
                                        }
                                        
// if view height is too small then redistribute view heights accordingly
                                        if (height1 < minSize1) {
                                                
                                                // adjust request view to min 
height
                                                height1Delta = height1 - 
minSize1;
                                                heightDelta += height1Delta;
                                                
                                                // calc view heights
                                                height1 = minSize1;
                                                height0 = [[[self subviews] 
objectAtIndex:0] frame].size.height;
                                                height0 += heightDelta;
                                                if (height0 < minSize0) {
                                                        height0 = minSize0;
                                                }
height2 = newSize.height - 2 * dividerThickness - height0 - height1;
                                                
                                                rect0.size.height = height0;
                                                
rect1.origin.y = rect0.origin.y + rect0.size.height + dividerThickness;
                                                rect1.size.height = height1;
                                                
rect2.origin.y = rect1.origin.y + rect1.size.height +dividerThickness;
                                                rect2.size.height = height2;
                                        }
                                        
                                        
                                } else {
                                        NSAssert(NO, assertMsg);
                                }       
                                
                        }
                        
                        [[[self subviews] objectAtIndex:0] setFrame:rect0];
                        [[[self subviews] objectAtIndex:1] setFrame:rect1];
                        [[[self subviews] objectAtIndex:2] setFrame:rect2];
                        
                        break;
                        
                default:
                        NSAssert(NO, @"invalid number of subviews");
        }
}

/*

 log the subview frames

 */
- (void)logSubviewFrames
{
        // spliview coordinate system IS flipped
        NSLog(@"Splitview is flipped: %@", [self isFlipped] ? @"YES" : @"NO");
        
        for (int i = 0; i < [[self subviews] count]; i++) {
                NSRect frame = [[[self subviews] objectAtIndex:i] frame];
NSLog(@"subview %i : origin.x = %f : origin.y = %f : size.width = %f : size.height = %f", i, frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
        }
}
@end

Thanks,
Soong




_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/jonathan%40mugginsoft.com

This email sent to jonat...@mugginsoft.com

Jonathan Mitchell

Developer
http://www.mugginsoft.com





_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to