Application Technologies • 55:38
Based upon standard HTML, CSS, and JavaScript, Dashboard widgets are simple to build and easy to extend. Learn the code tricks and design tips that can help make your widgets even better. We'll discuss how to use native code to integrate your widget into system frameworks, and show how a gorgeous widget can make a dramatic difference to your users.
Speakers: Christian Wagner, Matt Drance
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Good afternoon, and welcome to Authoring Dashboard Widgets. My name is Christian Wagner. I am a technical writer at Apple, and we are going to spend the next hour talking about Dashboard Widgets in Leopard and on Tiger. Before I go any further, I wanted to remind you that this is a hands-on session, so if you don't have the code for the hands-on already, please go to the developer.apple.com slash WWDC2006 and download the sample code associated with this session. There's three bits, resizer, sinker, and birthdays, so please make sure to get that.
Furthermore, I wanted to set your expectations a little bit. This session is for people who already are familiar with widget development. If you've never made a widget before, we recommend that you go to the Dashcode session tomorrow morning at 9 o'clock in the Marina, the Marina room, not the north side of San Francisco, and that you take advantage of the things that Dashcode offers you.
Building a widget from scratch isn't too hard, but Dashcode makes it just that much easier for you. So let's get started. Some of you are new, and so I just wanted to recap really quick what is Dashboard. So this is your standard issue Tiger desktop. Not too much crazy going on there.
You notice that there's this icon next to the Finder icon. It's the Dashboard icon. When you click on it, this layer goes over your screen, and this layer is Dashboard. On the Dashboard, you're going to find these handy little programs called Widgets. Widgets provide information, access to information that you want.
You can use them to have at your fingertips at a moment's notice. To users, they appear as applications, but underneath, they're really powered by web technologies like HTML, CSS, and JavaScript. Additionally, there's also some other technologies you can take advantage of like WebKit plugins and widget plugins. So that's Dashboard.
Dashboard today, well, as of this morning, there's over 2,100 widgets in the wild, and this has been the feature in Tiger. When anyone talks about Tiger, they think about the widgets. And so how many of you have already created a widget? Can I see a show of hands? All right, that's great. Well, thank you. We really appreciate it a lot. You guys are the ones that helped make this the feature in Mac OS X Tiger. So thank you very much. Give yourselves a round of applause.
So I want to take a moment to show you some of the great widgets that are out there. The first one is iClip Lite. And this is the winner of our Apple Design Awards last night. And we really like this widget a lot because it's fun to use. It's good looking.
It makes something that's normally really mundane and boring, like clipboard management, but actually makes it fun to use and looks really sharp. So this is a great example for you to learn from. And I suggest if you've never used it before, you download it and take a look at it.
Another great one is Weatherbug. This is a great widget because it's actually branded by the Weatherbug website. They have a widget that complements their website, offers the information you normally could get there, but in a nice, compact, good-looking form. So that's another good example for you to look at.
One that I like a lot is this one called QXP. And it's an expense tracker. It's really good looking. It's tiny. And that's one thing I love about it. It does what it's supposed to do in the smallest space possible. So that's another tip you can take away from this.
And finally-- who doesn't like the Gas widget? With gas prices probably going up again this week, this is a great widget to have on your dashboard. And again, it's fun. It has a lot of animation. It's good looking. But it's also really compact and gives you the information you want to have at your fingertips. So that's really handy.
So we've told you a little bit about Dashboard and Tiger. Let's talk about Dashboard and Leopard. And of course, you can't talk about Dashboard and Leopard without talking about Dashcode. Dashcode is our new widget creation environment, and you should really check it out. Even if you already are using the good old tried and true text editor and graphic editor, Dashcode has a lot of features that you can use to take your widget further and integrate more of the great technologies we've made available for you. If you go to the Marina Room tomorrow morning at 9:00 AM, again, you'll learn more about Dashcode. They're going to make a phenomenal widget. They're going to just walk you through the whole thing, so make sure to head over there.
CHRISTIAN WONG: Another thing that's really changed in Leopard is the architecture. So this is the dashboard architecture diagram you're all used to seeing in Tiger. And in it, you have the dashboard server, which is what provides the dashboard environment for you. And then you have the dashboard clients, which are each individual widgets. And that means that each widget has its own CPU, its own memory, all these resources all devoted to itself. And then inside, there's a web view where your widgets are running, and a couple of other associated good things.
But in Leopard, we've changed this a little bit. So what this diagram-- and you might not notice a difference, so let me go back one. Tiger and Leopard. The difference here, in case you didn't notice it, is that every widget is now running in the same dashboard client. So that means that each widget is in its own web view, but they're all inside the same process.
So why do this? The number one reason is performance. We get a two times speed increase when loading your widget and when working with widgets. So that's the most tangible benefit you get right off the top of the bat. And we think this is going to be a noticeable improvement for users and for you developers.
There is a caveat to this. If your widget uses a widget plugin, we do actually load your class of widget in its own process. And that's because we need to avoid namespace conflicts. So if you are using a widget plugin, you will be in your own process. And this just further reinforces the point that we like to use, that you should try to do everything you possibly can in JavaScript. JavaScript has a lot of features, it has a lot of API Therefore, you use it before resorting to a widget plugin.
This is different than with internet plugins, though. So if you wrote a custom web kit or internet plugin for your widget, that is running the same space. So now that all the widgets are running together, you really need to make sure you're using unique class names and symbols.
For debug purposes, there is a defaults write you can use that will give you the Tiger-style dashboard. And that's defaults write com.apple.dashboard processgrouping-int1. If you didn't write that down, we're going to demo it in a second, so hold on. And finally, the point, the biggest point is now since every widget's running together, you really should never block.
And I really need to emphasize that you should never, ever, ever, ever block. So what does that mean? If you're using XML HTTP request, use the asynchronous version. Do not use the synchronous version. If you're using widget.system for command line utility, make sure you are using the asynchronous version.
If you're using the synchronous one, you will be blocking every widget on Dashboard, not just the dashboard. So this is really bad. Make sure you're using the asynchronous versions. So I'd like to go hands-on now. If we could go to demo one, please. And I'm going to show you demo one, please.
All right. So what I'm going to show you now is just a really quick demo about the new dashboard, to show you the new speed of the new dashboard in Leopard. So if you have a Tiger machine, feel free to watch up here. If you are running a Leopard machine, feel free to follow along.
So what I'm going to do is I'm going to launch the terminal application. You'll find that in Applications, Utilities, and "Terminal" and I'm going to open it. Is everyone with me? Can I see a show of hands? Alright. CHRISTIAN WONG: So I'm going to type in the command that I showed you in the slide before. But instead of typing, I'm just going to drag in a text clipping.
So again, if you want to type this with me, it's default, write, com.apple.dashboard, and then I'm going to put a space there, process-grouping-int1. So I'm going to hit return, and then I'm going to kill the doc. And that's the process that houses Dashboard. So I'm going to kill the doc.
And so what I'm going to do then is once the doc loads up here again, I'm going to click on Dashboard. So 1-1000, 2-1000, 3-1000, 4-1000, 5-1000, 6-1000, 7-1000, 8-1000, 9-1000, 10-1000, 11-1000. Wow, you're getting the drift. This is the Tiger Dashboard. And so sometimes loading performance can be a little bit suboptimal.
I'm going to hide Dashboard. And now I'm going to delete that. We're going to go back to the Leopard Dashboard now. So I'm going to do defaults. And this time, I'm going to delete the one that we just put. So I'm going to do delete com.apple.dashboard. And then I'm going to put process-grouping. All right, everyone have that in? We're going to hit Enter. And then we're going to kill the doc again.
All right, so now we're back up. Ready to count with me? I'm going to click Dashboard. One 1,000, two 1,000. That was two and a half 1,000. So, you know, this was a really good example, but I think you get the idea that we've got considerable speed improvements here. So, thank you. Back to slides, please.
So we think that the new architecture in Dashboard, I think it will thrill a lot of you. I think you're going to get a lot more responsiveness out of it. Can I have slides, please? Thank you. So that's probably going to be the biggest, most tangible thing, but another thing that we offer in Leopard for Dashboard is Dashboard Sync. So you may have heard this already, but in Leopard, your dashboard can sync via .Mac syncing. So that means the presence of widgets and their preferences can be synced between different users using .Mac.
And the good news is that this is free. You pretty much don't have to do anything for this. If a user uses Dashboard Sync in the .Mac system preferences, your preferences will get synced, and it's great. It's going to work fine. But there is a little bit of a caveat. And the thing is that you load your preferences in your widget usually when your widget first loads. And so if your widget doesn't reload anywhere in there, you're not going to get any changes in preferences that happen.
So we've provided in Leopard a new API for this. This is Widget On Sync. And you're going to give it a handler. And basically, that handler is called whenever a sync event finishes. So when that sync event finishes, you just go through, check all your preferences, and update them as necessary. So this is a really nice way to handle it. Just add a bit of polish. But we think people are going to be really excited about this.
And there's another thing which you can do here, and that's a sync exclusion. If there's a preference that you have that you would rather not have it synced, you can, in your Info.plist, exclude any preferences that you would not like synced. So you do that using the sync exclusions key. And then you provide it an array of dictionaries where the first value, the first key, is the name of the preference that you would like excluded from sync. And the second key is whether it's global or not.
So if you set global equal to true, that means we're just going to look for that name, that string, and exclude it. If you set it to be false, we're going to look for the key. But we're also going to look for the widget identifier. It's a common convention in widgets that if you're doing per instance widget preferences that you prepend the key with the widget identifier. And so if you're doing that, we'll notice that, and then we'll exclude that key as well. So I'd like to go back to demo and show you a little bit about syncing widgets.
So if you take a look at the sample code that you downloaded for the session, there's a project called Syncr. And this works on Leopard only. So if you're on Leopard, feel free to follow along. If you are on Tiger, you might want to watch on the screen here.
And for the first bit of this demo, I'm actually going to be using a fast user switching a little bit, so you might be best off to just watch me for a bit. So I've got this widget here. I'm just going to double click on it and install it. And when I do it, it's actually really simple. All this widget does is it saves and retrieves preferences, just a string. So I'm going to put a string here, and I'm just going to say, hi, mom.
I'm going to set that preference. I'm also going to keep the widget. Probably helps if I do that. So that's pretty much it. I've got this widget on my dashboard, and I've got a string set as a preference. And just for demo purposes here, I'm also going to add a world clock widget here. And I am going to set its location to something else here in America. I'm going to put it to my hometown of Chicago.
So now that I've got that on my dashboard, those preferences are all stored. I'm going to go in System Preferences, and I'm going to go to .max sync. I've already got my user account set up here. So I'm going to go to the Sync pane, let this load.
And I'm going to sync this, my widgets. So this is going to take everything that's on my dashboard, the presence of widgets, and where they're located, and their preferences, and sync them to .mac. So this might take a few seconds. OK. So now that's been stored on .Mac, just like it's on my machine. I'm going to switch users to another user here. This is going to take a second.
All right. So now with this user, I'm just going to go back to the .mac prefpaint. Again, I've got the same .mac user set in here. So now that I've got that preference, I'm going to go to Sync. And you'll notice that I already have this prepared for Dashboard Widgets to sync right there. So I'm just going to say Sync Now.
Take a few seconds. We're on the internet, of course. All right. And then now I'm going to go down here to the dock, click Dashboard. And sure enough, I've got a clock for Chicago and my Syncr widget magically there. So that's one half of the sync story. We now have the same dashboard on two computers. Now what about this one? Let's say I change the dashboard here, and I want to then go back to the other account.
So I'm going to change this preference string back into "Hi Mom" here. And I'm going to say, well, for the time being, "Bye Mom." I've set that preference. So OK, we're going to go back to System Preferences here, into .mac, and we're going to sync that back to my first user. So this will take a few seconds again.
And then I'm going to go here back to the account I was using a second ago. And I'm going to sync the widgets again. And then I'm going to go here back to the account I was using a second ago. And I'm going to sync the widgets again.
And sure enough, it says, "Bye, Mom, now." So that's pretty much the whole round tripping. But you'll notice that Dashboard didn't-- it did not reload through that entire time. So how did it know that it should fetch a new preference? Well, I used Widget On Sync for that.
So let's actually pop this widget open really quick. Let's take a look at the code I used for that. And it's actually pretty simple. Now, for the purposes of this demo, I am going to be using Dash Code. I will be only opening this widget in Dash Code. So that means taking the widget as it is on disk, and just opening it up so I can read the source code files, and then also run it in Dash Code if I like.
So I'm going to take the before version, so if you look in your version of it in before, and open it on Dashcode. So I'm going to take the before version, so if you look in your version of it in before, and open it on Dashcode. So I'm going to take the before version of it in before, and open it on Sync. And we're going to say, hey, call this Sync function, OK? So I'm going to scroll down to the bottom here, and here's the Sync function.
It's basically two lines of code, and all I'm doing is I'm turning off, I'm showing an element in the widget's UI, and then I'm replacing the string. I'm reading it from file, and I'm saying, hey, just dump it in the widget. So that's pretty much it. It's pretty straightforward. The only other thing that's worthwhile of note here, as I uncomment this, is the fact that I'm using this make key function to get the widget identified. So I'm going to go back to the slides, please.
So that's syncing widgets. And the good news is that the vast majority of it is free. You should handle the widget on sync event, though. It's really important. It really kind of helps round out the experience for users. But you don't have to if you don't like to, or if you're not using preferences that you need to keep updated very often.
So at this point, I want to talk a little bit about some of the technologies that are new in WebKit. WebKit, if you don't know, is the technology that underlies every Dashboard widget. And they've been doing some new exciting things that you should be aware of that you might want to take advantage of in your widget.
The first of these is CSS borders. They've implemented support for adding a border radius on any div. So this means that, whereas before, you might have included rounded rects everywhere in your widget's implementation with rounded corners, you don't need to do that anymore. If you have a div that has a border on it, you can just apply the WebKit border radius property to it, and it'll just give you these nice, pretty, rounded corners all the way around. There's also support for border images as well. So if you want to use that, take a look into what WebKit has to offer.
The next thing, and this is just the beginning of the high DPI story for both Dashboard and for web pages, are high DPI media queries. These queries are directives you can use in your CSS file to query if you're on screen and the device pixel ratio. So this is the magnification that the user is running at. And based on that decision, you can supply different resolution images. This is a really big deal if you're using custom images on your UI and you'd like to be able to use the appropriate resolution of image.
So you can use this in combination with the background size, WebKit background size property. This allows you to scale images appropriately based on your entire web page or on the entire size of your widget. One thing which I missed in the earlier bullet point there was window.devicePixelRatio is a property that was added to the window object. And this gives you the ability in JavaScript to find out what the magnification is. You can't just, it's not that you can only do it from CSS.
You can do it from any other object in the world. And there's also some WebKit changes you should be aware of. Just some things that you might be wondering if you're running your widgets in Leopard why they're not working quite right. Be aware of these things. And the first of one of these things is that Canvas, which we introduced in Tiger, is now a standard. If you use Firefox or Opera, you'll see that the Canvas is now supported in those browsers as well.
But part of the standardization process is that it's a little bit different than it was before. So what does that mean? Before, you might have been using Canvas like this. You said Canvas, gave an ID, closed it off, and that was it. Well, now in Leopard, in WebKit in Leopard, you will have to supply a width and a height attribute as well.
And so the width and the height are the size of the buffer that's being drawn upon. And then in CSS, you can then position it and size it however it need be, and it'll scale it appropriately. But you need to supply this in your HTML file as well. And then in addition to that, Canvas now supports fallback content.
So that means if the browser can't render that, the Canvas, you can put fallback content in there. And then it's very important that you always include then the ending closing-- the ending Canvas tag. If you don't do that, WebKit will see the rest of the web page as fallback content, and that's probably not what you want. So make sure you're using that closing Canvas tag.
Another thing in Canvas, and this is something that is really handy, is that the path buffer doesn't clear automatically anymore. You explicitly clear it by calling new path. So whenever you're drawing a new path on the Canvas, you should call begin path to start a new path. Other changes in WebKit include, for arbitrary elements, make sure you're using get attribute to get an arbitrary HTML element. Using the dot notation that's common won't work anymore. Always close your script tags.
It's pretty common for some people to just use a closing slash on a script tag. Instead, explicitly use a closing script tag. And finally, if you're using display inline block, the metrics have actually been improved for that. So if you're using it, go back, make sure everything's looking right.
And the good news about all of these changes is that for your friends that aren't using Leopard, they don't need Leopard to check them out. They can go to nightly.webkit.org, get a new copy of WebKit, and actually run their widget in there, and they'll find out right away what's going on with their widgets.
So that's a handy thing for you to do. So dashboard in Leopard. I think, well, first and foremost, make sure you're going to the dashcode session tomorrow morning and the marina. It's my third time saying it, and I'll keep saying it. Dashcode's a great creation environment, and you should really go there. Go there, see what it has to offer. So make sure you're going there. You should definitely be aware that widgets are now in one process, so make sure that you're never doing anything synchronously.
Dashboard sync is here. Make sure you're handling the widget.onsync event. That way you can take advantage of any preferences that have changed during a sync. And finally, make sure you take into account for the new WebKit enhancements and changes. So we've talked about the dashboard of tomorrow, but Dashboard's here in Tiger today. And there's some things that have come out since we met last year that you should probably take advantage of to help make your widget more interactive, more interesting, and possibly complement your existing Cocoa application, too.
So the first thing I wanted to talk about are the Apple classes. And these were introduced in Mac OS 10.4.3. And these are a set of JavaScript classes that provide common controls like the Apple button or the Apple Glass button, which you'll find on the back of a widget, usually, and the Apple Info button on the front used to flip to the back.
In addition to that, you'll also find things like scroll bars and sliders. And then finally, a neat little class called Apple Animator, which lets you animate over a range of values over time. So to use one of these classes, you normally just... In your HTML file, use a script tag to set its source equal to one of the files.
And the files, the implementation files, are in System Library, Widget Resources, Apple Classes. This is all in the documentation available on the web, if you're curious. But from there, you would just include the class that you need. So in this case, we're going to include an Apple button, applebutton.js. And then in our HTML file, we're going to want to have a div. So the div here is what's going to hold whatever element we're going to put into it. In this case, we're going to put a Glass button in there.
So in our JavaScript, in an onload handler, we're going to use a specific constructor. This is the Apple Glass button constructor. And it takes three things in there. The first thing is the element, which is going to house the button. The second thing is the text that you want the button to have. And the third thing is the handler that should be called when the button is clicked. So other constructors have other sets of parameters that they take in. We're just going to show off. This is just the Apple Glass button.
Or you could use Dashcode. Dashcode does all of this for you. You just drag and drop an Apple button on there. You drag and drop an Apple slider on there, an Apple scroll area. No code. It's almost too easy. We're making it too easy for you. But I am going to talk about one of these Apple classes, which Dashcode doesn't provide an API for, or a UI for, and that's the Apple Animator. So the Apple Animator lets you animate over a range of values over time. Its base unit is an animation. An animation has two properties.
The range of values, so the starting and the finishing value, and then an interval handler, which is a function that's called whenever the animation's value changes. So you wrap an animation inside of an animator. Animators are timers, and they take in also two parameters, the first one being the period of time, so how long you'd like the animation to go on for, and then the interval, how often you'd like the animation updated. You can also have multiple animations per animator. You're not just stuck in a one-to-one relationship there.
Once you've set this all up, you'll be able to just call start on the animator. And that just tells it, hey, get ready, get going. And then every time the interval is reached, so every time you want the animation to update, the interval handler for the animation is called.
And that allows you to actually change whatever needs to be changed for the animation. So let's go hands-on on this. And the good news about this sample, if you can switch me to demo, please, is that this works on Tiger. So you don't need to be on Leopard for this.
So if you open up the sample code that we made for this session, and you go to the Before folder for a sizer, you'll see that there's a widget here. Everyone see that? Can I see a show of hands, please? Yeah. Thank you. Okay, so I'm just going to double-click on this widget, and I am not going to install it.
There's a little secret here. If you hold down the Command and Option keys when the widget installer comes up, you'll see that the Install button changes to a Run button, and that will run the widget in place. So I'm going to click that, and you'll see we've got a pretty bland, boring widget here.
But in the top right-hand corner is an Info button, and that's the most important thing. I'm going to click on that, and I'm going to flip to the back, and all of a sudden we get this kind of clipped mess. And the worst part about this is that to get it to redraw, okay, now I've got to move it.
So this isn't really pretty. The problem we have here is that we've got a front side that's different than the back side. So we're going to use an Apple animation to actually shrink the front side to the size of the back, and then flip it over. So that's kind of a neat futuristic effect, and I think your users will like it.
So, oh yeah, and you flip it back to the front. It wouldn't look too nice either, so. So again, I'm going to take the Resizer widget, and I'm going to open it in Dash code. But feel free to pop it open in any text editor you like. Everyone with me? Can I see a show of hands? All right.
CHRISTIAN WONG: So now that I'm here in Dashcode, I am going to actually hide the canvas. Now again, I have opened this widget in Dashcode. This turns off most of its code generation abilities. I won't be able to drag and drop any parts to it, but it does let me use its code editor, and it lets me run it and debug it using Dashcode's facility. So that's a handy feature if you already have a widget and you don't want to import it into Dashcode.
I'm going to hide the canvas here, so I'm just looking at the source code editor. You can show the source code editor by clicking the View Item here in the menu bar and choosing Source Code. And here we go. So this is the JavaScript file for our widget. Is everyone seeing this? So I'm going to actually make this widget, this window, as large as possible.
I'm going to scroll down past the legal here. And so what happens is, what I'll tell you is that when you click on the Info button, this Show Prefs function is called. So Show Prefs right now just does two things. It stores the height and width of the front of the widget so that we can use it later when we're switching from the back to the front.
And then it also just calls the code that actually does the flip animation. So I'm going to add some code in here that actually sets up the animation. And I'm not going to use any old Apple animation. I'm going to use the Apple Rect animation. Apple Rect animation actually gives me values in a rectangle, so two sets of values. And so to do that, before we actually set up the animation, we need to create two Apple rectangles.
So if you'll uncomment the code that I have here in this function here and here-- I'll walk you through it. The first thing that we do is we create two rectangles, the first one being the starting rect. This is where we're starting from, the size of the front of the widget.
And then I'm creating a finishing rect. That's the size of the back. And here, it's got just two magic values, 320 and 150. That's just the size of the back. I'm telling you that. So now that we have the rectangles, our starting and finishing rectangles in place, I'm going to create the new rect animation.
And then here I'm going to pass in the starting rect and the finishing rect, and then this rect handler. So this is going to be called every time the animation is fired, every time it's triggered. So if you scroll down underneath the flip to back function here, you'll see I have the rect handler here. And all it's doing-- and it's not terribly important-- is it's actually resizing the widget window and a few elements on its interface. So just feel free to uncomment that and follow me back up into the set back function.
Okay, so we're back here to line 91, where we have the current racked animation. So we've set that up, and now I'm going to create a new animator. And the two values I'm going to pass to the animator are 500 milliseconds, that's how long I'd like the animation to last, and 13 milliseconds, that's how often I'd like the animation triggered, how often I'd like to update its values. So once I've done that, I'm going to associate the animator timer with the animation values, using add animation. I'm going to say, hey, when this is done, flip the widget over to the back, and then I'm going to start the animation. And that's pretty much that.
There's two lines that I do need you to uncomment, though, if you want this to work nicely. And the first one is this flip to back. Now our animation is handling this. We don't need to do this manually. And the second thing is, in the actual flip to back function, I have a line here that says window.resize to. Again, our animation is taking care of the resizing, so we can hide that. So I'm going to actually run this widget now in Dashcode, show you what it does.
Okay, so we've got our widget running here. I'm going to move it up here, and I'm going to make it really, really, really big. And then I'm going to do that. You'll see that it's sucked up to the size and flipped over. There's also code in the sample that will actually resize it on the way back out.
So if you click on the Done button, it'll actually then flip it over and resize it back out. We won't go over that now, but it's all there. It's just commented out. Feel free to take a look at that later. All right, if I could go back to slides, please.
So that's Apple Classes, and you should really look into them for three reasons. First and foremost, they'll save yourself a lot of time and effort. These are things that you won't have to include tons of code in your widget for anymore. You just include these files, use the appropriate constructor, and you'll be set.
So your code will be less complex, and that's really important too. And finally, the one added bonus here is that you'll stay in sync with Apple. If we ever update any one of these things, how they look, their behaviors, you'll get them for free. So use the Apple Classes if you'd like to have standard controls provided by Apple.
So the next thing which I wanted to talk a few minutes about is the Quartz Composer WebKit plugin. This is something new we've shipped in Mac OS 10.4.7. And this allows you to take a Quartz Composer composition and embed it inside of a widget. And you would do that if you wanted to have some tighter integration, graphics integration, and better motion graphics inside of your widget.
To do this, you would use the embed tag, just like you would for any other plugin like QuickTime. And important things you need to pass in are the type. So make sure that you're passing in type x-quartzcomposer. So this tells the browser to load the Quartz Composer widget plugin-- or, I'm sorry, a WebKit plugin, not any other. And then make sure you provided a unique ID.
The reason you provided a unique ID is if you'd like to interact with your composition via JavaScript. So for instance, in this function, I'm obtaining the composition from the DOM. And I'm just telling it to pause. I'm saying, you know what? Whatever you're doing, don't do it anymore.
And in this function, I'm taking the composition and I'm actually changing one of its published keys. If you're familiar with Quartz Composer, you can take any value in there and publish it as a key. Well, this function, set input value, allows you to specify the key and a new value for it. So you can pretty much interact with any composition like you normally would in Cocoa, but you can do it from JavaScript. At this point, I'd like to go to demo and show you some neat Quartz Composer widgets.
So I'm going to show my dashboard. And the first widget I've got here is, granted, not very exciting, but it's a graph widget. This widget is giving me some statistics back on throughput. And you'll see that I can move it around here. It's a cube with a bunch of cubes in there.
And you'll see that I can move the widget around, show you just the bar graph. I can also show you the labels really nicely. So it's a nice way to visualize data. That's something that Quartz Composer is really handy for. OK, so this isn't terribly inspired. I'll admit that. So let's get rid of this guy and show you another widget.
This is a widget that I came up with. And what this widget does is it actually gives you a little doodle board. You can draw around here inside of a widget. And again, this white area here is just a Quartz Composer composition that I've come up with. And then I've provided some controls. So I can change the color, and I can change the brush size. And I can doodle around in here to my heart's content. I'm not feeling terribly creative today, so I don't think you'll get much out of me there. And then you can clear it off.
And you get the idea. Again, this is all done with Quartz Composer. And it's also done with Dashcode. Dashcode has a Quartz Composer template in there. So it gives you a running start if you ever want to use Quartz Composer inside of a widget. So definitely take a look at that.
So I'm going to hide this one. This is one that I came up with, and I kind of like this one a lot. It turns out that the United States Geological Survey provides earthquake data. And so they provide it in an RSS form. So what I've done is I've just taken that feed, and I'm mapping the RSS data on this widget. So OK, it doesn't look like much from where it is. Let's take a look. Let's zoom in a little bit.
So what I've done with Quartz Composer is wherever the earthquake is, I've provided this ripple animation. It's something you really couldn't do with the JavaScript canvas. And there's no other nice way to do this, save using a full-blown, your own custom WebKit plugin. So using Quartz Composer is kind of a nice, intermediate way to get this neat effect. And the takeaway story here is that try not to be gratuitous with Quartz Composer. Try to keep it measured. Do neat things. And I think it'll serve you really well.
Can we go back to slides, please? So that's Quartz Composer, and we think you'll really like that. So the last part of the presentation, I wanted to spend some time talking about Cocoa integration, and when you should make a widget. So canonically, we've always told you that you should make a widget if you'd have a website, and you'd like to distill some of that information down to a handy tidbit that you'd like to give users. Or if you have a utility that you use often, and you'd like to offer a limited version of it on your dashboard.
But the third time that you can make a widget-- and this is something we really wanted to emphasize-- is to complement an existing application. A good example of this, for instance, is the iTunes widget. The iTunes widget provides some functionality that iTunes does, but it's in a much smaller interface that people can keep handy on their dashboard, so they don't bring up their iTunes, for instance, all the time. My favorite example for this is the TimeCard application. You have a Cocoa application that lets you specify users, and different jobs for users, and lets you print out bills, and all this stuff that you normally would want from a TimeCard application.
But a great widget for that would be just really simple. It has a pop-up menu on it that lets you choose a job, and then it'd have a big red button on there that lets you clock in and clock out. Again, people won't have to launch your entire application. Just use the dashboard widget and move on. So whatever users are doing 90% of the time with 10% of the effort, that's perfect for a widget, usually. So to talk more about Cocoa integration, I'd like to bring up my colleague, Matt Drance from DTS.
Thank you, Christian. Can you guys hear me? OK. So as the slide says, my name is Matt Drance. I work in developer support for Dashboard, and I'm here to talk to you guys about the stuff that Christian couldn't get his head around. That wasn't very nice. So what is a widget plugin? Well, basically it exposes native services to JavaScript, things that JavaScript out of the box can't necessarily get to. It can be used for things like data retrieval, API exposure for the rest of the system. An example of this is the address book framework, which I'll show you in a moment.
And these are not graphical plugins. Christian just was busy telling you about WebKit plugins, things like Quartz Composer, the Quartz Composer plugin that's new. And those are WebKit plugins. Widget plugins are faceless, headless. They're basically intended for information retrieval. So if you want to display something in your widget with native code, a WebKit plugin is what you want to use.
And the idea here is to open your own apps and frameworks to your widget. Maybe you have a common data store that you can get to with C or Objective C code. Maybe you have an entire framework that you've exported as part of your application. Christian mentioned the Timecode app. If there's a tiny subset of functionality that you want to expose to your users in a very simple and clean fashion, maybe this is one road you can take.
And as Christian kind of said also, you really want to use this as sort of a last resort. You want to make sure that JavaScript, DHTML, all of those technologies that are normally available in Dashboard aren't going to solve your problem in some way. So now that I've just told you not to do this, let me show you how. Can we go to the demo, please? CHRISTOPHER PINTO: OK, so the first thing I'm going to do-- and those of you who may have downloaded the samples, you'll see the birthdays widget, hopefully.
[Transcript missing]
This week, so we go on through to Sunday, and then this month. So for the rest of August. And each of these is a clickable link that will then open the person in Address Book separately. So how are we doing this? JavaScript can't talk to Address Book normally. JavaScript doesn't know what Address Book is. So how do we get to this information? Well, we're using a widget plugin.
So can we go back to slides, please? So the first step is installing the plugin. Well, it's pretty simple. It's just a simple bundle that goes in the top level of your widget. You can see a picture of it right here. It has a file name extension of .widgetplugin. And you include a key in your Info.plist named plugin with the actual file name. And that should have you ready to go.
The next step, of course, is talking to the plugin. Well, the plugin itself appears in JavaScript as a member variable on the window object. So you can see here we're doing a check for the window.birthdays plugin. And we'll get to the origin of that name in a second.
But basically, just kind of like the window.widget property that we check for when using the Dashboard APIs, we check for the existence of the plugin, and then go ahead and call our methods. And because this is part of the window object, you can use--you can reference it either implicitly or explicitly. And I'm doing both in that example you see up there.
So actually writing the plugin is pretty simple too. It's just a standard foundation bundle in Xcode that you then customize slightly. You write the code that you need to. In this case, we're writing code that talks to the address book. You register the plugin with JavaScript so that your JavaScript code can see it. And then you grant access to the methods in the plugin. The entire plugin doesn't become scriptable by default. It's going to be up to you to decide what's scriptable and what isn't.
And the other thing you can do, this is not required but highly recommended, is to create JavaScript-friendly signatures for your methods. Obviously, Objective-C and JavaScript don't use the same syntax. So if you have some complex methods that you want to expose, the names can get pretty mangled. So you should probably do this yourself rather than trust us to mangle the name for you.
So, the first step, of course, is to initialize the plugin. And this is the one required method in the widget plugin protocol, and that's init with WebView. This is just a standard Cocoa object initializer. If any of you have done Cocoa development before, you should know what I'm talking about. It is required, but it typically doesn't have a lot in it. Depending on how complicated your plugin is, this may have one line of code, it may have 20.
The important thing to mention here is that you really don't want to manipulate the WebView that is passed into you here. This WebView represents your Dashboard widget. But there will be a time and a place, if you need to manipulate the DOM from inside your plugin, you want to do that after initialization. This is really just the object initializer. This is not the initialization of the widget proper.
And the other thing I wanted to mention is that you need to start implementing a dealloc method. For those of you who have written plug-ins in the past, you've probably gotten away with not writing a dealloc, because every widget has been in its own process, and the only time the plug-in is going to be unloaded is when the user closes the widget and the process is dead. So the cleanup really isn't important.
But now, in the advent of process sharing in Leopard, your widget may close and your plug-in may be unloaded, but the process will still be there. So it's going to be up to you to be responsible with resources. So if you have a plug-in that allocates a bunch of member objects, make sure you implement your dealloc method so that you can clean all that stuff up.
So registering the widget plug-in. You'll remember in our JavaScript code up on the slides, we were referring, in JavaScript, we were referring to our plug-in as the birthdays plug-in. So this is where that name comes from. It's the Windows Script Object Available. This is actually part of the web kit, web scripting protocol. This is not a dashboard API. And this is how you identify the plug-in to JavaScript. You implement this method, and the Web Script Object that you passed is actually a wrapper pointer to the Window Object in JavaScript.
And basically, you just set the current plug-in instance as a value on that Window Object, which is what allowed us to use the Window dot notation earlier on. So that string, birthdays plug-in, that you see up here is exactly the name of the variable that we're going to be referring to our plug-in from using JavaScript.
So now this is the real meat of the topic. This is where you start actually exposing the functionality that you want to use from inside Dashboard. The first is, is selector excluded from web script? This is basically a filter on all the methods that you've written in your plugin, as far as which ones you actually want to be scriptable.
And you notice that the method is excluded, not included, so there's a double negative thing going on here. You return yes when you don't want the method to be scripted. You return no if you do. That's bitten a couple people. So if you find your plugin being universally scriptable, Check this.
And the other one is the is key excluded from web script. And we usually recommend that you just return yes to this under all conditions. That's yes, I don't want it exposed. So if you have a key on the plugin that you want to be exposed, go ahead and encapsulate it, write an accessor method, and expose that instead. And the last one is the web script name for selector method. And this is the one where you can take your Objective-C function name, method name, and turn that into something that's more readable by JavaScript.
And this is very important. It's not required, but if you have an Objective-C method with multiple arguments, then you're going to start getting colons and separate tokens in there. And those things don't really make a lot of sense in JavaScript. So this is your opportunity to write a JavaScript method name that's going to make sense to you when you're writing in JavaScript code. So let's go back to the demo. I can show you what some of this stuff looks like.
So the first thing I want to do is just have a quick look inside the bundle, or I could open the widget again. So for those of you who haven't played around with widgets too much, you can just right-click or control-click on the widget itself in the finder. Click Show Package Contents, and this looks a lot like the slide you saw before.
Here's our plugin with the .widget plugin extension, and it's good. We have Property List Editor installed. So I'm going to open the plist, and this is the key that's going to make the Dashboard client actually pick the plugin up. So as you can see, the name here matches the name in the finder, and it's pretty much that simple.
That's what you need to do to tell Dashboard to recognize your plugin. So once Dashboard has recognized your plugin and tried to load it, there's other work that needs to be done. But let's stick with the widget for a second. I'm going to go ahead and open this in Dashcode, just like Christian did before. And let's get rid of the canvas. Let's see the source code. So I'm going to go into the script here.
So let's get past the legalese for a moment, and let's have a look at Show Today's Birthdays. So I'm doing some general markup work here in the JavaScript, and then here's our plugin code. How does that look? Oh, that's nice and big. So first we check for the existence of the Birthdays plugin on the window object. This looks a lot like the slides. And if it's there, we go ahead and we call the BirthdaysToday method. And then we go ahead and process that information.
It's going to return an array of information for us. So the question is, we've already answered some of it, where does this name come from? And then also, where are these method names coming from? In order to do that, we need to go out of Dashcode and go into Xcode.
So I'm going to open up this project, and we're just going to run through all of the methods in here. This plug-in is actually pretty simple, as you can see. I mean, it's got a very small, defined set of tasks that it performs. Here's some more legalese. So the first thing I want to do is I just want to have a real quick look at the code itself. And if you're looking at the sample that you downloaded, you should probably see some pragma marks in here that classify the methods based on what they do. So this should be listed under AB logic.
So, oh, this is the header font. Excuse me. That's why I don't see them. There we are. So let's go ahead and just look at the people with birthdays and future. So basically I'm going to give an interval, so one in the case of a day, seven in the case of a week, et cetera.
And this is it. This is really all the Cocoa work we're doing in terms of business logic. We're just going ahead and creating a search and address book, and then we're going to return an array of all the people that match the current interval on their birthday property.
So let's go back up to the actual widget plugin stuff. So here's our initializer. And as I said, depending on what you're doing, this method may have a lot of stuff or it may have nothing. In this case, it's just a simple initializer that defers to the superclass.
So then when it starts to get interesting is the Windows Script Object Available method. Again, we have this web script object that's passed into us, which represents the window object up in JavaScript land. And we simply set ourself as a key on the window object. And if I were to change this name and rebuild the plugin, then I would accordingly have to go and change all the variable names that I used in my JavaScript code to refer to this Now we move on to the is key and is selector excluded from web script. And like I said, we don't want to really have direct key access from scripting. It's kind of just asking for trouble. So we return yes all the time.
Yes, we want it excluded. And then we have is selector excluded from web script and web script name for selector. Now there are a couple things you can do here, and I got a little clever. I don't want to spend too much time on it. But a lot of times people just get the selector name as a string and have a huge if else block in this method to see yes, no, yes, no, I want it.
So in this case, what I've done is defined a prefix here, and for all the methods that I actually want exposed, I preface each method name with web underscore. And so I basically just do a search for that prefix, and if it's there, then yes, we'll script that. And it's a little more complicated, but it has less code. And I tend to like that.
So here's our web exposed methods, and you'll see this is the final part. You'll notice that I'm not actually calling the address book method that you saw before. I'm calling something called string results for people. So let's go ahead and see what that does. The key here is that the previous method that I wrote that actually performs the search in address book, that search is going to return me an NSArray of AB person objects.
Now, similar to the other stuff we've been talking about, JavaScript does not know what an AB person is. So it's going to be our job to take this information and convert it into something that JavaScript is actually going to be able to work with. So in this case, I'm taking an NSArray of people, and this is going to be an array of AB people.
So I'm going to send an array of people to the address book, and I'm going to send an array of people to the address book. And this is going to be the key method behind all of the web exposure. So now, instead of an array of AB people, we're going to be sending an array of NSStrings. And with that final piece of information, I'd like to go back to the slides and talk about one last thing.
So, let's talk a little bit about this data conversion. I mentioned that the NSStrings are going to be readable. And that's because some of this stuff is converted for you for free by the Objective-C JavaScript bridge. Most of them are primitive types. You see them up here. An NSNumber becomes a number in JavaScript. NSString becomes a string. An NSRay becomes an array. I have an asterisk there, not because it's a pointer to an array. There are no pointers in JavaScript. This is not a true JavaScript array object.
For all your standard uses, it will be for you. You can index it. You can get the link on it. You can modify the contents. But in terms of the real object-oriented array class in JavaScript, it does not have all of those properties. But 99% of the time, this is going to do exactly what you need.
So, from the plugin, we returned an NSArray of NSStrings. And actually, it was a two-dimensional array. I forgot to mention that. It was a two-dimensional array. Each entry in the first level is a record for the person. And then each entry in the second level is a record for the person.
And then each entry there also has separate tokens for the first name, last name, and birthday. So we've got a 2D array of NSStrings that was successfully converted and usable from JavaScript without any additional work. So for more complex JavaScript objects, the non-trivial ones, those simply get wrapped in a WebScript object instance. And you saw that in the WindowsScriptObjectAvailable method. That passed in a WebScript object pointer that, in this case, represented the window object from JavaScript. So that's your crash course on plugins. And I'd like to bring in Christian for the rest.
Thank you, Matt. And really quick, to just kind of clarify, the great thing about using a widget plug-in is that if you have a Cocoa application, you'll be able to make a widget that complements your application. This is something you can do today. You don't have to wait a year for Leopard to come out.
You'll be able to make a widget as a feature of your program and ship it today. And we really want to encourage you to do that. Really jump on this bandwagon. People really like widgets, and people are anxious to have them. So you really should be giving one if you have a Cocoa app.
So just to wrap things up, I want to talk a minute about sharing your widget. And the most popular place for people to download widgets from is the Apple Downloads site. The 2,100 widgets I talked about before are all available from apple.com/downloads/dashboard. And this is the place to go for widgets.
People go here to download them all the time. We list them, and we have top 50 and the newest ones. It's a great place for that. And we actually have a new submission process in place at connect.apple.com. And the great thing about this is that when you submit a widget through this process, you'll be able to have a listing that's actually perpetual.
And so you can just go there and update it, instead of before when you just had to update your widget each time you wanted to put it on the site. So that's really handy. And it also ties into the download verification feature. The point behind this is to make sure that people are getting the widget they expect to be getting. So in closing.
Leopard. In Leopard, the message is that widgets are here to stay. They're not going anywhere. They're popular, and people love them. So say hello to Dashcode. Use it. It has amazing features that will help you make amazing widgets, great-looking widgets. It's also a lot faster, so people will be happy with the performance we're hoping.
And you also should be able to handle a sync event. So if your widget deals with preferences, make sure you're handling a sync event as well. But today, in Tiger, make a widget. It's a great way to differentiate your application from any other one on the market. Simplify it using the Apple Classes. Quartz Composer helps you integrate great graphics into your widget.
Compliment your widget with an application. I really want to beat home that point, I guess. It's really important. And finally, when you're ready, submit it to Apple. So your roadmap for the rest of this week, there's a widget lab in Mac OS X lab right across the hall right after this. Tomorrow morning, 9 a.m. in the marina, for the fourth time, I'm telling you about the Introducing Dash Code session. It's a great session. These guys build a widget from stem to stern, and it looks phenomenal. You guys are going to love it.
So make sure you're there, and you grab the sample code first. And then after that, at noon, there's the Dash Code lab, also in the Mac OS X lab across the hall. And then tomorrow afternoon, there's a WebKit lab. So if there's any questions you have about WebKit, not necessarily the dashboard-specific, head over there. That's at 3.30, also in the Mac OS X lab.
So if you have any questions, there's a fella, Alan Samuel. Bluger1 at Apple.com is his email address. He's our evangelist, and you should email any questions you have to him. And feel free to join our community at dashboard-dev at lists.apple.com for any questions you have, any questions about Dashboard or Dash Code. Well, actually, not Dash Code yet. It's not shipping, so just for Dashboard. But feel free to come on there and ask your questions.