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 has known transcription errors. We are working on an improved version.
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, HIToolbox: An Architectural Overview. Well, actually... I'd like to welcome you to the HIToolbox Day. If you're a Carbon developer, today is 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, like, you know, the Draw Theme Text Box APIs, for instance.
Some new controls. But at the same time that you guys were porting 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 HI object? A HI object is truly all toolbox objects now are going to be an HI object. 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'll 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, and it's going to manage its own drawing, so you'll be called-- if you want to use CG2-- do your own drawing inside your view, and each object's going to 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 going to be able to, like, you know, add new controls to the toolbox easily, and for you as well, you're going to 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 HI Object. And we'll learn about why that's important.
Okay, 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.
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 introduce nice user features like live scrolling, which developers can 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. It's a 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 to these 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 Wait Next 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 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. In 10.1 and forward, it's accessible to Carbon clients. And during the 10.1 timeframe, 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 on to 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 Coco'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 HI object 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 going to run your app and you just tap in 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 Quick Draw though. We use floating point coordinates, which means we're out of the sixteen bit quick draw barrier.
is 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 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 buttons, select the 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 ComboBox, 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, uh... That's Kurt's computer. Uh, 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. It's 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. Um, we also have a couple of concepts, um, such as, uh, items that can't be removed. So, for example, I have this thing which just says can't remove me. And no matter how hard I, 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.
[Transcript missing]
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 going to 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. Okay. So it's important to note that this toolbar has a lot of functionality and it's all for free. You know, 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 like 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. In 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. ComboBox. Yes. It's a real ComboBox.
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. A couple other things I wanted to show you are things that we've added as well, but kind of more minor things. 2 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 quartz. Um, 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 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.
Let's start talking about HIObject. This is your new best friend. This is the common base class from which everything we do derives. 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, you know, 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 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 defprocs, 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 defprocs, 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 HIObject. 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 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 CarbonEvents are like objects and methods. It's exactly the same, and that's the way, that's our approach. So, to communicate between HIObjects, you use CarbonEvents. And all HI objects are implicitly event targets. So from any HI object, 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 certain, 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, you know, 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 it's, 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. The nifty thing that I mentioned about the fact that HI objects have an event target means that you can actually, by creating an HI object 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 plugin 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 HI object. 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 our hierarchy that we have right now. As you can see, 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. I mean, 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 gonna 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 iObject. Talk about dynamic casting between your instance data and an h iObject. And we'll cover the polymorphic functions.
[Transcript missing]
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'll 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 zero 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 HI object 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 HI object ref.
And this is why we need to talk about instance data versus HI object ref in a little bit, because no matter what types you're actually creating in there, you always get an HI object 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 like 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 going to talk about how all those things work and how the event handlers work in a little bit. And we're going to 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 deal with are k-event-hi-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 hi object. 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. And 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 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 going to 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,
[Transcript missing]
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 all of the stack there of superclass, subclass. So you need to be able to get your data back. So I have this HIObjectGraph. I mean, that's all HIObjectCreate 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 HIObjectDynamicCast. Again, we're very original.
So here's an example of it. So we have a routine, and it receives an HIObjectGraph. Inside here, basically all we do is we just pass in, we call HIObjectDynamicCast, we pass in the class ID, which would be our class ID, and passing in the HIObjectGraph, and we get out a pointer. And this pointer could be whatever. Now, technically I should have casted it to the object type here, 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.
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 HI object 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 constructMyObject, 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 callNextEventHandler 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 kinda cool, because what this means is that you can actually intercept and augment the parameters that are meant, that are destined for your superclass 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 superclass. And 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.
Okay, 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 kevent hio object print debug info protocol. It's a Carbon event that gets sent to the handler when hio object print debug info, the API, is called. And this is pretty cool, which means it doesn't matter if you have a window, a tool bar, it doesn't matter.
As long as it's an hio object ref, you can call hio object print debug info, and out in your terminal and standard out wherever it's directed, you'll 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. hio object print debug info. You're done. This is part of the beauty of this.
[Transcript missing]
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, hiobject.getEventTarget. 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. You know, it's not a question of how to put your HI object into a collection or how to retain or 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 That's the neat stuff.
Eventually, there comes a time in an H.I. object's life when it's time to die. And when that happens, and your ref count goes down to zero, 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 kevent hiobject destruct. We get our object from our user data, and we just call delete on it, because it happens to be a C++ object. All right. Now I'm going to take you through a painstaking demo, which is just mostly of a code walkthrough of how to subclass a toolbar item and the cool stuff you can do. All right.
Let's just launch this, which I should have already had launched. Okay. What we're going to do is we're going to extend the Carbon toolbar that we looked at earlier. We're going to do that by adding a custom toolbar item. We're going to see an example of how to do this. This isn't a simple example. This example actually works and does neat stuff.
It might be a little hard to follow at times, but bear with me. We have this custom toolbar item that we're running. 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 HIAbject construct, initialize, and destruct.
Can everybody read this, by the way? Is that fine? 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. 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. First one is RegisterToolbarItemClass. This function... It's 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 construct method, a static construct method for a 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. 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 initialized event.
It's a slight pain, but it's worth it. In this case, we're just creating it. It has to be an event class HIObject and event HIObject initialized. 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. Thank you.
Here's where we set them. The identifier we're just passing from our input parameter. The options we're specifying. In this case, we want to allow duplicates. 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. The finder has this ability to drag in file objects.
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 hi object 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 kevent hiobject construct. As I showed in the code snippet, we actually extract the hiobject instance parameter out of this thing, and we use that to pass into our construct custom toolbar item. And I actually won't even go into construct custom toolbar item.
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 a constant to the header. Anyways.
After it's constructed, we want to initialize our object. So we actually look for a kEvent, hIObject, initialize. 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. We've gotten this event and we call this. Before we actually called this, we've called our superclass via call next event handler. We know that HIToolbarItem 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 going to call HIToolbarItem APIs in here, which is the reason we need to hold onto 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 to the URL item, and we set 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's going to be a 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 auto-save the configuration for you in your application's prefs. And if you have it set to auto-save, 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. And 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 past this config data. And 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 namespacing, 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. And we pass in the config data that's actually passed to this function. So that's where we hook in that part.
And then the other thing is we're going to allow this to happen from a drag. So 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. So we had to actually add some code here to go through and see if we had any text flavors. And if we do, just extract the text, create a URL out of it, and create a custom toolbar item with that. So that's that.
Okay, so here's our toolbar again. And I'd love to know where the doc is. Okay, 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.
[Transcript missing]
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. will be presented through all of this fun stuff we're going to be doing with HiObject, Hiview, and all of that fun stuff. So that's my little toolbar item demo.
So in summary, all of the stuff that we've been showing you today, we've been working on for years. And I mean, it's been slow coming, but we're 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. 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, I, 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 have HIObject, HIObject, and HIObject. So we're gonna go ahead and look at the HIObject toolbar and HIOview. And if you would, just check 'em 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.
[Transcript missing]
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. And 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. HivuhiObject 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.