Carbon • 57:18
This session provides an overview of the High Level Toolbox architecture, including a focus on Apple's new foundation for building user interface objects. A brief overview of several new technologies in progress and how they integrate with one another will also be presented.
Speaker: Ed Voas
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it may have transcription errors.
Ladies and gentlemen, please welcome Mac OS X Evangelist, Xavier Legros. Hi, everybody. Good morning, should I say? Bonjour. I'd like to welcome you to session 204, "HI Toolbox and a Critical Overview." Well, actually... I'd like to welcome you to the HIToolbox day. If you're a Carbon developer, today it's really your day. And HIToolbox brought you on the platform, and for that, that was actually a great step forward. So now on Mac OS X, it's great. But you've seen only the tip of the iceberg. We showed you Carbon events, so you can use them in your application when you brought them, you know, on Mac OS X. We showed you Unicode support to, like, you know, the DrawTheme TextBox APIs, for instance, some new controls. But at the same time that you guys were putting your application, the toolbox has been through a lot of changes, and really a lot of changes.
A complete new architecture has been implemented, and until today, we haven't been able to show you, actually, the future of a toolbox. So in fact, today, we're going to be discussing a brand new toolbox. And for that, we'll start with this first section that's going to be talking about HI objects. And in here, this is very cool, because what's a HIObject? HIObject is truly-- all Toolbox objects now are going to be an HIObject. That means you as a developer are going to be able to subclass a control. That means it's going to make easier integration in your code, and it's going to be easier for us as well to create new features and new controls.
And Ed will be talking about this. After that, we have a session, I think at 10:00, that will discuss HIView. HIView is really, really cool. We've been waiting for it for a long time, and today we're going to be showing you what we've been doing. It's a one-pass drawing model. It's going to be able to use core graphics directly or Quick Draw for, like, you know, compatibility paths. The cool thing is that now, once again, each object is going to be its view as well. So you're going to do, like, your own clipping, what happens, you know, it's going to manage It's on drawing, so you'll be called if you want to use CG2, do your own drawing inside your view, and each object's gonna be a view. There are many advantages behind this. And of course, what will be the toolbox? We have new controls. And because of this new architecture that we have, we're gonna be able to, like, you know, add new controls to the toolbox easily, and for you as well, you're gonna be able to, like, improve your application and use all these new features in a brand-new manner. So today, really, it's the end of the toolbox, the way we know it. And to present you this new feature, who better than Monsieur Ed, the engineer formerly known as Ed Voas, to present this. Thank you.
Good morning. Okay. So, like Xavier said, you know, Carbon got you onto Mac OS X, but that's not it for Carbon. We have a lot more stuff we want to work on, a lot more stuff we want to do. And the purpose of this session is to kind of take you back from where we were and step you through the different Mac OS releases and tell you all the great things that we've been working on and how they fit in and why we are where we are today and where we're going. So in part of this, the second half of this session, we'll focus on our new foundation of the HI toolbox, which is based on HIObject. And we'll learn about why that's important.
OK, first let's get some terminology out of the way. What is HIToolbox? Well, basically, it is a user interface environment for Carbon. So we do everything with respect to appearance, events, controls, all on-screen widgets, everything. If you have a user interface, you are using the HIToolbox either directly or indirectly. We're also called the high-level toolbox. And I might use those phrases interchangeably, so they both mean the same thing. And what we're about is very simple. We want to make it as easy as possible to create a great Carbon application. We want to provide lots of free standard functionality while allowing you to customize it as you need to.
We're also trying to develop an object-oriented approach. Now, one could argue that, you know, even back in 1984, there was kind of an object-oriented approach. It wasn't driven through an object-oriented language or runtime, but they had controls, windows, and you had basically routines to operate on them. And now with HIObject, we're taking that a step further.
As I mentioned, it's easier to customize. And above all, we want to make it really fun and exciting to write a Carbon application for Mac OS X. Amen. Let's go through some history. First, Mac OS 8. This is kind of where we started. In that release, we introduced the Platinum appearance.
We also introduced a lot of new controls, things like progress bars, sliders, tab control. I think we had over 20 new controls in that release. We introduced the concept of keyboard focus, so you could actually have a visual focus for controls and be able to arbitrate between different controls using the focus APIs. We also introduced rudimentary control embedding, which is a precursor to HIView, which will be in the second session this morning.
And we introduced nice user features like live scrolling, which developers could take advantage of. But that wasn't good enough. We knew we had a lot more stuff we needed to do. And that takes us to Mac OS 8.5. In that, we had the full appearance manager with all of the different primitives that it was possible to draw. We actually took the high-level toolbox native, and this is a big deal. Prior to this, the toolbox was still written in 68K Assembler. We rewrote it in C, ported it, and this was actually a good thing because this was the basis for our Mac OS X toolbox. Very, very important move for us.
We also introduced lots of features that had been in third-party apps, but we actually pulled them into the toolbox, things like floating windows. We also introduced new concepts like the window proxy, something that kind of was seen in the Finder only prior to Mac OS 8.5. We opened it up for all developers. And we introduced interesting concepts like properties, extensible properties on all toolbox objects. So windows controls and menus all could have these properties that developers could attach things, and at the time we just called them refcons on steroids. But that even wasn't good enough. So that takes us to Mac OS 9. So we introduced things like control drag and drop, and well, that's about it. We didn't really do much else because we were kind of busy.
We were busy working on Mac OS X. And this is where the big change for us started. first big thing is we actually took the toolbox and ported it onto Mach and BSD. We also added data structure opacity, and this is a huge deal. While it might not seem like a big deal, it was huge to us, because now our data structures were inaccessible, and we could actually change the implementation under the hood without affecting your application. We introduced Carbon events. Again, this was a huge deal. We actually came out with a new event system that was much better than the previous WaitNextEvent event mechanism.
And it's a point of, uh, of, uh... Well, Carbon Events is basically like, you could consider it to be the entire nervous system of the toolbox. I mean, it is pervasive. We use it everywhere. It's an example of where we have one API to do many different things. And once you learn the one API, you're pretty much comfortable in different areas. Um, I want to talk about Aqua.
Through the appearance manager, we actually aided the move to Aqua. And in Mac OS X, we added the Aqua look. And we also enabled certain features that were aqua-specific, like sheets. We also added window buffering. On Mac OS X we kind of take this for granted. This is kind of a big deal because window buffering got rid of update event flicker. We don't have to worry about that stuff anymore. And we're able to do all types of cool quartz compositing because of this. You can have translucent windows and all types of cool stuff that we could only dream about before this.
And we introduced other developer features such as window groups. You could actually group windows together and actually move them as a physical unit, or you could actually change the layering of windows if our standard window layers, like floating document and modal layers, weren't good enough. But even that wasn't good enough.
Biggest thing we worked on in 10.1 was performance. That was the focus of Mac OS 10.1. And in that, we made dramatic improvements. If you were to do live resize speed in ListView in the Finder in 10.0 and in 10.1, you'd see an enormous difference. We did a lot of work there. We also added a couple of features.
such as services. So services is something that in 10.0 was only accessible to Cocoa clients. And 10.1 and forward is accessible to Carbon clients. And during the 10.1 time frame, we were making a lot of internal changes. And most of these changes were geared toward what we're going to be talking about today.
All the stuff that we've talked about so far still isn't where we want to be. There's still ways that we can refine ourselves and make the toolbox better. We aren't done. Just because you get onto 10 and that's it, we are not done. We have a lot more stuff to do. Talk about some of the things that are still kind of problematic even in the shipping Mac OS.
It's still a little hard to overwrite some default behaviors here and there. And it's kind of a pain to write custom controls. Plus on top of that, there's a different defproc model for each manager that we support. And a widget set that didn't completely match that of Cocoa's.
Well, we fixed all of this. In the Jaguar Toolbox, we have a lot of new features. And I think they're going to blow your mind. First is a new view architecture. We actually have a real view system now. It's called HIView, and that is the focus of the entire second session of today. We've added accessibility APIs, and these are important. And we're going to cover this a little bit in a second.
We also introduced new controls. We have the combo box. We have the toolbar control. We added drawers. And finally, we've actually unified all of our toolbox objects under this HIObject thing that I keep talking about. And the whole goal there is reduce and simplify. What we want to do is make one concept and then just use it everywhere. And once you learn the concept, it's easy to just go from there.
And this whole time, what we're trying to do is migrate from being a toolbox to a framework. So what we want to be is not just a collection of routines where maybe if I call them in the right order, something magical will happen, but rather something where we're just gonna run your app, and you just tap in, uh, where appropriate for your application, and do the interesting things that you do. And to facilitate this, we've added things like embedding, lots of new controls, Carbon events. Carbon events was critical to what you're going to hear about today. It is effectively our messaging system.
And of course the new View architecture. And we have more planned in the future. We'd love to do things like document model and other things. So what is this new HIToolbox going to bring to you? Let's talk first about the view system briefly. Again, HIView, composited drawing. We do one pass drawing in a composited fashion. CG or quartz drawing is the native drawing model. You can use Quickdraw, though. We use floating point coordinates, which means we're out of the sixteen-bit, uh, quick draw barrier.
It's not something that you want to go subpixel happy with, because the toolbox largely still deals in integer-based bounds and stuff like that. So if you started to do things in subpixels, we might do something interesting to some of the things you wanted to do. And it fully supports overlapping views. And as I mentioned, we have a whole session on this. I urge you to see it. Accessibility.
Accessibility is important because it allows application developers who provide assistive applications to actually peek inside your application and--applications and kind of see what's going on. For example, an application can peer inside your application and see all the windows and then see all the controls in there, and they can actually see what actions each of those controls can provide, and then they can tell the application to provide those actions. And it's in a safe and well-supported manner.
So you can do high level actions like press a button, select a menu item, things like that. And then the other side is that there are APIs for your application to allow these accessible, or these accessible solutions to get at the stuff in your application to make your application accessible, just like we're trying to make the toolbox accessible. This is really important, and we have multiple sessions on this this week, and I urge you to see them.
And then we have, as I mentioned, new controls. We have the combo box, something that's been missing from Mac OS for years. The toolbar, the same basic toolbar that you see in Cocoa. And drawers, like you would see in the mail.app. All right, so I'm my own demo boy today. So let's go over and take a look at some of this stuff.
Now, you should-- that's Kurt's computer. You should be aware that I've never given a demo in my life. I mean, I've done this for years now, and I've never done this. So be gentle. No pushups. All right, first thing you see on screen is a toolbar. Pretty cool, huh? All right, and it does all the toolbar stuff, right? You can hide it and you can show it. And if you hold down the command key, you can drag things around and they move.
And if you drag them off, they poof. We also have a couple of concepts such as items that can't be removed. So, for example, I have this thing which says it can't remove me. And no matter how hard I try, I can't remove it. I can move it around, but I just can't actually remove it. We also have a concept of anchored items. And anchored items allow you to say, this item should be over on the left and it is completely immovable. And that's what this one is. And no matter how hard I try to drag it out of there, I can't drag it out of there. All right. The next thing the toolbar supports is context menu. And from there, you can actually change the style. I can have icon and text. I can have icon. I can have text. But something new in Jaguar is you can actually say use small icons. And we use slightly smaller things. So you can actually have a small icon toolbar now.
Or you can have icon and text and all that fun stuff. One other interesting little thing that we've added in Jaguar is if you um, hold on the command key while clicking the toolbar button, you can actually step through all the different sizes automatically. Which is kind of neat.
Okay, one of the other tricks for the toolbar button is if you hold down command option and click it, you get the configuration sheet. Now you can also use the context menu to get this. And the configuration sheet is transparent and kind of neat, and it does all the things you'd expect.
For example, you can change the thing to text, text only, use small icons, just like you can from the context menu or from the toolbar button. And you can tell it to replace this with a default set and go back and everything's fine. You can also just add items. So you can drag things in, like I'm gonna drag this in, drag this in, and even this.
And that's all great. Then if I resize, I have the clipped items indicator, so the overflow indicator here, so I see all the things that I saw. If I choose customize, customize is a standard item that we just give you in the toolbox. And if I select it, it just works. The app is not doing any of this. This is completely under control of the toolbox. All this app does is bring up a window and attach the toolbar. That's it.
OK. So it's important to note that this toolbar has a lot of functionality. And it's all for free. As I mentioned, the app doesn't do anything to actually make this happen. Not one thing. The only thing it has to do is provide certain information for default items, allowable items, and things that should appear in the config sheet. All of these protocols and things are actually described in the new controls and services session, which is this afternoon.
So I have my little M logo here, and just by adding it to my toolbar I've gained 100 horsepower. But if I click it, I get a drawer. So check it out. And this drawer I can actually do fun stuff with. I can resize it, I can just close it again, open it back up. I could change its orientation. It could come out there. How about the bottom? Ooh, neat. Okay. That's drawers. Very easy. The API is extremely simple and very easy to use. Combo box. Yes. It's a real combo box.
but I bet we don't probably do a good job of this pop down list and it probably doesn't have a scroll bar. Oh, it does. Oh, look at that. Yeah, but it probably doesn't do type select or anything like that. Oh, it does. It also supports auto sort. Uh, you can have the disclosure of this thing, uh, be configurable so that it won't auto disclose or it will auto disclose. You can have the, you can define, uh, just how many items you want to show up in the combo box. It's highly configurable and it's very cool.
That's neat. Couple other things I wanted to show you are things that we've added as well, but kind of more minor things. two views that we've added. We have an image view and a scroll view. We actually have a scroll view. Do you understand? This is cool. All right.
So the first is an image view. And it's very simple. It draws an image. But it does it with quarts. And in this specific example we're just showing, uh, front of a car here. Um, but that's pretty much all it does. It just draws an image. But it has a specific property and that is that we've actually defined it and given it the scrollable protocol. And so we can actually embed it into a scroll view and it scrolls fine. And it does all types of neat stuff. We're actually gonna demonstrate the scroll view, uh, a little bit more in the HIV view session 'cause I have some specific things, uh, related to views that I wanna talk about. But it's a scroll view! Okay. All right. That's it for that. Let's go back to slides.
start talking about HIObject. This is your new best friend. This is the common base class from which everything we do derives. Yes. Before the Jaguar release, every toolbox manager had a different implementation for the item. So for example, Windows had its own little data structure and it was cute and all that stuff. The control manager had its thing and menu manager had its thing. Now they're all the same. They're all HI objects. And the old way, we had to do property APIs three times. We actually had three copies of the exact same code in three different places of the toolbox. So we can get rid of all that stuff now and unify it. We had different defproc models, now we have one. And we have different ways of doing retain release. Does anybody know what clone window does? Well it actually does a ref count, but it doesn't sound like it does, does it?
And Jaguar, we've unified all of this. We now have a common base class for all of our objects. This was something that was pretty much impossible to do before Mac OS X. And there's a couple of reasons why. One, we were still in 68K assembly, so it was kind of a pain. Two, there was no way for us to have a C++ class. And the reason for that is because if we wanted to have custom def procs, like custom controls, we couldn't have a C++ hierarchy because of one big reason. And that was that all of the C defs were in handles.
They were in these big handle blocks, and they would move around, and there's no way you can have a pointer-based C++ hierarchy that way. So it was really holding us back. The other thing was that you could get right at our data structures and poke at things, and we wouldn't know that you did it. Now that we've sealed things off, made our data structures opaque, and we're on a... on this new OS with all this nifty stuff and better memory management and we've gotten rid of handle-based def procs, we can really start to do some cool stuff. So we have one base class from which all things derive. We actually have polymorphic functions, which you can call them. I'll go through some of them later. And this is the birth of H-I object. What is it? Well, I've already said the first point about three times now.
But the most important thing is that you can subclass from HIObject. And one little nifty thing is that HIObjects are actually based on CF types, in a sense. This means that any HIObject can be put in any CF collection. You just put it in. You don't have to worry about special retain-release callbacks or any of that. You just pass the standard CF callbacks and, and then it just takes care of it. And it's pretty cool, because this means you can have a dictionary with all types of disparate types, and some of those types could actually be HI objects. Very cool.
HIObject is our object model. HIToolbox has an object model. It's called HIObject. So the way to think about this is that HIObject is the object. Well, HIObject and Carbon events are like objects and methods. It's exactly the same, and that's our approach. So to communicate between HI objects, you use Carbon events. And all HI objects are implicitly event targets. So from any HIObject, you can actually get its event target. This has some nifty applications, actually.
So what are the advantages to you? The first is consistency. We have one single API for doing certain things to HIObjects, which is really cool. And we do those in a polymorphic way. It becomes a lot easier to implement custom objects, because there's only one mechanism to do so. And it also means that you can actually derive from standard objects. It will be possible now to actually subclass standard system controls. Now, you wouldn't be able to do every single thing, because we don't actually expose every single method that we might have internally as a Carbon event. But where we do, that's where you can tap in. So you can do some really cool stuff.
The important thing is that all of these changes don't really impact your applications. Again, due to the opacity of our data structures now, we've basically completely gutted the toolbox and redone everything. And if you looked at the code now, compared to what it used to be, you wouldn't recognize it. It's that much better. And so all these changes will actually not affect you, unless you want to be affected by taking advantage of it. And of course you want to do that. And the nifty thing that I mentioned about the fact that HI objects have an event target means that you can actually, by creating an HIObject of your own design, you can now create your own event targets and then send events to that thing. So you can actually have an event target which represents your plug-in and then pass that around to other people. Very cool.
As I mentioned, toolbox objects are HI objects. This means that things like Control Ref, the toolbar, menus, and windows are actually typed after HIObject. It's questionable whether we'll really typedef them. We've started to. But that might cause weird little problems in your code, because you might have two methods in one class, and one takes a window and one takes a menu, otherwise they have the same signature. Well, because they're typedef now, they're the same thing. They actually have the exact same signature. So we're unsure whether we're gonna follow through on that or not. But effectively they are the same thing.
And here's a brief picture of a hierarchy that we have right now. As you can see, The HIObject is the base class, and from there we spring windows, views, menus, toolbar, the toolbar item, and we even have an HIObject for the application. And for views, we have our different views, like scroll bars, check boxes, push buttons, and so on.
So why would you use this? How would you use it? The first basic example is you want to create a custom toolbar item. Most toolbars are pretty boring if you just put a bunch of icons in. You probably want to do something interesting, like a search field or all of these other things. Well, you can do this by just creating custom toolbar items. And so you can actually subclass each toolbar item. I'm going to show you an example of that later.
You can write custom views in much of the same manner. And you can have custom views in your custom toolbar items. And then, of course, custom event targets, like I mentioned. Okay, so let's talk a little bit about what this stuff is, how it works, and how to use it.
So we're going to talk about how to register subclasses and how the construction process works. We'll talk about instance data that you would create and how they actually relate to each HIObject. Talk about dynamic casting between your instance data and an HIObject. And we'll cover the polymorphic functions.
To create an HIObject of your own design, the first thing you have to do is create your subclass. And you do this by calling hiObjectRegisterSubclass. Now, if you look at this API and you've done custom controls with CarbonEvents before, you're like, dude, that looks just like RegisterToolboxObject. And by and large it is, but there are subtle differences.
If you're doing any new work and you want to take advantage of HIObject though, you have to use this and not registerToolbox object. So what we do here is essentially you call this function, and you pass in your class ID. You also pass the ID of the class you wish to subclass. You can pass any options. Well, you really can't. You have to pass 0 right now, but maybe in the future. And then you pass in an event handler and a number of events similar to the way you would install an event handler using installEventHandler.
And what you get back from all of this is a class ref, which you can actually use later to unregister your class. Typically you would never unregister your class. The only reason you would is you're a plugin and you need to bring a class into existence. So you would register your class, create your instances, and then later on you destroy all your instances and unregister your class before your plugin exits.
Once you've done that, you can create HI objects. And the way you do that is HIObject create. We're very original with our API names. And all you do is you just pass in the class ID, and the way you get parameters into your construction, so to speak, is you pass in an actual event, an initialized event, which gets sent to your object at a specific point during construction. And we'll talk about that in a little more detail in a little bit. And what you get out of this is an HIObject ref. And this is why we need to talk about instance data versus HIObject ref in a little bit, because no matter what types you're actually creating in there, you always get an HIObject ref. It's similar to some kind of virtual constructor concept where you might construct some object of some derived class, but you still get back a pointer to the base class.
So here's just a brief example of subclassing a toolbar item. We'll show an explicit example a little bit later. So all we're doing here is we have some custom ID, and we're just using Java namespacing to define the ID here. And we just pass in as the first parameter. and we're subclassing the toolbar item. So we pass in KHIToolbarItemClassID. We have no options. We specify our class handler, the events that we want to handle, and we get back our class ref.
Now we're gonna talk about how all those things work and how the event handlers work in a little bit. And we're gonna start to get into it here at the very beginning. HA object construction. How do we create these things? Well, like C++, we create things bottom up. So we always create superclass before we create subclass. And we do it two phase. We do a construct phase and then an initialize phase.
And the three most important events to, uh, to deal with are k-event, h-i-object, construct, initialize, and destruct. And in fact, construct and destruct are required. If you attempt to register a class that does not specify these in its event type list that you pass to it, we will laugh at you and give you an error.
So let's just give you a little bit of a diagram of that. Here we have class bar, which derives from class foo, which derives from HIObject. What happens is, first we call HIObject to construct. And it does. Next, we end up calling foo to construct. And it does. And finally, bar. And now we're, we've kind of done all the construction. And the object kind of looks like this now. But that's not a realistic picture. In reality, the picture looks more like this. Essentially, each subclass will wrap the previous class by holding onto a reference to it so that it can obviously call base class functionality.
So the construct event is actually kind of magical in a way. It doesn't get sent to your event target because you don't have an event target yet. We haven't actually constructed the item yet. So the way we do this is we actually call your event handler directly, which means we're going to pass null for the first parameter, which is this event handler call ref magical thingy which you can use to call through to next handlers. You know, because we're passing null for that, that means you can't call through to next handlers. So it's important to just ignore all that and just deal with the, the problem at hand, which is constructing your object. When you register to your subclass, you can pass user data. That user data gets passed into your construct event. And actually, that is the last time you will ever see that data. And we'll go into why in a second.
In response to the construct event, all you do is you create your object, whatever that takes, malloc a block of memory, new some object, it doesn't matter. And then what you do is you actually just take that instance and you stuff it into the initialize, I mean, into the construct Carbon event, and we will get that back, and then we're gonna use it.
The instance data that you return can be anything you want, as I mentioned. It doesn't matter. But you have to make it type void pointer, because we don't know what it is. And we use this information in two ways. First, we actually store it off with the class ID so we can do dynamic casting later, and we'll show you how to do that in a second.
The next part is the more interesting part, where we actually take that instance that you returned to us and we use that as your user data in your event handler. So that means that once your object is constructed, you will receive your instance in the user data always. That's why I said that the class, um, that the construct handle will actually get the user data specified when you registered only the first time. And so you can use that for some neat little stuff.
OK. Just want to kind of clarify the relationship between your instance data and HIObject rev. Basically, an HIObjectRef is not your instance data. It is our base class object. So it's kind of unlike C++ in which case, you know, the disk pointer is kind of the same throughout the, throughout all of the stack there of superclass, subclass. So you need to be able to get your data back. So I have this HIObject graph, I mean, that's all HIObject create gave me. How do I get my data back out of that? And the answer is dynamic casting. And we have an API to do that called HIObject dynamic cast. Again, we're very original. So here's an example of it. So we have a routine and it receives an HIObject graph. inside here, um, basically all we do is we just pass in, we call HIObject dynamic cast, we pass in the class ID, which would be our class ID, and passing in the HIObject graph, and we get out a pointer. And this pointer could be, uh, whatever. Now, technically I should have casted it to the object type here, uh, but it's merely for example. So if the pointer comes back null, then it wasn't a valid cast, meaning that this object is not what you thought it was, and you would choose to do nothing else. We know it's one of our objects, and now we have our instance data, and we can go off and do something cool. Thank you. Okay, let's go into exactly how you handle the construct event. Again, everything's done through Carbon events. Like I said, it's the nervous system of what we do. It permeates everything. So here's our Carbon event handler, and here we are handling the K event HIObject construct event.
Here's a close up. So inside here, first thing we do is we extract the HIObject instance parameter from the event that is incoming. This gives you the actual HIObject graph. And what you would typically do is you would take that HIObject graph and pass it to your construction for your particular part of the object that you want to create. And you would store that off.
So in this case, we're calling construct my object, passing in that object ref, and we get out the object that you created. And on the flip side, once we call that, we set the event parameter back in, setting it to type void pointer. Currently, we're actually reusing the same parameter. It's questionable whether that will change or not.
Okay, so your object's been constructed, but you're not really done. However, after you've been constructed, this is when we actually install your event handler onto the event target. along with your instance data as the user data. So at this point, your object now kind of has a Vtable installed. But there's one more phase we need to do, and that's initialization.
So after we've successfully constructed your subclass, the object is sent an initialization event in a standard Carbon event manner. We just get your event target and send the initialization event. And this initialization event is the initialization event that you passed in to HIObjectCreate. And this is how you get your parameters into your object creation.
It's very important that you call next event handler first. You have to initialize your superclass before you can proceed with initialization. It's slightly unfortunate that you always have to do this. But on the other hand, it's kind of cool, because what this means is that you can actually intercept and augment the parameters that are meant, that are destined for your super class and change them in ways kind of analogous to the colon operator when, in a constructor in C++. How you can have your constructor colon and then pass parameters, you can actually change the parameters to your super class. That's pretty cool. And it's important to make sure that once you've called callNextEventHandler, you check to make sure that the error was no error, because otherwise, you're in for a lot of hurt.
All right, so here's an example of the code necessary to initialize. Very simple. Now, the user data has already been set up to be your instance pointer, so you just cast it out of that, and you get your object back. And now you simply call nextEventHandler. If that went fine, now we just-- in this case, we have a C++ object that we created, and we just call the initialize method on it, passing the event. And in there, you would extract all the parameters.
OK, that's it. Now you have an object that can play in the world of hiObject, just like any other object. This is very cool. And there are a lot of uses that you can put to this. One interesting little thing we can talk about is debugging support. Any object on the system supports this protocol here.
which is the k-event-hi-object-print-debug-info protocol. It's a Carbon event that gets sent to the handler when hi-object-print-debug-info, the API, is called. This is pretty cool, which means it doesn't matter if you have a window, a toolbar, it doesn't matter. As long as it's an hi-object ref, you can call hi-object-print-debug-info, and out in your terminal, and standard out wherever it's directed, you will actually get a full debugging output. You don't have to remember, oh, was it gdb-show-control-info, or debug-print-window, or I don't know, I don't remember. You don't have to remember. There's only one thing to remember now. HIObject, printDebugInfo, you're done. This is part of the beauty of this. And so you can tap into this as well. So when you subclass, you can just actually add your information to the output as well. So it's kind of cool.
We also have some basic polymorphic functions, like you can get the class ID from an object. You can compare it to something to make sure it's a known class ID if you need to do that. You can see if an object is derived from another class, cool stuff like that. And of course, the most important one of all, HIObjectGetEventTarget. So you can actually get the event target from the object. And I should note, by the way, that eventually, we're hoping to merge event target and HIObject so that they are one and the same. So if you actually call hiObject getEventTarget in the future, you'll actually get the hiObject ref that you passed in back.
So I mentioned that these things are CF types, and so these questions have a simple answer. It's not a question of how to put your HIObject into a collection or how to retain and release them. You just use core foundation. You'll notice in the new stuff that we have, we don't have, like, HI toolbar release, HI view release. You just call CF release on it, and you're done. You don't have to remember new APIs. Very easy. And like I said, you can just intermix all of this stuff into dictionaries, arrays, and all types of neat stuff.
Eventually, there comes a time in any NHI object's life when it's time to die. And when that happens, and your ref count goes down to 0, it's time to go. And we destroy things top down, just like C++. And this is convenient for us, because that's our natural event flow. So we just send you an actual destruct event, and that's where you handle the destruction. And I wouldn't call through, because then you'd be destroying your superclasses before they're probably ready to be.
And, again, simple code snippet. We're handling k event h i object destruct. We get our object from our user data, and we just call delete on it, because it happens to be a C++ object. Very easy. Now I'm going to take you through a painstaking demo, which is just mostly a code walkthrough of how to subclass a toolbar item and the cool stuff you can do.
just launch this, which I should have already had launched. Okay. What we're gonna do is we're going to extend the Carbon toolbar that we looked at earlier. And we're going to do that by adding a custom toolbar item. So we're going to see an example of how to do this. Now this isn't a simple example. This example actually works and does neat stuff. So it might be a little hard to follow at times, but bear with me. So we have this custom toolbar item that we're running. that we want to make. First thing we do is to find the set of events that we want to handle for the toolbar. You'll notice I'm handling HIObject construct, initialize, and destruct.
Can everybody read this, by the way? Is that fine? Okay. And so that's the HIObject protocol that we're handling. As far as the toolbar protocol, we handle two events: get persistent data and perform action. And we'll talk about exactly what those mean in a little bit. I'm not going to go too heavy into what the toolbar stuff is all about, because we kind of talk about that in the new controls and services talk later. But I'll kind of give you a rough feel for how things are. The other thing is we have our custom toolbar class ID, and it's just a CFString.
And our toolbar object is really simple. All it does is it holds on to the toolbar item ref, just in case it happens to need it. and we have a URL in it. And the whole purpose of our custom toolbar item is you'll be able to drag a URL into the toolbar and then click it and have it go to that URL.
So we have a whole bunch of functions here, and that's great. The first one is registerToolbarItemClass. This function... kind of a one shot function. And here's what we call HIObjectRegisterSubclass. As you can see, I'm registering my custom toolbar item class ID, and it's a subclass of toolbar item class, of toolbar item. And I'm passing in my custom toolbar item handler, my event handler, and my events that I want. Now I'm passing zero in for the user data here, but you could pass something more interesting, like maybe a, a construct method, a static construct method for for C++ subclass.
Below that, we actually have our public API for actually creating one of these things. Now, first thing we do in here is we call registerToolbarItemClass. And this ensures that whenever this API is called, our toolbar item class is automatically created for us. There are other ways to handle this. You can do this at app initialization or things like that, especially if you were going to create one of these things from a nib. One thing I should note is that we've had for some time in IB the concept where you can drag in a custom view and then give it a class ID, and then that would be created automatically for you when the nib is brought into existence. That stuff works just the same with HIObject. So that's an example where you might not call this API, but instead you need to know that the class ID has been registered ahead of time so that IB can go ahead and create it. So, the next step is to create an initialize event.
It's a slight pain, but it's worth it. In this case, we're just creating it. It has to be event class hiObject and event hiObject initialize. If you attempt to call hiObject create with an event that is not that, again, we will laugh at you and give you an error. Now we come to a concept of required parameters for superclasses. Any toolbar item has two required parameters on construction-- the identifier, and the options.
And here's where we set them. The identifier we're just passing from our input parameter. The options we're specifying. So in this case, we want to allow duplicates. And that's because we want to make sure that we can drag multiple of these things in there. We might have multiple URLs we want to drag in. That's similar to the concept of the toolbar in the finder. So the finder has this ability to drag in file objects. Now, technically, they're all the same type, but they all represent something different. In that case, you want to allow duplicates. By default, the toolbar items don't allow duplicates. And it checks duplicates based on, typically, its identifier. I should mention that. Or it actually-- there's an HIObject is equal call that will be called to test equality further than that if you need that.
So that's our standard parameters. We have to add that. And here's our parameters. Now we got this URL in and we just want to set it in. And we set it in as the keventparam toolbar item config data parameter. And we kind of do that for a reason.
We want construction to be very similar to when we construct from a configuration file in the toolbar. And it just so happens that when that happens, that's the parameter name that gets passed to us. So we're just going to reuse that for our regular construction so that we can have one funnel and one way to create things and make things easy on us.
So that's how we get our URL in here. And the URL can actually come in as a string or an actual CFURL for this example. And finally, we create an instance of our class, called, you know, of our custom toolbar item class. And we get back an HA object graph. Now let's look at some of the simple functions in here. So when we get called to construct, actually let me zip down to the actual event handler. That'll make things a little easier.
Here's our event handler. And the first event we handle is kEventHiObjectConstruct. As I showed in the code snippet, we actually extract the HiObjectInstance parameter out of this thing, and we use that to pass into our construct customToolbarItem. And I actually won't even go into construct customToolbarItem. All it does is malloc a block of memory and set some fields up. Very simple. And we get back our item out of that, and then we actually reset it into the event using the same parameter as type void pointer. And as you can see, I haven't added the constant to the header.
After it's constructed, we want to initialize our object. So we actually look for a k event, h i object initialized. And this is where we will actually receive the parameters that we set in that initialize event in our other function. So let me go to the initialize routine.
This is our initialize routine. So we've gotten this event, and we call this. And before we actually call this, we've called our superclass via call next event handler. So we know that HIToolbar item is now created. And actually here's an example in here. You can see that we're actually going to modify or talk to the actual HIToolbar item. We're actually gonna call HIToolbarItem APIs in here, which is the reason we need to hold on to the HIToolbarItem ref that is passed to us in our construct handler. So the first thing we do is we just look for our config data parameter. So we get that.
And that's all fine. If we got it, then what we do is we try to determine what type it is. If it happens to be a string type, we create a URL from that. If it happens to already be a URL type, We just retain that. And in this code I'm making a wild assumption. And if the parameter isn't there, we make a reasonable default and we set our URL to apple.com.
The next thing we do is we actually set the label of the toolbar item to just this generic URL item. Ideally, I'd set it to URL, but URLs can be arbitrarily long, so for this example I'm being a little bland. I'm sorry. We set our icon. of the toolbar item to just be the generic URL item. And then we set the help text to be the URL, so we can demonstrate some little help tags in the toolbar.
So after that, our item is initialized. And it just exists in the toolbar, and everything is glorious and happy. And then somebody comes along and clicks on it. And when they click on it, This event is sent out. K event toolbar item perform action. In this case, if we have a URL, we just call ls open CFURL.
Here's an interesting little event that we send out to the toolbar. If you're doing a custom toolbar item, you might have custom data. In this case, our custom data is the URL itself. So, whenever the toolbar writes its configuration out, It actually asks you for any extra data that you might have that it might want to write out into this XML blob that it's about to do. The toolbar can be configurable or non-configurable, and it can autosave the configuration for you in your application's prefs. If you have it set to autosave, then you will get this event. In this case, what we want to do is just call our own function to create some persistent data. Inside that function, all we do is we just extract the string of the URL and we pass it back. Then we just set it into the parameter here. We pass it back as the config data.
You'll notice the same parameter name that was incoming in the initialize event. And then when the XML stream gets written, that string will get written into it as well. And this way, when the toolbar comes into existence again from that configuration, we will automatically be passed this config data. Then we can get our URL back. Okay.
So that's basically the custom item. And then in our main here, There are a couple places where we actually reference this. We actually use this my custom identifier string. Typically it would be Java name spacing, but I'm just being lazy here. Couldn't come up with anything interesting. When the toolbar goes to configure itself or needs to create anything by identifier, it will ask this concept of a toolbar delegate. You'll learn more about that in new controls and services. But suffice it to say, when we're asked to create an object of my custom identifier, what we do is we turn around and we create one of our custom items. We pass in the config data that's actually passed to this function. That's where we hook in that part.
Then the other thing is we're going to allow this to happen from a drag. You're going to be able to drag text into the toolbar and we're going to create one of these items out of that. We had to actually add some code here to go through, see if we had any text flavors. If we do, just extract the text, create a URL out of it, and create a custom toolbar item with that. That's that.
Okay. So here's our toolbar again. And I'd love to know where the doc is. Oh, there it is. All right. So I've actually built this already. And all we're gonna do now, we're just gonna pick a couple of URLs. You'll notice there's a little bit of a delay, and then you can add it in. And my toolbar item is created just like that. Now, drag another one in.
There it is. And then, if I hover over it, you can see it will actually display the URL that I want to go to. And I click it, and bang, I'm there. Very easy. Go here, and I can launch Sampler. Simple. And that's really the power here, is that you can do anything you want with this stuff, and you can create all types of custom stuff. From the toolbar item, you can actually have a custom toolbar view. So the toolbar item is actually called whenever we want to create the view for the items, you can actually create custom toolbar views. And these are all done via HI objects. This is the basic knowledge you need to possess in order to stick with us. Yes? all of this fun stuff we're going to be doing with HIObject, hi view, and all of that fun stuff. So that's my little toolbar item demo.
So in summary, all of this stuff that we've been showing you today, we've been working on for years. And I mean, it's been slow coming, but... You know, now that we're on 10, and now that we have all of the power beneath us that we need, we can do some amazing things. And we're starting to do these things now. I mean, HIObject, once you start to use it, you're going to realize, oh, my God. Thank you. This is easy. And then when you start to use HFU, you're gonna go, man, how did I ever use that control manager? I can't even comprehend it. And we'll demonstrate that in the next session. And trust me, you're gonna dig it.
Now, HIObject is our future. We've always talked about Carbon events, Carbon events, Carbon events. HIObject is what Carbon events plug into. This is the receptor for Carbon events. This is, this completes our object model. This is gonna be really cool. So what we need you guys to do is to at least look at the headers and give us your thoughts. So we have three headers of interest on the seed that you receive. We toolbar, and HIView. And if you would, just check them out, see what's there, see what's not there, and give us feedback. And the person to bother with that feedback would be Xavier. Thank you.
We'll just take you through the road map quickly. After or before this. All right, I want you to all get in your time portal and go backwards in time to yesterday. Because if you don't know about how to migrate to Carbon events, you should, especially if you're here. So you can go back and review the DVD or whatever if you need any knowledge about how to migrate your app to using Carbon events. Following this session, in this room, we have HIView.
Later on this afternoon, we talk about new controls and services, and then we have a special session on how to improve your performance with the Carbon events. There's actually other tips and tricks in there, too. It's not fully about Carbon events. We actually have some other ways of getting some performance out of your app. We actually have preliminary documentation on this stuff that we're going to be talking about today on the developer website, I believe. HIVUI, HIObject, and HIToolbar, they're all preliminary right now. There'll be more to come. Of course, there's always learning Carbon and all that fun stuff.