As you can see in the code below, it is easy to create the toolbar, set its delegate and show the toolbar in the window. The most important piece of code here is the fact that now the application controler (self) is the toolbar delegate. The delegate will have the hard work to create all the views needed to implement the toolbar content.
var toolbar = [[CPToolbar alloc] initWithIdentifier:"Photos"];
[toolbar setDelegate:self];
[toolbar setVisible:YES];
[theWindow setToolbar:toolbar];
Start with the methods that tells the toolbar which toolbar items may be present in the toolbar and which are the default toolbar items that are created at startup.
// Return an array of toolbar item identifier (all the toolbar items that may be present in the toolbar)
- (CPArray)toolbarAllowedItemIdentifiers:(CPToolbar)aToolbar
{
return [CPToolbarFlexibleSpaceItemIdentifier, SliderToolbarItemIdentifier, AddToolbarItemIdentifier, RemoveToolbarItemIdentifier];
}
// Return an array of toolbar item identifier (the default toolbar items that are present in the toolbar)
- (CPArray)toolbarDefaultItemIdentifiers:(CPToolbar)aToolbar
{
return [AddToolbarItemIdentifier, RemoveToolbarItemIdentifier, CPToolbarFlexibleSpaceItemIdentifier, SliderToolbarItemIdentifier];
}
The identifiers used are just plain string objects.
var SliderToolbarItemIdentifier = "SliderToolbarItemIdentifier",
AddToolbarItemIdentifier = "AddToolbarItemIdentifier",
RemoveToolbarItemIdentifier = "RemoveToolbarItemIdentifier";
Now that the toolbar knows which toolbar items it need to create, it will ask its delegate to create the views. Its is done in the method toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:. A CPToolbarItem is created and associated it with its identifier and returned to the Toolbar.
- (CPToolbarItem)toolbar:(CPToolbar)aToolbar itemForItemIdentifier:(CPString)anItemIdentifier willBeInsertedIntoToolbar:(BOOL)aFlag
{
var toolbarItem = [[CPToolbarItem alloc] initWithItemIdentifier:anItemIdentifier];
if (anItemIdentifier == SliderToolbarItemIdentifier)
{
}
else if (anItemIdentifier == RemoveToolbarItemIdentifier)
{
}
return toolbarItem;
}
We have now two cases : a simple one (for button with a text and picture) and a more complicated one (with custom view). For simple toolbar item, we just have to retrieve the pictures, associate them with the toolbar item state. We associate which action will be trigerred and which object will be called.
if (anItemIdentifier == RemoveToolbarItemIdentifier)
{
var mainBundle = [CPBundle mainBundle];
var image = [[CPImage alloc] initWithContentsOfFile:[mainBundle pathForResource:@"remove.png"] size:CPSizeMake(30, 25)];
var highlighted = [[CPImage alloc] initWithContentsOfFile:[mainBundle pathForResource:@"removeHighlighted.png"] size:CPSizeMake(30, 25)];
[toolbarItem setImage:image];
[toolbarItem setAlternateImage:highlighted];
[toolbarItem setTarget:self];
[toolbarItem setAction:@selector(remove:)];
[toolbarItem setLabel:"Remove Photo List"];
[toolbarItem setMinSize:CGSizeMake(32, 32)];
[toolbarItem setMaxSize:CGSizeMake(32, 32)];
}
For custom toolbar item, we have to create a custom view (here an instance of PhotoResizeView) and associate the view with the tollbar item (using setView:).
We don't associate a target/action with the toolbar item. This will be done in the slider contained in the PhotoResizeView.
if (anItemIdentifier == SliderToolbarItemIdentifier)
{
// The toolbar is using a custom view (of class PhotoResizeView)
[toolbarItem setView:[[PhotoResizeView alloc] initWithFrame:CGRectMake(0, 0, 180, 50)]];
[toolbarItem setLabel:"Scale"];
[toolbarItem setMinSize:CGSizeMake(180, 32)];
[toolbarItem setMaxSize:CGSizeMake(180, 32)];
}
A custom view need to be a subclass of CPView.
@implementation PhotoResizeView : CPView
{
}
The view is initalized in initWithFrame: method. We create a CPSlider and add it to the PhotoResizeView instance ([self addSubview:slider]). We also associate the action sliderChangedValue: sent by the slider when it is used by the user to the PhotoResizeView instance ([slider setTarget:self]).
- (id)initWithFrame:(CGRect)aFrame
{
self = [super initWithFrame:aFrame];
if (self != nil)
{
// Create a slider centered vertically and 16 pixels height.
var slider = [[CPSlider alloc] initWithFrame:CGRectMake(38, CGRectGetHeight(aFrame)/2.0 - 8, CGRectGetWidth(aFrame) - 80, 16)];
[slider setMinValue:50.0];
[slider setMaxValue:250.0];
[slider setTarget:self];
[slider setAction:@selector(sliderChangedValue:)];
// Add the slider in the PhotoResizeView instance
[self addSubview:slider];
[slider setValue:150.0];
// Create two labels to show the mimimum and maximum values offered by the slider.
// code ommited for simplicty can be found in the dowloadable zip file
}
return self;
}
We now have to implement the action sliderChangedValue:. This is where the trick is located. We ask the application (CPApp (or [CPApplication sharedApplication])) to trigger itself (nil) the action (adjustImageSize:) with the slider as the sender. This will allow us to call [sender value] to get the slider current value.
- (void)sliderChangedValue:(id)sender
{
[CPApp sendAction:@selector(adjustImageSize:) to:nil from:sender];
}
You may wonder how the CPApplication can handle the adjustImageSize: method. In fact since we don't have subclassed CPApplication, the object CPApp can not handle this action. So it will ask its delegate to handle the action. And since we have implemented this method in AppController, this method will get called.
@implementation AppController : CPObject
{
}
- (void)adjustImageSize:(id)sender
{
var newSizeAsString = [CPString stringWithFormat:@"%d", [[CPNumber numberWithDouble:[sender value]] intValue]];
[label setStringValue:newSizeAsString];
}
@end
Hey, but i don't have coded a line to indicate that AppController is the CPApplication delegate. Well it is located in the "Info.plist" file.
We have a key nammed CPApplicationDelegateClass which indicates that AppController is the delegate. Another key nammed CPPrincipalClass indicates that CPApplication is the class to be instanced at application startup.
In the file "main.j", the function CPApplicationMain will read the Info.plist file, create an instance of the class indicated by CPPrincipalClass (CPApplication in our case) and create an instance of the class indicated by CPApplicationDelegateClass (AppController in our case). It will also set the instance of AppController as the delegate of the instance of CPApplication.
function main(args, namedArgs)
{
CPApplicationMain(args, namedArgs);
}
If you'd like to see the complete code listing from the tutorial, you can download it all in a single file: Tutorial_Toolbar.zip. The web application is available online: Tutorial for CPToolbar.