Creating The Application
If you haven't already done so, head over to http://cappuccino.org/download/ and follow the instructions for installing Cappuccino. Don't download the Starter Package unless you have a reason to do so. For our purposes, just execute the curl command listed on that page and answer the prompts that follow.
Since we're going to be using Xcode to build the interface, you'll also need to be on a Mac to follow along. Cappuccino applications can technically be authored on any platform, but the toolchain offers more when you're on a Mac.
Let's create the app in /Library/WebServer/Documents
cd /Library/WebServer/Documents
capp gen KVOTest -t NibApplication
The capp command is basically generating an application called "KVOTest" using the NibApplication application template. A NibApplication is one that contains a starter XIB (Xcode interface document). Next, fire up XcodeCapp.app
At this point, you should be able to open your web browser to http://localhost/KVOTest and see the default slider and text field. By the way, that is an example of KVO, but we're going to take that apart so you can see what's going on behind the scenes.
open /usr/local/narwhal/packages/cappuccino/support/XcodeCapp.app...you should see its icon appear in the menu bar. Click that icon and tell it to listen to your new project. This will automatically compile our XIB into Cappuccino's CIB document every time the XIB is changed. It will also watch our source code for changes and tell Xcode about any outlets and accessors we've written.
At this point, you should be able to open your web browser to http://localhost/KVOTest and see the default slider and text field. By the way, that is an example of KVO, but we're going to take that apart so you can see what's going on behind the scenes.
Customizing the Interface
Click the XcodeCapp.app icon in the menu bar and choose to open your project in Xcode. Go ahead and delete the slider bar and the text box so it looks like this:Next, you're going to add the following components to your window:
- Horizontal Slider
- Check Box
- Text Field
When you're done, your layout should look like this:
Since the web is a slightly different platform than your desktop, Cappuccino's code in the default template will attempt to make this window fullscreen, which we don't want right now. So go into /Library/WebServer/Documents/KVOTest and edit AppController.j. Comment out the [theWindow setFullPlatformWindow:YES]; line with two slashes. Now if you reload http://localhost/KVOTest, you should see something like this:
Setting Up The Models
Technically, we could connect all three components using KVO from within Xcode and avoid touching any code. But, we're going to use some very simple key value paths to illustrate how the bindings are interconnected.
Edit AppController.j again and add some properties with accessors to this class. Hint: accessors are needed here because using them alerts the bound objects to any changes that are made to the properties they represent. It should look something like this:
@import <Foundation/CPObject.j>
@implementation AppController : CPObject
{
CPWindow theWindow; //this "outlet" is connected automatically by the Cib
BOOL sliderIsEnabled @accessors;
int sliderValue @accessors;
}
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
{
[self setSliderIsEnbled:YES];
[self setSliderValue:50.0];
}
- (void)awakeFromCib
{
[theWindow center]; // center the window on the screen
}
@end
Binding the UI Components to the AppController
As soon as you save your changes, XcodeCapp.app has very helpfully informed Xcode about our new accessors, which we can now use as key paths for binding. So go back into Xcode and select the "Enable Slider" checkbox. Now click the bindings tab, select "App Controller" from the "Bind to" dropdown list, then type "sliderIsEnabled" in the "Model Key Path" box. It should look like this:
Save your project in Xcode and XcodeCapp.app should automatically convert it to a cib for us. At this point you can reload your app in your browser and see that the check box is checked. Kind of boring since that's the way it was. But, if you set the value to NO using [self setSliderIsEnabled:NO]; and reload, you should see that the check box is now unchecked. It's getting fun, isn't it?
Next let's bind the enabled state of the slider to the value of the check box (sliderIsEnabled). We'll do so just like before, but instead of binding the slider's value, we're going to bind it's enabled state. You'll find the Enabled option under the Availability heading on the bindings tab.
At this point you can reload your app in your browser and see that unchecking the box disabled the slider bar, and checking the box enables it again. Keep in mind, all we're doing is modifying the value of a property of AppController. The UI components are simply observing the value of that property for changes and updating accordingly.
The value of the text box and slider are going to be bound to a shared property as well, but since we're binding the value of each of these UI components, they will be able to update each other. Select the slider, then follow the same steps that you executed when binding the check box's value above, but enter "sliderValue" for the Model Key Path instead. Select the text box and do the same. Save your project in Xcode and reload your project in your browser. At this point, you should be able to move the slider back and forth and watch the value change in the text box. Likewise, you can type a value into the text box and watch the slider move.
You'll notice the that slider will only update the text box after you drag and then let go of the slide knob. If you want this to be more realtime, go back into Xcode and select the slider bar again. Click the 'attributes inspector' (which is to the left of the bindings tab and conveniently looks like a slider bar) and scroll down to the Control heading. Under the Control heading, look for the Continuous option and enable it. Save the project, reload the browser window, now the text field should update while you drag the slider knob instead of when you've finished dragging it.
Closing Remarks
It should be noted that Cappuccino can be very heavy in terms of what the browser needs to download from your server. Therefore, most browsers will try to cache as much of your application as it can. This can cause problems during the development phase. If you encounter a scenario where you expect something to have changed but it's just staying the same, don't forget to empty your browser cache from time to time.
If you'd like to learn more about Cappuccino, please join the 'general' mailing list.





