• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: NSSplitView resizing
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: NSSplitView resizing


  • Subject: Re: NSSplitView resizing
  • From: "email@hidden" <email@hidden>
  • Date: Wed, 26 Aug 2009 17:14:54 +0100


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 (email@hidden)

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:
This email sent to email@hidden

Jonathan Mitchell

Developer
http://www.mugginsoft.com





_______________________________________________

Cocoa-dev mailing list (email@hidden)

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:
This email sent to email@hidden


References: 
 >NSSplitView resizing (From: Oftenwrong Soong <email@hidden>)

  • Prev by Date: Re: NSSplitView resizing
  • Next by Date: Re: How to create GPS enabled Iphone application?
  • Previous by thread: Re: NSSplitView resizing
  • Next by thread: Re: NSSplitView resizing
  • Index(es):
    • Date
    • Thread