Apple-style expanding preferences panel
Apple-style expanding preferences panel
- Subject: Apple-style expanding preferences panel
- From: "Bobby B" <email@hidden>
- Date: Sun, 2 Apr 2006 13:00:09 -0400
Hey guys;
I thought I'd post this to the list so it would be archived somewhere,
and maybe someone who needs this can use it.
This is what I came up with for doing a Safari/Mail/iTunes style
Preferences Panel. You know, one that expands and contracts as
needed. I downloaded the Toolbar sample from Apple and added the
preferences panel stuff to it, and the actual call that sets the new
window's size I got from cocaodev.com.
The controller.m and controller.h (below controll.m) are posted below.
All you need to do use this is:
in Controller.h set the: IBOutlet NSWindow *window to be the outlet
for your window that you are using for preferences, and:
In your .nib file with the window, create CustomViews (drag them into
your .nib and then design them as you want), and make outlets for your
controller named prefsPanel1, prefsPanel2, etc.
Right now this supports up to five panels, and in this example below I
only use two panels. You can see how to add more panels easily. I
know some of the code is junk, and I used some work arounds,
including:
1. I have a method (function?) that displays the current panel, but I
couldn't figure out how to call it from the selector (from within
addToolbarItem), so I had to do a little "rigged" thing where I have
five different methods to show a panel, but each one just calls my
method (showPanelNumber(x,x,x,x))
2. When switching between windows, I would lose the bounds of my
NSView, so I just put them all in globals at the startup. This works
fine. I read on cocoadev.com it's something about [[window
contentview] retain], but it didn't work for me, and it gave me some
weird Mach .com error.
I'm going to try to figure out how to put this into an object so that
I can just hook it into all my future programs. Most of the comments
in the programs are from Apple from the Toolbar example.
Hope this helps someone,
Bobby
Controller.m:
#import "Controller.h"
// All NSToolbarItems have a unique identifer associated with them,
used to tell your delegate/controller what
// toolbar items to initialize and return at various points.
Typically, for a given identifier, you need to
// generate a copy of your "master" toolbar item, and return it
autoreleased. The function below takes an
// NSMutableDictionary to hold your master NSToolbarItems and a bunch
of NSToolbarItem paramenters,
// and it creates a new NSToolbarItem with those parameters, adding it
to the dictionary. Then the dictionary
// can be used from
-toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar: to generate
a new copy of the
// requested NSToolbarItem (when the toolbar wants to redraw, for
instance) by simply duplicating and returning
// the NSToolbarItem that has the same identifier in the dictionary.
Plus, it's easy to call this function
// repeatedly to generate lots of NSToolbarItems for your toolbar.
// -------
// label, palettelabel, toolTip, action, and menu can all be NULL,
depending upon what you want the item to do
static void addToolbarItem(NSMutableDictionary *theDict,NSString
*identifier,NSString *label,NSString *paletteLabel,NSString
*toolTip,id target,SEL settingSelector, id itemContent,SEL action,
NSMenu * menu)
{
NSMenuItem *mItem;
// here we create the NSToolbarItem and setup its attributes in
line with the parameters
NSToolbarItem *item = [[[NSToolbarItem alloc]
initWithItemIdentifier:identifier] autorelease];
[item setLabel:label];
[item setPaletteLabel:paletteLabel];
[item setToolTip:toolTip];
[item setTarget:target];
// the settingSelector parameter can either be @selector(setView:)
or @selector(setImage:). Pass in the right
// one depending upon whether your NSToolbarItem will have a
custom view or an image, respectively
// (in the itemContent parameter). Then this next line will do
the right thing automatically.
[item performSelector:settingSelector withObject:itemContent];
[item setAction:action];
// If this NSToolbarItem is supposed to have a menu "form
representation" associated with it (for text-only mode),
// we set it up here. Actually, you have to hand an NSMenuItem
(not a complete NSMenu) to the toolbar item,
// so we create a dummy NSMenuItem that has our real menu as a submenu.
if (menu!=NULL)
{
// we actually need an NSMenuItem here, so we construct one
mItem=[[[NSMenuItem alloc] init] autorelease];
[mItem setSubmenu: menu];
[mItem setTitle: [menu title]];
[item setMenuFormRepresentation:mItem];
}
// Now that we've setup all the settings for this new toolbar
item, we add it to the dictionary.
// The dictionary retains the toolbar item for us, which is why we
could autorelease it when we created
// it (above).
[theDict setObject:item forKey:identifier];
}
// Calculation, from Apple
float ToolbarHeightForWindow(NSWindow *window)
{
NSToolbar *toolbar;
float toolbarHeight = 0.0;
NSRect windowFrame;
toolbar = [window toolbar];
if(toolbar && [toolbar isVisible])
{
windowFrame = [NSWindow contentRectForFrameRect:[window frame]
styleMask:[window styleMask]];
toolbarHeight = NSHeight(windowFrame)
- NSHeight([[window contentView] frame]);
}
return toolbarHeight;
}
// Just my generic routine to display the appropriate panel number
void showPanelNumber(int panelNumber, NSRect viewBounds, NSView *
viewPanel, NSString * windowTitle, NSWindow *window)
{
NSRect windowFrame = [NSWindow contentRectForFrameRect:[window frame]
styleMask:[window styleMask]];
NSRect newWindowFrame = [NSWindow frameRectForContentRect:
NSMakeRect( NSMinX( windowFrame ),
NSMaxY( windowFrame ) -
viewBounds.size.height-ToolbarHeightForWindow(window),
viewBounds.size.width,
viewBounds.size.height +ToolbarHeightForWindow(window) )
styleMask:[window styleMask]];
[window setFrame:newWindowFrame display:YES animate:YES];
[window setTitle: windowTitle];
[window setContentView:viewPanel];
}
@implementation Controller
- (IBAction) showPanel1:(id)sender {
showPanelNumber(1, panel1Bounds, prefsPanel1, @"General Preferences", window);
}
- (IBAction) showPanel2:(id)sender {
showPanelNumber(2, panel2Bounds, prefsPanel2, @"Statistical
Preferences",window);
}
- (IBAction) showPanel3:(id)sender {
showPanelNumber(3, panel3Bounds, prefsPanel3, @"", window);
}
- (IBAction) showPanel4:(id)sender {
showPanelNumber(4, panel4Bounds, prefsPanel4, @"", window);
}
- (IBAction) showPanel5:(id)sender {
showPanelNumber(5, panel5Bounds, prefsPanel5, @"", window);
}
// When we launch, we have to get our NSToolbar set up. This involves
creating a new one, adding the NSToolbarItems,
// and installing the toolbar in our window.
-(void)awakeFromNib
{
// Get the values for the global bounds, etc
panel1Bounds = [prefsPanel1 bounds];
panel2Bounds = [prefsPanel2 bounds];
panel3Bounds = [prefsPanel3 bounds];
panel4Bounds = [prefsPanel4 bounds];
panel5Bounds = [prefsPanel5 bounds];
// Start setting up the toolbar as the NIB awakes
NSToolbar *toolbar=[[[NSToolbar alloc]
initWithIdentifier:@"myToolbar"] autorelease];
// Here we create the dictionary to hold all of our "master" NSToolbarItems.
toolbarItems=[[NSMutableDictionary dictionary] retain];
addToolbarItem(toolbarItems,@"GeneralPrefs",@"Blue Text",@"Blue
Text",@"This toggles blue text
on/off",self,@selector(setImage:),[NSImage
imageNamed:@"blueLetter.tif"],@selector(showPanel1:),NULL);
addToolbarItem(toolbarItems,@"StatisticalPrefs",@"Red Text",@"Red
Text",@"This toggles blue text
on/off",self,@selector(setImage:),[NSImage
imageNamed:@"blueLetter.tif"],@selector(showPanel2:),NULL);
// the toolbar wants to know who is going to handle processing of
NSToolbarItems for it. This controller will.
[toolbar setDelegate:self];
[toolbar setAllowsUserCustomization:NO];
// tell the toolbar that it should save any configuration changes
to user defaults. ie. mode changes, or reordering will persist.
// specifically they will be written in the app domain using the
toolbar identifier as the key.
[toolbar setAutosavesConfiguration: NO];
// tell the toolbar to show icons and text by default
[toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
[toolbar setSizeMode:NSToolbarSizeModeRegular];
// set general prefs to be selected by default
[toolbar setSelectedItemIdentifier:@"GeneralPrefs"];
[self showPanel1:nil];
// install the toolbar.
[window setToolbar:toolbar];
}
-(BOOL)validateMenuItem:(NSMenuItem *)menuItem
{
return YES;
}
// Try deleting this..
- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem
{
return YES;
}
// This method is required of NSToolbar delegates. It takes an
identifier, and returns the matching NSToolbarItem.
// It also takes a parameter telling whether this toolbar item is
going into an actual toolbar, or whether it's
// going to be displayed in a customization palette.
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
itemForItemIdentifier:(NSString *)itemIdentifier
willBeInsertedIntoToolbar:(BOOL)flag
{
// We create and autorelease a new NSToolbarItem, and then go
through the process of setting up its
// attributes from the master toolbar item matching that
identifier in our dictionary of items.
NSToolbarItem *newItem = [[[NSToolbarItem alloc]
initWithItemIdentifier:itemIdentifier] autorelease];
NSToolbarItem *item=[toolbarItems objectForKey:itemIdentifier];
[newItem setLabel:[item label]];
[newItem setPaletteLabel:[item paletteLabel]];
if ([item view]!=NULL) {
[newItem setView:[item view]];
}
else {
[newItem setImage:[item image]];
}
[newItem setToolTip:[item toolTip]];
[newItem setTarget:[item target]];
[newItem setAction:[item action]];
[newItem setMenuFormRepresentation:[item menuFormRepresentation]];
// If we have a custom view, we *have* to set the min/max size -
otherwise, it'll default to 0,0 and the custom
// view won't show up at all! This doesn't affect toolbar items
with images, however.
if ([newItem view]!=NULL) {
[newItem setMinSize:[[item view] bounds].size];
[newItem setMaxSize:[[item view] bounds].size];
}
return newItem;
}
- (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar;
{
// Optional delegate method: Returns the identifiers of the subset of
// toolbar items that are selectable. In our case, all of them
return [NSArray arrayWithObjects:@"GeneralPrefs",
@"StatisticalPrefs", nil];
}
// This method is required of NSToolbar delegates. It returns an
array holding identifiers for the default
// set of toolbar items. It can also be called by the customization
palette to display the default toolbar.
- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
{
return [NSArray
arrayWithObjects:@"GeneralPrefs"/*,NSToolbarSeparatorItemIdentifier*/,@"StatisticalPrefs",nil];
}
// This method is required of NSToolbar delegates. It returns an
array holding identifiers for all allowed
// toolbar items in this toolbar. Any not listed here will not be
available in the customization palette.
- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar
{
return [NSArray
arrayWithObjects:@"GeneralPrefs"/*,NSToolbarSeparatorItemIdentifier*/,@"StatisticalPrefs",nil];
}
// throw away our toolbar items dictionary
- (void) dealloc
{
[toolbarItems release];
[super dealloc];
}
@end
-----
Controller.h:
#import <Cocoa/Cocoa.h>
@interface Controller : NSObject
{
IBOutlet id window;
// Five views, for expanding my preferences when needed
IBOutlet NSView *prefsPanel1;
IBOutlet NSView *prefsPanel2;
IBOutlet NSView *prefsPanel3;
IBOutlet NSView *prefsPanel4;
IBOutlet NSView *prefsPanel5;
NSMutableDictionary *toolbarItems; //The dictionary that holds all
our "master" copies of the NSToolbarItems
// Globals to keep track of the bounds, etc
NSRect panel1Bounds;
NSRect panel2Bounds;
NSRect panel3Bounds;
NSRect panel4Bounds;
NSRect panel5Bounds;
}
//Required NSToolbar delegate methods
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
itemForItemIdentifier:(NSString *)itemIdentifier
willBeInsertedIntoToolbar:(BOOL)flag;
- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar;
- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar;
- (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar;
@end
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden