Writing your own interface for Quicksilver is surprisingly easy, especially if you have experience in Cocoa and Xcode. Having covered the basics about the QSPlugin key in the Info.plist, as well the as the fundamentals of writing Quicksilver actions using Xcode, we are ready to start in a completely new direction. The Quicksilver interface.
Setup Xcode
Writing an interface is very similar to writing an action, but the classes and Info.plist keys are somewhat different. Download the QS Interface Project Template. If you haven't previously downloaded a Quicksilver plugin template for Xcode, you'll have to do some basic setup.
Once you've installed the project template, you can build a new interface project "out-of-the-box" to make sure it works.
Unfortunately, development isn’t a linear process, so you’ll probably have to keep referring back to different sections of this article as you develop your interface.
Info.plist keys specifications
We tell Quicksilver that we're providing it an interface in the QSRegistration section of the Info.plist file, under QSCommandInterfaceControllers. By default, this should have been already added in when you created the project.
<key>QSRegistration</key>
<dict>
<key>QSCommandInterfaceControllers</key>
<dict>
<key>testInterface</key>
<string>testInterface</string>
</dict>
. . .
. . .
</dict>
There is also a key called QSPreferencePanesTemplate inside QSRegistration. If you wish to have a preference pane for your plugin, rename the key to QSPreferencePanes and create the pref pane; otherwise you can leave it or delete it. As per the previous setting, these values should be filled in already by the project.
<key>QSRegistration</key>
<dict>
. . .
. . .
<key>QSPreferencePanesTemplate</key>
<dict>
<key>testInterfacePrefPane</key>
<dict>
<key>class</key>
<string>QSPreferencePane</string>
<key>description</key>
<string>testInterface interface preferences</string>
<key>icon</key>
<string></string>
<key>name</key>
<string>testInterface</string>
<key>nibBundle</key>
<string>com.blacktree.Quicksilver.testInterface</string>
<key>nibName</key>
<string>testInterfacePrefPane</string>
<key>type</key>
<string>hidden</string>
</dict>
</dict>
</dict>
If you're not having a preference pane, you should remove the PrefPane.nib file from your project when you're ready to release your plugin; it'll save bandwidth as well as reduce clutter.
You may not have an idea of the bindings you’ll be using yet (or maybe you do), but when you do use them, it's good practice to specify some default values for Quicksilver to use when the value hasn't been set by the user. These values go in the QSDefaults key.
<key>QSDefaultsTemplate</key>
<dict>
<key>interface.bindingname</key>
<string>value</string>
<key>interface.bindingname2</key>
<real>2.54328</real>
<key>interface.bindingname2</key>
<integer>4</integer>
</dict>
If you're using the "QSDefaults" key, make sure you’re renamed it from "QSDefaultsTemplate" to "QSDefaults".
Don’t forget there’s also a QSPlugin section in the Info.plist file that you should fill in.
Coding
That's the Info.plist file out of the way for now. It's time to start coding!
There are a few basic Quicksilver terms I hope you're familiar with:
![Quicksilver interface selectors [Quicksilver Interface Selectors]](http://dev.lipidity.com/wp-content/uploads/2007/06/qs-selectors1.png)
Fig 1. The selectors are the used to pick the direct object, action, and indirect object respectively. Indirect object is often optional or not required.
Arrangement and positioning of the views using Interface Builder is left as an exercise for the reader. You don't want a lesson on how to drag and drop, do you?
Controlling the Controller
As mentioned before, the superclass (QSResizingInterfaceController) handles most of the work for you, therefore the majority of code you add will not be in new methods. Most of the code you write is (probably) going to be in existing methods that you will subclass.
Major methods
Here are the main methods you may decide to add extend:
- - windowDidLoad
-
Anything that requires interaction with the interface window when it is first loaded. As the name implies, this method is called when the interface window is first loaded. All your setup with the window goes in this method. For example, setting the colors, adding bindings, and choosing animation effects for hiding / showing the interface etc. Please make sure you call
[super windowDidLoad];somewhere to let Quicksilver to do its setup as well.This is not the place to add initialization code for the class itself. There's an
-initmethod for that. - - maxIconSize
-
The maximum size allowed for icons in the interface selectors. Adjust this depending on how big or small your interface is. It's set at 128 x 128 by default, which should be fine in most circumstances.
- - showMainWindow:
-
Any fancy animations or transitions you want for when the interface is being shown can go in here. You could also do something like:
if ( [[self window]isVisible] ) [[self window] pulse:self];. (The pulse method is explained later). If you're having your own transition effect and don't want Quicksilver's default fade-in animation, run[window setFastShow:YES].If you can, call the super implementation of the method where you would call
-makeKeyAndOrderFront:, otherwise the option to suppress hotkeys while the command window is shown wont be honored. - - hideMainWindow:
-
Calling the super's implementation will hide the window with Quicksilver's effect. If you're using your own animation, you can run
[window setHideEffect:nil]so Quicksilver doesn't run its own effect as well. - - activateInTextMode:
-
The use of the "Activate in Text Mode" trigger may be a rare occasion, but it's a cool place to throw in a surprising animation. Fumo has used Core Graphics warp and cube effects for this in the past.
Expansion
If you're having your interface expand and collapse, you'll need to subclass the following methods:
- - showIndirectSelector:
-
Position the indirect selector in an appropriate location on the window. It may be useful to position it relative to another object, for example:
[iSelector setFrame:NSOffsetRect([aSelector frame],0,-250)];Don't forget to call super! - - expandWindow:
-
Expand your interface. You'll probably be using
[[self window] setFrame: display: animate:]to resize the window smoothly. - - contractWindow:
-
As per
-expandWindow:, but make the window smaller. - -hideIndirectSelector:
-
In most cases you wont need to subclass this. It just hides the indirect selector.
Preference Panes
The easy stuff
Creating your preference pane is easy since a lot of the work is done for you by the project template. There's a .nib file included in the interface project called "testInterfacePrefPane" (where "testInterface" is the name of your Xcode project). With this .nib file:
- Open it.
- Add controls and bindings.
- Save and quit.
That’s creating it, though. There's still a little more work to be done.
Info.plist keys
Remember the QSPreferencePanes key I mentioned earlier? That needs to be present under the QSRegistration key in the Info.plist file. You don’t need to configure it, but you can change some keys like description with a short summary of the preference pane, or set an icon.
(Reminder: Make sure to drop the "Template" from "QSPreferencePanesTemplate")
The "Customize" button
To set a customize button to show the preference pane for your interface, simply uncomment the -customize: method that's added in by default. It’s that easy.
- (IBAction)customize:(id)sender{
[[NSClassFromString(@"QSPreferencesController") sharedInstance] showPaneWithIdentifier:@"testInterfacePrefPane"];
}
A note on bindings
When the user changes something in the preference pane, the binding will be updated. And that’s it. You’ll need to bind your classes / controls not only in the preference pane, but also in your code so that any changes to bindings reflect in your interface. For example, if you add a binding for "testInterface.fadeStyle", you may want to do something like:
[self bind:@"fadeStyle"
toObject:[NSUserDefaultsController sharedUserDefaultsController]
withKeyPath:@"values.testInterface.fadeStyle"
options:nil];
If you’re binding an NSColor, make sure to use NSUnarchiveFromDataTransformerName
Building
Once you’ve got your code ready, build the project.
(Don’t puke if you get warnings that you can’t get rid of; there will be a few warnings regarding some methods like -setWindowProperty:, and another great error if you add a preference pane and the customize button. We’ll have to live with them for now.)
Double-click on the built product to have Quicksilver install it. If there are no serious faults with the code, you’ll see your Incredible Interface™ and be able to enjoy, use, and distribute it!
Summary
- Download the Xcode Project Template, making sure everything else is setup correctly
- Add code to the subclass of
QSResizingInterfaceController - Arrange the views in Interface Builder
- Add preference pane and bindings
- Build project
- Test and distribute
If you need any additional information or resources, help is always available at the Quicksilver forums or you can contact me.
Be sure to show me how you go with your interface!
Also in this series
Quicksilver plugins in Objective-C
A guide to obtaining the required resources for Quicksilver plugin development in Xcode. Also runs through setting up Xcode to make creating plugins easier.
Info.plist - QSPlugIn
The various sections of Quicksilver plugins, including the basics on how to properly configure your plugin, setup the correct options and settings, and how and where to write your plugin.
Quicksilver Actions
A quick look at QSActions in the Info.plist, followed by our first look at the code needed to get a plugin up and running.
8 Comments so far
Leave a commentHehe, good one, I wrote a transformer for that once myself, not knowing about this one
.
voiced by sphynx on June 14, 2007 4:56 am | Permalink
Leave a comment