Application Frameworks • 1:09:11
This session is for C and C++ developers who want to learn how to structure an application using the modern Carbon APIs and tools: RunApplicationEventLoop, Carbon Events, Interface Builder and .nib files, Sheets, Aqua, and more. Developers with existing source bases will learn how to incrementally move their applications toward a more modern architecture.
Speakers: Xavier Legros, David McLeod, Guy Fullerton
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
My name is Xavier Legro and I'm the Mac OS X evangelist. I work in developer relations. And I'd like to welcome you to session Making a Modern Carbon Application. The funny thing is that we were talking with the guys from HIToolbox and we were wondering about the sessions this year. We had great content for HIView. We wanted to give you a good update on what's going on with developing using HIToolbox. And then we got to the conclusion that there was still one missing piece.
You see, people have been using Carbon in different terms in the last three or four years since Mac OS X. There is this notion that Carbon is related to CarbonLib. And so some people think that, well, they're coming to the platform and they're wondering, "Well, should I use Carbon? Is it CarbonLib? Do I want to work on Mac OS X?" So we wanted to emphasize the fact that when we talk about Carbon development now on Mac OS X, we're really talking about developing using the toolbox.
And the idea is writing a modern C and C++ application. And the goal of this session is to develop a modern C and C++ application. So the goal of this session is really to kind of scratch what we've been doing, scratch the fact that you have CarbonLib to support and Mac OS X because we think now we are past that point.
And we want you guys to make sure we understand that as well. And the goal of this session is really to focus on new technologies we've been introducing, new concepts that are very, very important on Mac OS X for you to develop a modern C and C++ application. All right, for that I'd like to invite David McLeod on stage, who's going to be taking you through the different concepts and the main key architectures that you should think of when you develop your application on Mac OS X. David? Thank you, Xavier.
Thank you very much. And thanks for coming. I really am happy about that because it means to me that our message is getting out that the HI Toolbox is renewed and you can make a modern Carbon application with the new HI Toolbox. First, I'd like to kind of qualify who should be here. It's kind of three groups.
People that haven't made a Carbon application yet. Second, people that have made applications, but maybe Mac OS 9 based and made wait next event applications. And third, there's probably a group of you that are responsible for porting applications to Mac OS X. So this will be helpful for you, and that's sort of the target audience today.
What do we mean by a modern Carbon application? A modern Carbon application is a full OS X citizen, behaves efficiently, doesn't block and use up too many resources. And we get that because we've introduced a simplified event dispatching model called Carbon Events, where you're not polling anymore to handle your events. And this makes for a very efficient application.
Also, your user interface is now defined using NIBs. We'll get into those in more detail as the session goes on. Also, your modern Carbon application uses up-to-date OS services like navigation services, CFPreferences, icon services, quartz, all the modern services that are available to you on the operating system.
So what I'm going to go over today, we're going to show you how to make a new Carbon application using Xcode. Go over making a user interface using Interface Builder and making your user interface in .niB files. I'm going to show you some of the user interface elements that are available in HIToolbox. It's actually very complete and you can make a very full application just using the pieces that are available from HIToolbox.
Having those pieces is nice, but it also shows you how to handle events and commands so you can deal with the user as they use your application. I'm also going to touch on using alerts in your applications to sort of interrupt and notify the user of things and communicate with them.
The last thing I'll touch on is navigation services a little bit, just so you can show how to interact with users in terms of selecting files and folders. So I'm really only going to cover two frameworks today, and they're both sub-frameworks of the Carbon framework. So I'm going to cover HIToolbox, which is going to cover all of the user interface stuff. I'm also going to touch on navigation services.
Overall, what is Carbon? When we talk about Carbon, what does it mean? A lot of people think it's CarbonLib, like Xavier was saying, or that it's some sort of bridging technology just to get you onto OS X. It's actually not true. Carbon is a full set of C and C++ APIs that you can use to make a full native client, native application on OS X. These APIs give you full access to the OS X services that are available to you, like the user interface, accessing the file system, using the drawing primitives in Quartz. Specifically, HIToolbox is the set of C and C++ APIs that you can use to make a user interface.
So we're thrown around the term "modern" a little bit. What do I mean by being a modern application? Something that's important to us is that you use Xcode so that you can make a Mako application. Another benefit of using Xcode is that your application will be made as a bundle. I'll kind of cover that in the next slide. Also, being modern means that you're going to use Interface Builder to design your interface and make the user interface with .niB files.
As I stated previously, being modern also means using the existing Mac OS X services that are appropriate for your application, such as Quartz, Navigation Services, HIViews. to make your application. Also, you want to use the Carbon Event Dispatcher. It's the modern, efficient, non-polling event dispatching mechanism that's available in HIToolbox. I'm going to cover that later on.
So for those of you that may be familiar with writing Mac applications the old way, maybe similar to the way you wrote them on OS 9, or the way you wrote them using CarbonLibs and WaitNextEvents applications. There's a few differences. First, the old way, a lot of the APIs dealt with Pascal strings.
That made it very hard to internationalize your products because you couldn't use the extended character sets that are available and are needed for other languages like Japanese, Chinese, etc. All of the new APIs use CFStrings, and that means that they're Unicode savvy, and you can use all of these extended character sets to provide your application, specifically the user interface.
The old way of dealing with events was to use WaitNextEvent and pull continuously using up CPU cycles. The new way is to use Carbon Events and not really pull at all and only be notified when important events are coming to you. That means that you block and you're not using the CPU and you're an efficient, great performing application on OS X. The old way of dealing with files is not even available anymore, standard file, and is replaced by using navigation services to select files and folders and communicate between the user and the application where files are going to be saved and retrieved from.
Another difference is moving from a single application file where the application was totally enclosed in this one big file with resource forks and all kinds of things in there to an application bundle. An application bundle, it seems the same to you when you use OS X, but it's still... Like, when you go to use OS X, it appears to be one icon with everything all squished into one binary, but it's actually represented by a folder underneath that's only evident to you as a developer.
And that folder contains the binary of your application and your .nib files, any other additional files like maybe some sounds or some image files, as well as your plist to describe yourself. So it's actually a whole hierarchy that's underneath. You can also provide localization information in there, and it's all seamlessly done through the application bundle.
The old way of making your user interface was to build it up in a set of resources and maybe load it with a dialogue manager or something. The new way is to use a niB-based user interface by designing your user interface with Interface Builder. I'm going to talk about niBs a little bit later on in the presentation.
The old way of actually creating a user interface was to use controls. And the new way is to use HIViews. They've totally replaced controls. Even the things that you're used to calling controls, like buttons, et cetera, are actually underneath the covers, really just HIViews. So the modern way is HIV use because they also do what we call the compositing system, and that means that the drawing model is much more efficient when your user interface draws. Only what needs to be drawn gets drawn.
And lastly, the old way of drawing was to do it with Quick Draw and ports and all that. And the new way is Quartz. It's got all kinds of really great features. It's very efficient. And it's used system-wide, so you want to use the system-wide drawing mechanism in your Carbon application. So I'd like to invite Guy Fullerton up on the stage. He's an HIToolbox engineer with me. And he's just going to show you how to make an application with Xcode.
Switch over to the demo? Yeah, good. All right, so what I'm going to show you today is just kind of a reinforcing of some of the things David's going to talk about on his slides. Let's say I want to build a new application. Let's say I want to make some kind of picture viewer. So I'll take you through the steps I might do for that. First thing I'm going to do is launch Xcode to create my project.
And I'll choose New Project. Now, there's a couple different choices for Carbon applications. There's just plain old Carbon application, and then there's the nib-based Carbon application. I want the nib-based one because that's the more modern alternative. So I'll choose that. Let me put it on my desktop. And let's call it--I'm spacing. I've got to get this right, otherwise everything's going to go bad. Let's call it strip.
Okay, so Xcode creates the project for me, and Xcode's going to take care of a lot of the other sort of modern features. It's automatically going to build the application into a bundle, and it'll have a plist and all those kinds of things. So Xcode already sets up a main for me. It's just a very skeleton main.
It does the basic things you'd need to do at App Bring Up Time, which is very little these days for a modern Carbon app. And I'll go through that in a little bit more detail in a later demo. But for now, let's just build it and run it and see what we get.
So the app launches. It just gives us a basic window. We've got a menu bar that you can track in and whatnot. You can resize the window. Unfortunately, that's not the most modern UI for resizing, but we'll take care of that in a little while later. So that shows you just how to get sort of a bare-bones skeleton application up, and I'll go into more details in successive demos later.
David? Hopefully you've installed Panther on your machines already and maybe have tried to do this and you've seen that it's pretty easy just to get a simple app up and running. But a simple app is not very fun unless you have a user interface. It's not all about being fun, but you want to be able to have some functionality. And your functionality can't really be provided to the user without a user interface.
When you make your user interface, you want to make a modern user interface. And I'd like to kind of touch on the points that really define what a modern user interface is. First, you want to adhere as closely as possible, with exceptions, to the human interface guidelines. So there's a document that we'll put up a reference to later on, and you could read through that. It sort of gives you the guidelines about how you're supposed to use the user interface elements in conjunction with each other.
One thing about laying out the user interface, you can see in the top right here I have a picture of Interface Builder. There's some little blue dashed lines. So it has a guidelines feature. An Interface Builder helps you lay out your user interface according to some of the guidelines provided by the Human Interface Guidelines document.
The next thing you can do to adhere to the Human Interface Guidelines is to use HIToolbox controls. We work very hard on the controls to make sure they adhere as strongly as possible to the guidelines given to us by the Human Interface team. And if you use them, you get all of our hard work for free.
Next, you want to use modern compositing windows. I mentioned compositing earlier. It's a drawing model where only what's needed to be drawn gets drawn in your application. It's much more efficient. You can provide an application that's much more modern by using this. It's kind of nice because it's just a checkbox in Interface Builder. You click the checkbox for compositing. If you use all of our controls, they can be used in composited windows.
And you get the efficiency right there for free. And that's nice. The next thing is live resizing. Guy showed he was resizing the window there in the application. It just showed an outline of the window. And that's not really a responsive user interface. So you want to turn on live resizing. Again, it's just a checkbox in Interface Builder. You check it. And that means that as the user modifies the window, changing the size, etc., the user interface responds instantaneously, and the user can see what resizing that window means.
You also want to use the standard event handler. Again, guess where the checkbox is? Using the standard event handler for a window means that all of the routine event handling for a window, like what to do when someone clicks on a title bar and drags it around, or clicks in the close box or the grow box, all gets handled automatically through the standard event handler.
Also, when it comes time to interact with the user, maybe to interrupt them and give them information, you can use Sheets or standard alerts as appropriate. When it comes time to interact with the user to describe locations of files and folders, You can use navigation services. So all of these things together will give you the basics of a modern Carbon application.
So I talked about the user interface elements that are available to you. There's quite a few. I can't stick them all in one slide, so I've had to break them up into bits because there's just so much available to you. First, there's a few kinds of menus you can use. There's the menus you're used to that come out of the menu bar.
There are contextual menus that you get when you command-click or right-click on user interface. These contextual menus change according to what piece of user interface is being interacted with. There's also pop-up button menus. All of these are just lists of commands that the user can make, select to perform actions. They're just simple actions that a user can pick. Next, we have a whole bunch of different kinds of windows. And you want to use windows to present your user interface and your information to the user.
So the biggest window we have up there is a document window. You'll put the contents of any sort of document functionality of your application there. Here it's just a picture from TextEdit, I think, or SimpleText. Out the side of that window is a drawer where you can provide additional information about specific content.
On the bottom left, there's a utility window. That's a good place to show properties of objects in a user interface. On the bottom right, I show a sheet. And a sheet is a kind of window that actually is grouped with a document window, and it comes out of the top and is associated directly with that window. So you can interrupt the user and provide information about a specific window that way.
Next, there are a few kinds of toolbars. We have metal toolbars. I show a few examples here. The second one is large icons with text. And then I show some, there's different modes that you get all automatically and free. You can select small icons without text. And you can have iconless entirely. I don't know if you can see well in this slide, but there's a little chevron on the right, and that's all provided automatically. The chevron on the right turns into a set of menu items when there's not enough room to display the toolbar functionality that's available.
Now the controls. There's a lot of controls available to you. And I'll break it out into a few slides in a second, but I've highlighted some in yellow or orange here. These are the new ones that we've introduced in Panther. We have the segment view. It has a little asterisk there because we didn't finalize the APIs in time to get onto your seed CD. Sorry about that.
My fault a little bit. Also, we've introduced a text view, which is a wrapper around the MLTE text objects. It's very powerful. We've also added a search field so that you can provide the search functionality like you see in the Finder and as you've seen introduced in many places in Panther.
I've divided those up into a few different groups. That's the way I group them mentally. I don't know if everybody does. I'm just going to go through those groups to talk about the different kinds of controls that are available. The first group is buttons. There's push buttons that you're all used to, radio buttons and check boxes that if you don't know what they are by now, you've been using them for quite a long time. They're the standard ones you're used to. We have bevel buttons, sort of squarish buttons that can have icons and textual content.
Little arrows, which you see are the... On the top right there, it's a little set of arrows used to allow a user to increment or decrement some values. And we have the new segment view, which you can see on the bottom. I'd like to take a second to tell you what the segment view is.
The segment view is a way to allow the user either to select something or to provide some sort of state functionality when you click on the buttons. It's very powerful. Guy had a session earlier where he talked about it in detail. If you can see that on the DVD, he really provides a lot of good information about segment view.
The next group is text controls. We have a lot of those now because we've been adding some. First, there's edit text, which you're used to for putting small textual items in, say, a form user interface of some sort. Static text for labeling pieces of your user interface. We have the new text view, where you can provide much more complex textual information, allow the user to edit it and have it styled, etc., using the MLTE objects at its center.
We have the clock control, which is what we call the piece here that's used to display the time and the date. And you can allow the user to set display times or dates to users or allow them to set times and dates. We have the new search field, which you can use to provide the user a chance to give you feedback about what they want to search for.
It should be used exclusively for searching rather than any other specialized content because it's been branded to be a search field and it's very important to maintain that look as something a user will automatically think about as a searching item. And lastly up there we have a combo box which is sort of a combination between a pop-up button with a menu and a text field where they can sort of type select things.
The next group is tracks. So we have scroll bars in there where a user can scroll their user interface. A slider for indicating values on a sliding scale. Progress indicator for you to provide feedback about the progress of your application. That's the one that's got a blue rectangle on it there. And a relevance bar for providing feedback about how relevant maybe something in a set of searches is to a user. The more full the relevance bar, the more relevant it is to the user.
We have kinds of controls I call grouping controls, like the tab control, which is the outermost group here. It says 1, 2 at the top, where you can group the user interface by tabbed panes. A group box, show that there, just sort of put a box around. These are interface elements that are related to each other.
There's also the radio group, which is kind of a nice feature. It does some automatic behaviors for you. You can put up a series of radio buttons, then as the user selects the radio buttons, it communicates with the other radio buttons to turn them off and on automatically.
The last kind of controls is kind of a miscellaneous pack. It's our specialized controls. We're getting to have quite a few of those now. So we have an image view. We've got SpongeBob up in an image view here. Does anybody know who SpongeBob is? If you come to my office, you can meet him.
We also have the chasing arrows. That used to be a control that had arrows chasing each other. Now it kind of looks like a pinwheel or a spoke effect. It shows an animation that shows your application is doing something. We have separator bars. Those are vertical and horizontal lines. You can draw those to the user interface guidelines by using that control. They're used to separate your user interface into different compartments. We have the icon control and image well, where you can show other image content and they're clickable.
We have the scrolling view here. That's that other image here with the person in pink in it. And it allows you to put content in it and automatically scroll it. You get a lot of functionality there that you used to have to do by hand. And also for providing sort of hierarchical data representation or table data representations, we have a sort of a complex control called a data browser.
So back to the big list. It shows that there's a lot of user interface that you can use without creating your own to create your applications user interface. You shouldn't really have to create too many custom controls. You should be able to use these and make a fairly full-featured application.
All of these user interface elements, it's nice to talk about them, but I'd like to just touch on NIBs and talk about what I mean by NIBs. NIBs are an XML-based extensible way of describing a user interface. I'm going to get, I think it's best of all to show Guy, to get Guy to show you how to make them, but before he shows you, I'm going to talk about how you can load your user interface out of a nib.
Loading a NIB is very straightforward. There's an API called CreateNibReference. So you're going to have a file inside of your application bundle. and you'll know the name of it. You'll provide it to this API in the incoming nib name parameter. Tell it you want to load that nib reference. And that's all there is to loading it.
After that you'll load all of your nib content out of it. When you're finished with it, you dispose of the nib reference. Now, I don't mean that you have to keep your nib reference around for the whole lifetime of your application. Just open a nib reference, get what you need out of it, and close it again. If you need to use it later on in your application, you can reopen it without a problem.
That's okay, but all you're doing is opening the nib reference. The interesting part is taking your user interface out of the nib reference. So there's a few APIs for doing that. You can set your menu bar from a nib. You can create a menu from a nib. Or you can, kind of most interestingly, because you're going to be making a lot of your user interface in Windows, you can create Window from Nib.
All of these APIs take reference to the nib that you created with CreateNibReference, the API I showed in a previous slide. As well as a name that you have decided while you were creating the nib. So say you named your window "main window," you provide that string there, and that is the nib that will be loaded with createWindowFromNib.
Once you've loaded a few things out of your nib, so what? You know, you have, you've loaded them. What do you do then? How do they work? There's a simple API called RunApplicationEventLoop. And it's simple because it's very easy to call. I have a prototype at the bottom here. RunApplicationEventLoop. And it kind of gets everything going.
It starts the event dispatcher, which starts all of the standard event handling that happens in your application. It's important to note that this API, you call it and it goes away. It doesn't come back for the duration of your application until your application quits. does all the standard event handling, like application activation and deactivation, and that kind of thing. It calls all the event handlers that are necessary for your user interface to work.
And it's pretty easy to use. And why I bring this up is because just in the APIs that I've gone through, I can show you a .niB sample code right up here, and it's a full application. It doesn't do much, it's just a user interface. But it's a full application. So I create a .niB reference, referring to the .niB file in my application bundle. I set up a menu bar from that .niB by passing in the -- I have a menu bar named MyMenuBar in this example.
Then I create a window from the nib called myWindow. I'm done with the nib reference, so I dispose nibReference, and I run Application Event Loop. This is a full application, and it'll start up, put up a menu bar, put up a window that can be dragged around and used.
So I'd like to invite Guy to show the next step, making a user interface with Interface Builder. GUY SMITH: Thank you, David. OK, so he's told us about all the tools we need to put together an interface. And I will show you how to build one. So when I created the project with Xcode, it automatically created a main.nib for me. And in fact, it loaded some of those things automatically in the little skeleton app that Xcode created for me.
And this code looks almost identical to the code David showed you before. So no surprise there. So let's take a look at the nib that Xcode created for us. And we'll look at that with Interface Builder. First thing you'll notice is that the title of the nib is main.nib.
Minus the .nib part, minus the extension, that is the name you would pass to the Create Nib Reference API to create your nib. And similarly, everything that's in your nib also has a name. For instance, the menu bar is just called Menu Bar, so you'd load it with that name. And the main window is just called Main Window, and you'd load it with that name.
All right. So let's actually do something interesting with this nib. Let's open up our window in the nib. And I want to make a picture viewer, so I need a place to display some pictures, first of all. So I'm going to-- oh, before I do that, I actually want this thing to look a little bit better than just a plain white canvas. So let me bring up the inspector. And I want to set a few attributes on the window. So there is a notion of a theme brush that can be associated with the window. And this determines how the window background paints.
Since this is going to be a normal window in the document layer of an application, and that's sort of the mode-less layer, I want the mode-less look, which will give me the Aqua lines. Now another thing I need to set on the window is I want some of those modern features turned on.
So I'm going to click the library size checkbox and the compositing checkbox so that I can use HIViews and so I can get a nicer looking resize than just that sort of rectangle. All right, so now let me actually add some stuff to the window. I want a place to put my images, so I'm going to drag out a group box here. But I don't want a title on it, so I'll delete that.
And let me put it in the right place. So here's an example of some of the guides David was mentioning for Interface Builder. It lets you sort of snap your controls and views to the right place according to the user interface guidelines. And Interface Builder does that automatically. So let me grow this thing out a little bit. And I want some push buttons.
Put that in the right spot here. I want a previous button. One thing I want to do to this button is I want to be able to react to it when the user clicks on it. And the way I'm going to do that is to set up a command, associate a command with this button. It's just a little four-character code. This command will automatically be sent out via the Carbon event mechanism whenever the user clicks on the button. Now another way I want to set up this button is by giving it a signature.
This signature allows me in my code to be able to fetch that button from the window. I can say sort of give me the control with the signature previous and ID 0 out of a window so I can manipulate it later if I need to. So I'll go ahead and set that up. And in addition to the previous button, I want a next button. Let's get that in the right spot. Give it the right title.
[Transcript missing]
GroupBox, grow that to the right size. And I need to set an image view specific-- oh, I need to do two things to the image view. First of all, I need to give it the right signature so I can find it later. This one doesn't need a command, so I don't need to do that. Let's set up an attribute. I'm going to set the scale to fit attribute. It just modifies the way the image will get actually displayed in the image view. OK, so let's save our nib.
and Rebuild Our Project. And it comes up just like that, so the interface got loaded. Now, unfortunately, even though we've got library size turned on, the views are not relaying out. This is something we addressed in Panther. We have a new set of APIs called HIV layout. We're going to talk about it in session, I think it's 4:25, which is immediately following this one, but in the room upstairs.
And we'll go into details on exactly how to use that. Now, I've already written some code to do that. And let's go ahead and add that to our application. I'm going to add a bunch of files. Oh, let me add it to a different place here. Add a bunch of files.
So I'm also adding some files for some other functionality I'll show off later. The important thing I needed is in FilmstripView.c. So let's go back into main and let's set up the layouts. So I will need to include a prototype that's in FilmstripView. And, all right, so let's see, I've created my window. Right before I show it, I want to set up the layouts that take advantage of HIViewLayout to automatically resize all my widgets. And that is via my setupWindowLayout function.
So now that that's in, I should be able to build and run. And now, when I resize the window-- oh, you know what? I must have missed something in the nib, but I'll fix that later. So anyway, the buttons move around appropriately, and the group should move around as well. But I botched something up, but that's OK. I'll fix that for the next one.
So that shows you how you can construct a basic interface. And like I said, we'll go into more details on how to use HIV layouts, which is a really powerful technology. And we'll go into details in another session. So in subsequent demos, I'm going to actually add some functionality to this app.
But before I do that, David needs to lay some more groundwork for you. Thank you, Guy. So Guy showed that it's pretty easy to use Interface Builder to add some of the user interface elements that are available from HIToolbox into your application so that you can provide feedback to the user about how your application is going to look and work.
Next I want to go over roughly what the architecture of a modern Carbon application is. So we showed you the nibs that define the user interface. I think that's very important. Once those nibs are up, Carbon Events are what animate it and what give it life. So the handling of Carbon events is important. Like I mentioned before, it's a non-polling way of handling events, where you only listen to the events that are important to the application.
I want to talk about event handlers in a few slides. There's also the handling of HICommands. HICommands, the way I think about them, is that they're sort of simplified Carbon events where they just describe an action that the user wants to take. You do this through an HICommand, which is just a 32-bit value that identifies an action.
You also want to provide a reactive user interface. What I mean by that is that as the application changes its state underneath, you want the buttons and menus, etc., to react to that change of state by becoming enabled or disabled or have the names of menus change, etc. Menu items change, etc. Other than that, all you need to focus on is the functionality that you're providing in your application that makes it special and makes your customers want to buy it.
So here's kind of a pictorial overview of what I just said a minute ago. So we've got these yellow blocks up here. They represent the nibs that you made, as Guy showed you. As you load them in, you can maybe programmatically modify them, as he did with adding layouts to them. And we'll show you that you can also install event handlers to provide your custom functionality from the application.
Once you load them, you run Application Event Loop. And the standard event handler is going to take care of all the routine operations. That's what those orange circles mean. That means that all the window dragging and menu tracking, etc. are going to be handled by the application for free. It's all routine stuff and you don't have to worry about it.
But say you put in an event handler or a command handler on the OK button. When they click the OK button, you can have a custom event handler that's there that gets called and then you can deal with your functionality afterwards. And that's really what you want to focus on is your functionality that you're providing that makes your application special.
So I keep talking about events. What are these events? They're the basic messaging system of HIToolbox. So when a user clicks, or there's any typing, or there's communication between user interface elements, or any part of the application, there's these messages going around, and they're Carbon events. HIToolbox internally sends tons and tons of events. And that's what makes the whole event dispatching system so powerful.
The cool thing is that you don't really need to handle too many. In fact, you don't need to handle any at all. You only have to focus on the ones you care about. You ignore most of them all the time. There's many, many events that are happening that you never even know are happening and you don't really care.
I've mentioned standard event handling a few times now. What is standard event handling and what's so special about it? Well, like getting your eyesight this WWDC or getting a t-shirt or whatever, the best thing about it is there's tons of free things in there. It means that you don't have to do any work. And the less work that you have to do, the more you can focus on the functionality of your application.
So, HIToolbox really deals with most of the events that happen automatically, like application activation, deactivation. You don't really want to pay attention to that unless you care. Menu tracking, control tracking, and all the window handling. The more you use this automatic behavior, the better off you are because you haven't had to do much work.
And if we change the way the handling works, we're going to change it in the standard event handler so that it behaves the specified way. And you'll get that behavior for free, even in an already shipping application if we can. Best of all, it simplifies your UI code so there's less work for you, less work for you to keep track of, and it's easier for you to maintain your code base.
So I got a standard event handler up here. You notice some of the spaces fell out. It's standard event handler all squished together. That's kind of a special event handler that we have for Windows. I mention it because you need to turn it on with that checkbox that I showed in a previous slide and was on in the Interface Builder when Guy was making the user interface. It handles all of the standard window behavior for free, so you don't have to worry about dragging the window around or resizing it or handling the clicks in the close box.
It also, much more importantly, routes all the mouse and keyboard events to the controls and the user interface that you use inside of a window to make up your applications interface. As I mentioned earlier, you just click a checkbox in iB and you can forget about it. That window is just going to behave properly for the duration of your application.
So I'd like to kind of break down events and event handlers a little bit. Events are identified by their class and their kind. It's a pair. It's a pair of two 32-bit values. So there's an event class, like KEventClassControl, or KEventClassWindow, or KEventClassMenu. And then there's different event kinds in those classes. So say with the Control class, there's KEventControlBoundsChanged or KEventControlPartHit or whatever.
There's a lot of events, as I mentioned, and we work very hard to document all of them very well. If you go and read carbonevents.h inside the HIToolbox headers in System Library Frameworks, they're very well documented. And the more you learn these, the more you'll learn about what kind of power you can add to your application when you need to.
Now there are a few different event types. We break them down into sort of three categories. There are method types of events, and that's an event that gets sent to something to tell it that it's chance to handle "It's functionality specific to itself. For example, you could have a k-event control draw event, get sent to a control. And that's... That's the event system calling the control and letting it know that it should do its functionality then.
There are also notification events that get sent to different event targets. Let the target know that something is happening. An example of this might be a k-event control bounce changed event. So you can let a control know that somebody has changed its bounce, and the control needs to react to that somehow.
There's also hook kind of events. We call them hook events. That's where the event system lets a target know that something's happening. Then that target can optionally do something about it if it wants to. A good example of that would be maybe a k-event window bounds changing event. Notice "ing" at the end instead of "changing." That means that the window is in the process of changing. If you want to override that behavior a bit and modify the way it's changing before it actually changes, that's your hook to be able to do that.
So I said event targets a few times there. There are a few different kinds of event targets. The main ones are the application event target. So any events that are going to the application like the quick command or the application event, application activated command, event will go to the application event target. There's also a window event target, so any events that are related to windows will go to the window event target. Same goes for controls, and there's a few other specialized kinds that I won't get into.
This is all nice, but what you want to do is install your own custom event handler on the pieces, on the targets that are important to you to add your functionality. And you do that with a custom event handler. And here's the prototype of how your custom event handler should look. So I have it. It's called MyControlEventHandler.
It'd be great if it was bigger. I guess I didn't make it big enough. And it takes, when it gets called, it gets passed an event handler call ref. Generally, you don't really care about that unless you're passing the event on to some other part of the system. You get an event ref, which is important. It tells you what kind of event is coming in.
And you're also given a chance to set up some user data beforehand, and that user data will be passed in to the event handler. So let's go into that in a little bit more detail. Here's an example event handler. It's a control event handler, and it happens to be handling a notification that the control has changed its bounds.
So the first thing it does is it takes some user data that's passed in and it changes it, casts it into a control. I'll show you why that is in a little bit. That's a good way of passing in which control is being handled, which control is being passed this event.
Next thing you want to do is use getEvent parameter to extract information out of the event as you handle it. In this case, it's extracting the current bounds of the control as a rectangle and sticking it into the new bounds, hirect. Once you have that, this is where you have your chance to provide your functionality. You do something with what the new bounds of the control was. Maybe you relay out a control or I don't know exactly what you're going to do.
And then when you're finished, you return no error to mean I handled the control. So you really only want to return two different types of things from an event handler that the event subsystem is going to care about. No error, which means I handle it and there's no errors. You don't have to worry about anything. Or an event not handled error, which means I didn't handle an event. You can do whatever you want on top of that.
Installing an event handler is very simple. You install a event handler and you pass in the target. Who you're installing the event handler on, and who's going to be receiving these events that you're going to be allowed to do some handling on. I'm going to pass in an Event Handler UPP. That's a UPP, Universal Procedure Pointer, I think, to the Universal Proc Pointer, to the custom event handler that you've written.
You're going to tell it how many events you're interested in, and you're able to pass in an event type spec. A pointer to an event type spec array, which is a bunch of pairs of class and kind, event class and event kind, that describes which events your handler is interested in receiving calls for.
The last highlighted item in user data, you can pass in whatever user data you want. What I like to do is pass in the instance of the kind of target that I'm passing in. So if I'm installing this on a control, I'll almost always pass in the control ref as my user data. It's a good, easy, fast way of getting your control ref back out when your event handler gets called. It's on a window, I pass in the window ref as my user data, etc.
And what you get out is a event handler ref, which you can use in various ways afterwards if you care to. It can be null. Most of the time you don't really care. There are a few shortcuts to get the target. So everything has a target, as I mentioned earlier, like an application, a window, or a control. And so there are some macros that are just kind of wrappers around this API where you can say, "Install Control Event Handler," and specify the control rather than getting the target out of the control and specifying that.
It just makes it a little bit easier for you to call it. There's a nice one for "Install Window Event Handler." And you can call "Install Application Event Handler," and it'll just go get the current event application target and pass that in. It's very similar to the one above.
So here's an example of installing an event handler. Like I said, it's very straightforward. We've got the prototype for my event handler, which is implemented elsewhere. This is just the prototype for it. It's implemented a few slides back. I have an event type spec array where I've specified that I'm interested in kEvent class events and kEvent control bounce chains event kinds. And then I just install control event handler. On my control, this is the wrapper, it goes and gets the event target ref for that control.
I pass in a UPP to my control van handler. Conveniently on OS X, UPPs are just the function pointers. In this case, I'm just passing in a function pointer for an easy example. I count how many event types were in that event list. There's a nice macro in carbonevents.h, getEventTypeCount, which will just do a size calculation and pass in the right number there all the time, no matter what you change the array to. Pass in the event list.
And also, the last one there is my control. So as my user data, I'm passing in the control ref to my control so I can extract it later on easily. Here's an example of a little more complicated event handler where it's handling more than one kind of event by using getEventClass and getEventKind to check the class and kind of the incoming event. So it uses the inEvent parameter that's coming in, use getEventClass and getEventKind to check if it's, again, a kEventClass control or kEvent control bounds changed.
That's all nice, but I'd like to give you a little overview about the flow of the events and commands in your application. When an event is first sent, this is kind of a general thing, I'll talk more, maybe some specifics at the end, goes to the focus control. If the focus control doesn't handle it, say in this case the button was the focus control, if the button doesn't handle it, it gives its parent, the group box, a chance to handle it.
If the group box has any parents, say a tab control in this example, it gives that parent a chance to handle it. If that control doesn't handle it, it gives the parent window a chance to handle it. If the parent window doesn't handle it, it gives the application a chance to handle it. So it kind of goes up the hierarchy and gives everybody a chance to handle events.
Now there are a few exceptions. Sometimes notifications aren't really important to propagate or some method calls. You tell a control, "Hey, draw yourself." You don't want to give the window a chance to draw the control. It just doesn't make sense. So there are a few exceptions and those are all well documented in carbonevents.h.
I just want to touch on handling commands a bit. Like I said, they're simplified Carbon events where you just have a 32-bit value identifying an action that the user wants to take. What's really nice about these is that the source of the command is not really relevant. If you want to close a window, you don't care if the user is going to close the window by clicking the close box or selecting close from the close menu. All you care about is they want to close the window. So it's nice that you can kind of ignore the source of a command, and that's what makes them special.
It doesn't really matter where they come from. They can come from menus. They can come from controls. They can be programmatically generated. You just want to handle them. Say, "Hey, this command happens. "I want to handle that command." And it doesn't... The handling of the command is really contextual, so whoever handles the command, it kind of depends on what the command is. So you can install a command handler on different controls in your user interface to respond or anywhere, in any of your event handlers.
Like I said, you don't really care about where the command comes from. And what's nice about that is that a command can come from multiple different sources, and you don't care as long as you're getting a command. In this case, I'm getting a "khi-command-close," which is a sort of a standardized command that you could use out of carbon-events.h.
And it doesn't matter that it's coming from the close box or maybe an OK box on a window or from a menu item. All you care about is that the user wants to close, and you can handle it and not worry about where it came from. So, here's an example command handler, and it's just a specialized event handler, where we're checking, we're doing getEventClass and getEventKind again, but we're checking for this specialized class and kind pair, keventClassCommand and keventCommandProcessKind.
The command ID is a parameter of the event. We just extract the HICommand out of the event using getEvent parameter, and then we switch based on the command ID. And this is where we dispatch out and handle our action. This is where you can provide your functionality. So next I want to ask Guy just to go through handling events and commands in the application a bit and kind of build up our application a bit more.
Okay, yeah, so let's add some event handling here. Because Xcode and Interface Builder already set up my window to use the standard event handler, most of the functionality happens for free. You can click on the menus, you can resize the window, drag the window around, click on buttons and stuff.
All I really care about is when the user clicks on a couple of buttons in my application. Now, if you recall, back when I set up the nib, I put commands in each of the previous and next buttons. And so those commands are going to automatically be sent out whenever the user clicks on those buttons.
So I just need to listen for those commands. So let's go ahead and wire up that functionality. Okay, so what I need to do here is include another header for the prototype. I've got a handler already written. And I'm just going to make sure to install that on my window after I've set up the layout and before I show the window.
Now let's take a look at that handler. Actually, before I show you the handler, let me show you something else. I've already defined some constants that represent those same commands that I associated with the button, and I'll be using those a little bit later on in the code. So here's my, it's a little wrapper routine that shows you how to install an event handler on a window. I built up one of those event type specs, which is a list of all the events I want to handle.
In this case, I only want to handle one event. It's an event of class KEVENT_PROSESS_COMMAND. This is the event that gets sent out when a command is chosen from something. And then I install my handler on the window, and the handler looks an awful lot like the code you just saw from David.
If I get one of these events, the KEVENT_CLASS_COMMAND, KEVENT_PROSESS_COMMAND events, I need to extract this HICOMMAND structure that describes things about the command. It tells you specifically what that command is. It tells you what the command ID is, as well as some information about where the command came from. So you can find out if it came from a menu, and if so, which menu item, should you need to know that.
In my particular case, I don't need to know any of that. So I just switch on the command ID that comes in. And right now, I've got some stubbed out functionality, but just for this demo real quick here, I'm going to beep whenever the user chooses the previous or next image. So let's go ahead and build and run.
and hopefully we've got volume on on this machine. Uh-oh, I don't know if the volume's turned on. Okay, there we go. Yeah, so you can hear this is beep working. That's not a whole lot of great functionality. I'll actually show you how to put some real functionality behind this in a few minutes after David goes over some other features. Thank you, Guy. So you can see that it's very easy to install an event handler to take care of the events that you care about. In this case, it was some commands, handling commands.
Next I just want to touch on, we're not really going to use alerts in our demo application, I just want to touch on alerts and when you should use them. So what are they? They're a way of intercepting, interrupting the user to tell them something important. You've seen them before. So you can inform the user of errors or warnings or bad things that have happened, maybe good things that have happened. When should you use them? Well you should use them when it's important enough to interrupt the user's workflow.
with something that they need to address. There's two different kinds of alerts. You get standard alerts, which come up in a dialogue, as you see on the top there, and that interrupts the application application-wide. That means that the user can't use the application anymore until they address the alert that's come up in front of them. You want to do that with something that's important. Say they quit the application and they have multiple open documents.
You want to interrupt the whole application and say, "Hey, you don't want to quit until you've saved all your hard work. You're going to lose it." There's also sheet alerts, and these don't interrupt the application, the user application-wide. They interrupt the user just on a per-window basis. So they come up out of a sheet, maybe out of a document window, and interrupt that one window and give information about that one window.
So say a user is just going to close a window that they had modified, you can have a sheet alert come out of there and say, "Hey, you've changed things in this document. You're going to lose them if you just close it without saving." Using standard alerts is very straightforward. There's an API called CreateStandardAlert. You pass in the alert type. You can give it a string for what kind of error has happened, and you can give a little explanation about that error.
There's a few settings that you can set up in a param block. I'm not really going to cover that. You can read about it in dialogs.h. And what comes out as a standard alert? What comes out as a dialogue ref to the standard alert? Which you can then pass to the next API, RunStandardAlert, where you pass in the alert dialog ref of the alert you've just created.
And that will block the application and won't return until the user's dealt with that. The first step is to create a dialog. The dialog is a set of standard ones that are listed in dialogs.h that you can use to determine how that dialog was dismissed, and then you can take the right course of action.
Here's an example of them in use. So I'm creating a stop alert because I can't save because a file is locked and I can't override it. So I just use createStandardAlert to do that and I run standard alert, an interruption comes up, a dialog that contains that message with maybe an OK button, that's all I care about. I run the standard alert and when it comes back the only way they can dismiss it is by clicking the OK button. My part hit will indicate that the OK button was clicked.
Using alert sheets is very similar. You pass in the alert type and the error and the explanation. Everything is exactly the same except you also pass in a notify target. This is important because you're not going to stop the application and block it application-wide. You're only going to interrupt one window.
So you want to pass in a target that's going to get events from this sheet that comes up to let your application know when it's been dealt with. So most of the time you'll pass the window target of the window that's being interrupted. And what you'll get out of that is a dialog ref to the standard alert sheet that is created.
To show this sheet, use the ShowSheetWindow. It's another API. You'll notice, though, that it takes as its parameter in sheet is a window ref. We've been using dialog refs for standard sheets up until now, and it's kind of a little bit of a legacy workaround where you have to get the window out of the dialog ref. I'll show that in a second here.
So here's an example of using a standard sheet. It's a stop alert that comes up. There's a very similar message to what I used before. Can't save that file because the file is locked. But in this case I'm passing a notification target, and that's who's going to receive events when things happen to the sheet. So I get window event target of my window, and that's the window that's being interrupted by this standard sheet.
I get that sheet back as a dialog ref. And then I need to pass a window ref to show sheet window, so I get dialog window from that sheet. and that brings up the sheet and then your application will then continue. And you'll only get notifications about that sheet closing later on as events coming into your event target, in this case your window event target.
Another way that you will kind of need to interrupt to interact with the user is files. It's a very, very common one. Users are doing all kinds of things with files. You want them to be able to select files and folders, maybe select save destinations. And to do this, you want to use navigation services.
It does all the work for you. You don't have to worry about how the file system looks or where everything's laid out or anything like that. You just call the APIs and it'll do all of the open save panel stuff for you. Users are very used to it. They know how to use it. And you're providing a user interface that they're used to.
Good thing about this is that when Navigation Services revs, like it did for Panther, you might have noticed, you get all these improvements for free in your application. So everybody that was using Navigation Services got the new Open and Save panels system-wide for Panther. It's also very customizable, so if you need to do something special, like have a pop-up box to select a kind or some check boxes for some settings, you can add that to Navigation Services easily.
If you're not doing any customization, it's very straightforward to use navigation services. In fact, I'd say it's pretty easy, although the API looks a bit daunting. So say you want to choose a file, nav choose file. You can specify a whole bunch of parameters, and I encourage you to go to Navigation Services Framework, navigation.h, and read about them. But in general, unless you're doing some customization, you don't really care about those. You'll notice that they're all documented down the right-hand side as being able to be null. So if you pass null for all of them, you'll get the standard behavior of Navigation Services.
In fact, all I really care about is the reply coming out. That's the most important thing, because that tells you what the user selected or that they have selected. NavChoose folder is very similar. Again, you can see there's a whole bunch of different parameters, but the only real important one to you is reply, unless you want to do some customization.
So here's Navigation Services in action. I use navchoose folder. I'm passing null for all the parameters except for the reply, which I'm passing in as a pointer to a navreply record. After that returns, I check to see if the reply was a valid record, if the user actually selected something.
And then, because the reply can contain a lot of information, like multiple selections, etc., we use Apple Events to extract the selection out of the returned reply. And it's pretty straightforward. So you use aegit nth pointer, you pass in the selection from the reply, you tell it you only want one thing and you're interested in fsref, and you can get out your folder ref.
So let's bring Guy back, and he'll show you how to use one of those APIs in the application. Okay, so a picture viewer is not very good unless I can provide the user a way to choose some pictures to view. So I'm going to add that sort of functionality.
But I need to add another button to my nib here so that the user can... "So let's add another button. Let's call it, I don't know, set folder. Make it a little bit bigger so it looks nice. And, uh, let's add a button. Now, in order to react to a click in this button, I need to give it a command. So we'll do that.
and to show off the power of commands a little bit, let's also modify our menu bar. Why don't we skip the menu for now? We're running short on time. Oh, we're running short on time? Oh, indeed we are. Okay, so I could put that command in the menu bar and invoke it that way as well.
But for now, we'll just put it in the button here. Okay, so we're saved. The next thing I need to do -- so I don't want to really worry myself about the various image formats that there are on Mac OS that we support. I'm going to let QuickTime deal with that because it does it very well. So what I need to do here is add another framework, QuickTime framework to my project.
[Transcript missing]
Those sys beeps I put in place and put the real functionality behind the previous and next buttons. And so you'll also see that I handle a third command in the handler I already showed you, which handles the set folder button. Now, my code I've already written for set folder just uses that nav choose folder API exactly the way David showed you up there before. So we'll just build and run this.
Click Set Folder. Up comes the Nav dialog. And now I'm browsing some pictures. And I react to that as the user clicks the Next and Previous button. So I did all that just by installing one Carbon Event Handler and building my nib. Now that's cool, but not every application can just use these standard views that we already provide.
A lot of applications need their own custom views, and I'd like one as well. I'd like this interface to be a little bit better. I'd like to kind of show previews, if you will, of all the images-- of the images in the folder that I'm browsing. So I'll add that very quickly. Reopen my nib. Let's grow the window a little bit bigger. And--oops. There we go.
I'm going to throw a user pane in place, just sort of as a placeholder. And my code goes in and replaces this with my actual view. Put that in the right spot. Let's give him the... Oh, I'm trying to remember my command. Ah, yes. Give him the right signature so I can find him later. And I don't actually need to give him a command. I think you should give it a command. You think I should give it a command? Okay, let's give it a command. Oh, David knows we should give it a command. That means we must.
All right, and so my code's already been modified, actually, to deal with that. So all I need to do is rebuild and rerun the application and choose another folder. Let's do this one. And all of a sudden, hey, I can see myself, and I get previews of myself as well, and I can scroll around and react to the next and previous buttons. These are images from last year's demo, so they just happen to have these previews.
So that's it. That's pretty much straightforward stuff. We'll actually go into details in the HIView in-depth session on how you can create a custom view. Not exactly this one that I showed here, but something that's actually a lot cooler, because this one here is actually fairly simple. So anyway, I'll hand it back over to David to wrap up.
Thank you, Guy. So finally we got some functionality into the application. The end functionality that you're going to add to your application, it's important. And you can kind of forget about some of the user interface details. You didn't have to worry about the window resizing and the window handling and all that. You got the full application by focusing on the functionality of the application.
So in summary, it's hard to show you everything that's available in HIToolbox. We just want to give you a good start. So you want to use the new HIToolbox to make a fully featured, full Mac OS X citizen using C and C++ APIs. We want to use .nib files to create a great user interface, but one that you don't have to really worry about how it's all implemented, because you'll use Carbon Events and you'll only pay attention to the events that you're interested in to make your functionality, to make your great app. So I'd like to bring Xavier up to go over some of the documentation that's available and the roadmap for other sessions that relate to HIToolbox. Thank you.
I apologize, we've been running a little bit late, so we're going to try to go quickly over the documentation so we have time for some questions. We have a bunch of documentation available now for Carbon Events and HIView in general. I encourage you to go on the ADC home where you'll see a list of the new documentation and all the things that are available for doing C and C++ development.
When you look for documentation or sample code, check out HIToolbox first and then go to the Carbon section, depending on what you want to find out. Some documentation as well is available for like the human interface guidelines to help you design great user experience on Mac OS X. And hopefully none of you need the Carbon Porting Guide anymore.
As I said, a lot of sample code. Look in the HI toolbox for Mac OS X folder on the sample code in the Carbon folder, and you see a bunch of things for like HIView. Pretty nice. And I encourage you to go, which brings me again to the documentation, and which is something actually the team wanted me to emphasize, which is they've spent a lot of time actually documenting the headers as much as possible as a first point of documentation for you guys. It doesn't replace, you know, of course, real documentation, but I think it's a good start for you, like, when you're looking for like what a Carbon event does, a special Carbon event and things like that. So check the header files.
One session, if you have custom controls that you should really, really, really go and see is the next one on the third floor in the Presidio room where actually Ed will be talking about HIView and we're going to give you, we have very cool demos where we're going to show you on how to implement your own custom HIView. Very nice, I encourage you to attend that session of course.