Re: expanding the view area of a window - please help
Re: expanding the view area of a window - please help
- Subject: Re: expanding the view area of a window - please help
- From: Kurt Revis <email@hidden>
- Date: Mon, 18 Mar 2002 08:12:51 -0800
On Monday, March 18, 2002, at 07:34 AM, Manuel Arturo Marques Pita
wrote:
What I'd like to do for ths second option (customise) is to have the
same
thing they implement in applications such as Illustrator in which at the
botton of a window/panel there is a long rectangular button that
expands that
window and shows more detailed controls. Pressing the button again
collapses
the window to its original state, this happens in a nice animated
fashion...
Warning, long explanation ahead!
There are a couple of different things to do here.
* To resize the window, use -[NSWindow setFrame:display:animate:].
* To add the extra controls, get the view that they should be inside of
(this may be your window's content view, or some other NSView or NSBox
inside of that), and add each extra view using -[NSView addSubview:].
Getting all of the other details right is a little tricky, though:
* You need to make sure that your extra controls are in the nextKeyView
loop when they are in the window, and take them out of the loop when
they aren't.
* If your window is resizeable, and the "normal" controls in it are set
up to resize with the window, their resize behavior needs to be
different while the window is automatically resizing. (I forget all of
the details...it's been a while since I handled this.)
* If your window isn't visible, don't animate the resize--it will take
time even though nothing is actually shown on screen.
Here is the class I use. To see it in action, download my app MIDI
Monitor:
http://www.snoize.com/MIDIMonitor/
You use it by creating your window in a nib in the normal way, with
everything set up in the configuration when the extra controls are
shown. Select the extra controls, and select Layout->Make Subviews
Of->Custom View in IB. Make the custom view be an instance of this
class (DisclosableView). Hook up a button to the -toggleDisclosure:
action of the DisclosableView.
(You will undoubtedly want to experiment with how your controls and the
disclosable view are positioned. You can also set the disclosable view
to have a non-zero height when the items in it are hidden, using
-setHiddenHeight:.)
Hope this helps. Does anyone else have an implementation of something
similar? I don't claim that mine is particularly fantastic or general or
anything, but it works for me.
-------------------------------------------------------
DisclosableView.h:
-------------------------------------------------------
#import <Cocoa/Cocoa.h>
@interface DisclosableView : NSView
{
BOOL isShown;
double originalHeight;
double hiddenHeight;
NSArray *hiddenSubviews;
NSView *nonretainedOriginalNextKeyView;
NSView *nonretainedLastChildKeyView;
NSSize sizeBeforeHidden;
}
- (BOOL)isShown;
- (void)setIsShown:(BOOL)value;
- (double)hiddenHeight;
- (void)setHiddenHeight:(double)value;
// Actions
- (IBAction)toggleDisclosure:(id)sender;
- (IBAction)hide:(id)sender;
- (IBAction)show:(id)sender;
@end
-------------------------------------------------------
DisclosableView.m:
-------------------------------------------------------
#import "DisclosableView.h"
@interface DisclosableView (Private)
- (void)changeWindowHeightBy:(double)amount;
@end
@implementation DisclosableView
- (id)initWithFrame:(NSRect)frameRect;
{
if (!(self = [super initWithFrame:frameRect]))
return nil;
isShown = YES;
originalHeight = [self frame].size.height;
hiddenHeight = 0;
return self;
}
- (void)dealloc;
{
[hiddenSubviews release];
[super dealloc];
}
- (void)awakeFromNib;
{
if ([[self superclass]
instancesRespondToSelector:@selector(awakeFromNib)])
[super awakeFromNib];
isShown = YES;
originalHeight = [self frame].size.height;
}
- (BOOL)acceptsFirstResponder
{
return NO;
}
- (BOOL)isShown;
{
return isShown;
}
- (void)setIsShown:(BOOL)value;
{
if (isShown != value) {
if (value)
[self show:nil];
else
[self hide:nil];
}
}
- (double)hiddenHeight;
{
return hiddenHeight;
}
- (void)setHiddenHeight:(double)value;
{
hiddenHeight = value;
}
//
// Actions
//
- (IBAction)toggleDisclosure:(id)sender;
{
if (isShown)
[self hide:sender];
else
[self show:sender];
}
- (IBAction)hide:(id)sender;
{
NSView *keyLoopView;
unsigned int subviewIndex;
if (!isShown)
return;
keyLoopView = [self nextKeyView];
if ([keyLoopView isDescendantOf:self]) {
// We need to remove our subviews (which will be hidden) from
the key loop.
// Remember our nextKeyView so we can restore it later.
nonretainedOriginalNextKeyView = keyLoopView;
// Find the last view in the key loop which is one of our
descendants.
nonretainedLastChildKeyView = keyLoopView;
while ((keyLoopView = [nonretainedLastChildKeyView
nextKeyView])) {
if ([keyLoopView isDescendantOf:self])
nonretainedLastChildKeyView = keyLoopView;
else
break;
}
// Set our nextKeyView to its nextKeyView, and set its
nextKeyView to nil.
// (If we don't do the last step, when we restore the key loop
later, it will be missing views in the backwards direction.)
[self setNextKeyView:keyLoopView];
[nonretainedLastChildKeyView setNextKeyView:nil];
} else {
nonretainedOriginalNextKeyView = nil;
}
hiddenSubviews = [[NSArray alloc] initWithArray:[self subviews]];
subviewIndex = [hiddenSubviews count];
while (subviewIndex--)
[[hiddenSubviews objectAtIndex:subviewIndex]
removeFromSuperview];
sizeBeforeHidden = [self frame].size;
[self setFrameSize:NSMakeSize(sizeBeforeHidden.width, hiddenHeight)];
[self changeWindowHeightBy:-(originalHeight - hiddenHeight)];
isShown = NO;
}
- (IBAction)show:(id)sender;
{
unsigned int subviewIndex;
if (isShown)
return;
[self changeWindowHeightBy:(originalHeight - hiddenHeight)];
[self setFrameSize:NSMakeSize([self frame].size.width,
originalHeight)];
subviewIndex = [hiddenSubviews count];
while (subviewIndex--)
[self addSubview:[hiddenSubviews objectAtIndex:subviewIndex]];
[hiddenSubviews release];
hiddenSubviews = nil;
[self resizeSubviewsWithOldSize:sizeBeforeHidden];
if (nonretainedOriginalNextKeyView) {
// Restore the key loop to its old configuration.
[nonretainedLastChildKeyView setNextKeyView:[self nextKeyView]];
[self setNextKeyView:nonretainedOriginalNextKeyView];
}
isShown = YES;
}
@end
@implementation DisclosableView (Private)
- (void)changeWindowHeightBy:(double)amount;
{
// This turns out to be more complicated than one might expect,
because the way that the other views in the window should move is
different than the normal case that the AppKit handles.
// We want the other views in the window to stay the same size. If a
view is above us, we want it to stay in the same position relative to
the top of the window; likewise, if a view is below us, we want it to
stay in the same position relative to the bottom of the window. However,
views may have different autoresize masks configured.
// So, we save the old autoresize masks for all of the window's
content view's immediate subviews, and set them how we want them.
// Then, we resize the window, and fix up the minimum and maximum
sizes for the window.
// Afterwards, we restore the autoresize masks.
// (Also note that we do not want to modify our own autoresize mask.)
NSWindow *window;
NSView *contentView;
NSArray *windowSubviews;
unsigned int windowSubviewCount, windowSubviewIndex;
NSMutableArray *savedAutoresizeMasks;
NSRect newWindowFrame;
NSSize newWindowMinOrMaxSize;
window = [self window];
contentView = [window contentView];
windowSubviews = [contentView subviews];
windowSubviewCount = [windowSubviews count];
savedAutoresizeMasks = [NSMutableArray
arrayWithCapacity:windowSubviewCount];
for (windowSubviewIndex = 0; windowSubviewIndex <
windowSubviewCount; windowSubviewIndex++) {
NSView *windowSubview;
unsigned int autoresizingMask;
windowSubview = [windowSubviews
objectAtIndex:windowSubviewIndex];
autoresizingMask = [windowSubview autoresizingMask];
[savedAutoresizeMasks addObject:[NSNumber
numberWithUnsignedInt:autoresizingMask]];
if (windowSubview == self)
continue;
// We never want to anything to change height.
autoresizingMask &= ~NSViewHeightSizable;
if (NSMaxY([windowSubview frame]) >= NSMaxY([self frame])) {
// This subview is above us. Set it to be stuck to the top
of the window.
autoresizingMask &= ~NSViewMaxYMargin;
autoresizingMask |= NSViewMinYMargin;
} else {
// This subview is below us. Set it to be stuck to the
bottom of the window.
autoresizingMask |= NSViewMaxYMargin;
autoresizingMask &= ~NSViewMinYMargin;
}
[windowSubview setAutoresizingMask:autoresizingMask];
}
newWindowFrame = [window frame];
newWindowFrame.origin.y -= amount;
newWindowFrame.size.height += amount;
if ([window isVisible])
[window setFrame:newWindowFrame display:YES animate:YES];
else
[window setFrame:newWindowFrame display:NO];
newWindowMinOrMaxSize = [window minSize];
newWindowMinOrMaxSize.height += amount;
[window setMinSize:newWindowMinOrMaxSize];
newWindowMinOrMaxSize = [window maxSize];
// If there is no max size set (height of 0), don't change it.
if (newWindowMinOrMaxSize.height > 0) {
newWindowMinOrMaxSize.height += amount;
[window setMaxSize:newWindowMinOrMaxSize];
}
for (windowSubviewIndex = 0; windowSubviewIndex <
windowSubviewCount; windowSubviewIndex++) {
NSView *windowSubview;
windowSubview = [windowSubviews
objectAtIndex:windowSubviewIndex];
if (windowSubview == self)
continue;
[windowSubview setAutoresizingMask:[[savedAutoresizeMasks
objectAtIndex:windowSubviewIndex] unsignedIntValue]];
}
}
@end
--
Kurt Revis
email@hidden
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.