Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2003-425
$eventId
ID of event: wwdc2003
$eventContentId
ID of session without event part: 425
$eventShortId
Shortened ID of event: wwdc03
$year
Year of session: 2003
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC03 • Session 425

Carbon: HIView in Depth

Application Frameworks • 1:06:13

Learn the architecture of Carbon's modern, full-featured, efficient, composited, object-oriented view system. Learn how to adopt HIView using both exhaustive and incremental approaches to improve the performance, functionality, and simplicity of your applications. Find out how to use Aqua's metal tool, leverage Quartz drawing, implement custom views, and enhance the standard views through HIObject subclassing.

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.

That's me. HIToolbox, well, HIView is probably the most important technology in the HIToolbox these days. If you're going to deploy an application on Mac OS X that is using HIToolbox, you need to know about HIView. Over the past few years, we've been laying this foundation. Carbon Events was like the first big milestone, and we had this nice messaging system. On top of that now, we have HIView. And in Panther, we finished that transition, so now that HIViews are pervasive.

For this reason, everything we do going forward is going to involve HIView in some form or another. So it's important that you learn about it. If you're not ready to adopt it right this second, that's okay, but please learn about it and understand it and try to figure out how it's going to work, how you're going to end up adopting it into your applications in the future.

What this session is going to be is a quick overview of HIView. We're just going to fly through some of the basics. We're going to learn how to use it, how you turn it on, what makes it go. We'll also talk about how to write a custom view. What does it take? And it turns out it's pretty easy. And along the way, we'll touch on performance things, ways you can make your views perform really well in the HIView model. So HIView is a complete replacement for the control manager.

Technically, though, we always throw around these terms, HIView, controls, what's the relation? HIViews are controls, controls are HIViews by and large. HIView is kind of a subset of the old control manager, and it allows controls to work in this new mode, which we call composited mode. And it is, quite honestly, the best thing to hit the toolbox since, I don't know, OS types, I don't know, making stuff up.

So, it's important that you really understand the relation between the two. So, if you were to look in HIView.h, you'd see that, you know, not everything that was in the control manager is in there. Well, that's because we, you know, we haven't finished moving everything over there yet. But it is perfectly legal to call control manager calls on HIViews. There are some exceptions, and we're attempting to document those in the header in the controls.h right now.

Also understand, as I mentioned, we're now pervasive with these views. We use them everywhere. So this is great for you guys because you can learn one system, one way to write a view, and you can deploy it in menus, windows, and as a control, obviously. This is a modern view system. It uses all the latest technology. Basically, we draw using Quartz, use floating-point coordinates, we support overlapping views, which is really cool, and as a Panther, we support layout facilities, which we kind of touched on in the HI Toolbox session.

So first let's talk about composited drawing. What does that mean? What it means is you have a predictable way to draw. That doesn't involve erasing behind oneself. The old control manager would always erase behind itself before it redrew the control. This has happened back since 1984. It's always been that way. Couldn't exactly change it until now. Now we have a composited drawing system. We draw everything back to front, and we also obey all of the Z-ordering among siblings, which gives us the ability to overlap views, as I mentioned.

Since we don't do a race behind, you don't have to worry about pattern alignment problems, all that stuff. It's just gone. You don't have to worry about it. And drawing always occurs at a very specific time, and that's at event loop time. So right before we would go and flush your Windows contents, we actually would repaint all invalid areas of the view system. So speaking of invalidation, what is it?

So essentially, as your views decide that they need to be redrawn, they will mark themselves invalid. The toolbox will only mark things invalid if you move a view, if you resize a view, or if you show a view. Other than that, it's all up to you. For resizing, we do have a special event that we send out.

K event, K control, it must be K event, control invalidate for size change. This event will, as I mentioned, will get sent out during a resize operation. So you might have a control that knows that, well, my top left area always kind of stays constant. So if I'm growing, I only need to paint like that inverse L area that's just been revealed. And conversely, as the thing is shrinking, you might decide you don't need to redraw at all. But it's really important to know that you have to invalidate. It's your views. It's totally up to your view to invalidate when it's appropriate.

So, and this means things even like value change, activate, deactivate, highlight. We don't know if that means anything to your control, so it's up to you to tell us by invalidating. An important note is that invalidating a parent does not invalidate the children. This may seem strange, but it's that way for performance reasons. And I won't go into the details. I can bore you with that some other time. But it's really important to keep that in mind while you're using this.

The two APIs that you use are HIView Set Needs Display and HIView Set Needs Display in Region. And the second call is to be a lot more specific about what you want to get redrawn. If you have a very complex control, this call will be your friend because essentially you just want to invalidate some small part of your complex control.

So in the realm of geometry, we have new types-- HIPoint, HIRECT, HISize. And these are essentially just typedefs of the CG types. But we kind of renamed them to kind of fit into our namespace and also to imply something else, which I'll get into in a little bit. All of the APIs that we have are in terms of these new types. The only legacy quick draw type that you'll ever see in our API are region handles right now. And eventually, those will be replaced by H-I shape refs.

There are two coordinate systems for every view: frame and bounds. The frame is where you live inside your parent, expressing your parent's local coordinate system. Your bounds is your local coordinate system. Whenever you're moving or resizing your view, it's always done in frame coordinates. Whenever you're doing stuff internal to your view, hit testing, drawing, it's always in your bounds coordinates. And it's just important to always keep that in mind.

The great thing about having a local coordinate system is obviously all of the rectangles are local to you. If you get moved or resized or any of that stuff, you can pretty much not have to recompute all these rectangles all the time. So it allows you to cache things. It's pretty cool.

So, a quick visual example: you have a button inside a group box. Its bounds are 0, 0 to 120. This is very different from the old control manager way, where it always lived in port coordinates, which was not always fun. So now you have your local coordinate system. It's great. Its frame is where it lives in its parent, as I mentioned. So in this case, it might be 30 pixels down and to the right from the top left of the group box.

Okay. There are two ways to draw in HIView: Quartz and Quickdraw. Quartz is obviously the native drawing model for Mac OS X. Have we said that enough? It's got all types of cool--I think it's one of the coolest features of Mac OS X myself. It's got all types of cool things: transparency, anti-aliasing, Bayesia curves, really cool stuff. And all of our drawing internally is actually done through Quartz. We also fully support Quickdraw, though. If you have your code, it's probably likely all in Quickdraw code. You can migrate that very easily. That's one issue though. Quartz has a lower left origin.

We don't. We use top left origin. And this is the other reason for having a redefined type space. So when you see an HI point or an HI rect, you know that this is in terms of top left coordinate system. So it's a signal to you to know which kind of orientation we're in.

So, ultimately, we've transformed the context that we deal with and that we give to you whenever you draw. We put zero, zero at the top left where it rightfully belongs and where you're really used to. We have an old joke. I won't go into it. I won't make friends.

So this is important because our windowing system has always been top left based. And of course all your existing code is top left based, and we don't want to throw that away or have you flip rectangles for the rest of your life. We just want you to take your code and be able to migrate it easily. It also saves us from things like window resize. If we had a bottom left origin when you resize the window, essentially all your frames are changing throughout the window, and that's kind of a big pain in the neck. So we wanted to avoid all that.

But you've got to realize that you're basically drawing upside down. And if you get a context from us, and you call-- CGContext DrawImage, you'll find it out quite apparently. So to help you with that, we have helper APIs to deal with it. First is HIView DrawCGImage. That will take the image and render it in the appropriate way. And we also have HITHemeDrawText. This is the Panther version of DrawThemeTextBox, which also will draw in the correct orientation.

Only H-I-Theme draw text. It's much faster, so use it. So essentially these APIs just take the context, flip it, draw, and flip it back. And flipping it is only a matter of a couple lines of code. I don't have an example of showing you that, but it's really easy to do.

The next issue, or the next solution, really, is that of layout. So one of the big pains we've always had is being able to layout all of your controls, and you always have to do that manually. You have to listen to balance changes, events, and move everything around, and it's a lot of code. We wanted to eliminate that, okay?

So, we have layout facilities. I'll kind of show this in the HI Toolbox session. So what we have here is an automatic way to position or size any of your views in your window based on any other view in your window. They do not have to be next to it. It doesn't have to just be your parent. It could be any view in the window. Theoretically, we were talking earlier, theoretically you could base this across windows. It'd be crazy, but hey, have fun. So there are multiple ways to do this. You have bindings, positions, and scalings.

We'll touch on those as we go forward a little bit. So, as I mentioned, you can do it with any other view. And the way you actually go about this, the mechanics are a couple of APIs: HIView get layout info and HIView set layout info. So typically the code flow would be get the layout info that's there, you know, twiddle your bits, and reset it.

But just setting a layout doesn't actually make the layout reflow. If you want that to happen immediately, use HIView ApplyLayout. What we would do otherwise is just wait for a balance change event to happen on the original view that you're relating to, and then we would make everything go online. This is kind of analogous to if you selected a whole bunch of objects in some drawing program and then said, you know, "Align left," and everything would align left. Well, the selection is like creating the bindings, and then saying "Align left" is like calling ApplyLayout.

So a little code to show how to do this. It's pretty simple. Again, you just get the layout info. Set some settings, and then you set it. In this case, what we're doing is we're setting bindings. So bindings are kind of like the power plant bindings, where you essentially bind an edge of your view to the edge of some other view.

In this case, we're binding the left and the right of our view to our parent. And you can indicate parent by just passing null in the to view field of the structure. And in this case, what would happen is as the window resized horizontally, your view would resize horizontally.

The next type of positioning we support is scaling. In this specific example, what we're doing is we're taking, as we are scaling to 50% of our parent view's horizontal distance. So, whatever width my parent is, I'm going to be half that. You can also use this in cool ways, because often times you might have a view that embeds a second view, and the second view is actually exactly as big as the first view.

I was doing this the other week, in fact. And what you can do is you can just use this and just say, "I'm 100% of my parent." And you can do that in both axes, X and Y, so width and height. And it just automatically lays out all the, yeah, stuff, things. All right.

Position is the next one. Position you can really think of as alignment. In this specific example, what we're going to do is we're going to position ourselves centered horizontally to some view that happens to be above me, like literally above you. And this is all great code and it's wonderful, but why don't we take a look at what it actually So you saw some of this in the-- I'm my own demo boy, by the way. You saw some of this in-- which machine am I on?

This one? This one. Oh, there we go. All right. So you saw some of this in the HIToolbox session. I'm just going to show you again because I'm that way. So basically, I'm going to edit mode here, and we have this crazy little control. And what we want to do is--

[Transcript missing]

What we want to do is just have this thing bind to this parent, let's say. So we can just bind our right to the parent's right, and as we move, the thing moves with it.

We can also bind the left to the parent's left, and as we move, it moves automatically. And now the things are kind of stuck to the edges. Great thing about the way we have done the layout stuff is that you don't really-- it doesn't matter where it is. The bindings just work. So you can just move stuff around, and the bindings just work. It's automatic. You don't have to think about it.

Of course, there are other things you can do. I could take this view and instead of binding it this way, I could instead just bind it to, once again, 0.5.

[Transcript missing]

Last option that we mentioned was aligning to some other view. So I have B and I have A. See, we knew that was going to happen.

Haha! You can't stop me. Oh! Alright! We really knew that that was going to happen. Oh, yeah. You can't stop HIToolbox. All right. We're from the Department of Redundancy department. So do we really have everything here? Why is that not an app? Or a folder? Is this a folder?

[Transcript missing]

All right, well, you have fun with that. I had the best demo ever, but you'll never see it now. Of course, now I'm building up your expectations. and the Crack team, yes, they do smoke crack, are up there. We'll move on. All right. How do you use HIView? Now we'll have one big demo at the end. How do you use this stuff, now that you saw how great it was?

It's easy. First thing you want to do is turn on the compositing mode of a window. As I mentioned, we have this compositing mode, which we draw. We draw back to front, no erase behind. It's really great. So the way you turn it on is to basically specify kWindowCompositingAttribute at creation time of your window. This means you do have to call something like createNewWindow to make that happen. Or you can just use a nib, check the checkbox. There's a checkbox for this. And it's just magic.

We also say use the standard window handler. You don't absolutely have to, but you'd be crazy not to, because it just does so much stuff for you, honestly. You know, there are people who come to us and say, "Oh, I'm not using the standard window handler. How do I do this?

How do I do that?" And then we start to realize, man, we do a lot of stuff for people. So really, if you're going to call -- if you're going to use composite mode, we really recommend you use the standard window handler. They're a match made in heaven.

So we've always supported like a control hierarchy on Mac OS X. But when composited mode is on, it's a little different. It's actually a bit deeper. In the past, we've had this concept of the root control, and the root control essentially represented your Windows content area. But in HIView, the root is actually the structure.

So that means that all of the window widgets that you see in Mac OS X, Jaguar, Panther, they're all controls, just like anything else. They never were before that release. It was always just an illusion. So we've done a lot of work to really make all that stuff work. So now you have this concept where we used to have this old root control, which is really the content, and now you have this new root control, which is really the structure. So it's important to keep that in mind when you're in this mode.

: What happens to something like create root control is that if you call it in the composite window, you will get an error. It will tell you the root already exists. Now, If you call getRootControl, what you'll get is the content view of the window. And this is to allow you to have code that's compatible with the past. And you might have called getRootControl and then embed control on that. That code will still continue to work as expected.

If you want the real root, you call HIView.getRoot and pass the window ref. So, if you look at the two calls there, they're equivalent. GetRootControl and HIViewFindById using a control ID, in this case, khiviewWindowContentId, which is defined in HIView.h, both of these calls will yield the exact same call. Both work. The second is technically more correct.

In the simplest of all worlds, you'd have a window that used all toolbox controls and all would be blissful. And in that case, all you have to do is go in and turn on the compositing attribute and things would just work. I mean, really, it's just that transparent to you. However, there are some things that your code might be doing which might change. It depends on how far you go.

So, for example, if you're calling set and get control bounds, These are actually frame coordinates when you're in composite mode, despite the name bounds. So it's important to keep that in mind. Get and set control bounds, frame coordinates, and composite mode. So you have to look at everywhere you're using that for a specific window that you might be dealing with and say, all right, am I dealing with moving or resizing?

Then I should probably use HIView set frame or get frame. Am I dealing with drawing, hit testing, or anything that's more local? I should use HIView get bounds. And you can convert points from bound space or from view to view by using HIView Convert Point, Rect, and Even Region.

Another common problem that we find is that you take some existing code with some existing controls, with some existing bounds, and you just embed them. And then half your stuff went away. And you're wondering where it is. Well, it's probably living way down below the bottom of your window and you can't see it. And it's been clipped out by its parent. And yes, we do clip to parent in HIView all the time. There's no sometimes anymore.

So this is caused by using basically the wrong coordinate system. You're using port coordinates everywhere, and you're trying to now embed that all in an HIView space where, again, they're parent-relative. So if you had a group box and a push button in it, which was supposed to be 20 pixels down, you might find that it's way down now just because you're in the wrong coordinate system. So these are things to keep in mind.

Some more do's and don'ts. If you ever called draw one control or update control, in general, don't. Instead, just use hiv_set_needs_display if you really need to re-draw something. Though in general, you probably won't need to do this because it'll be your custom views or things like that that will invalidate themselves.

I would say you almost never need to do that from an external point of view. If you absolutely need to draw immediately, you can use hiv_render on Panther. Technically, you could use draw one control on Jaguar. But again, we're really trying to emphasize the invalidation model over the direct draw model. So really try to focus on that. And only use direct drawing if you absolutely have to.

Because in truth, if you're going to use something like hiv_render, you're essentially rendering all the layers in the composite layer for the region that you're interested in. So it can be a little pricey. So you don't want to draw as much as maybe you used to. Let it happen at event loop time. Where everything can be aggregated and we have a better performance overall.

If you ever called Draw Control in current port, just don't. It doesn't work in composited mode. It just ain't gonna happen. So what you want to do instead is if--you might be able to use something like HIView Create Offscreen Image. This is an API that allows you to just take any control in your hierarchy and just create a CG image ref from it. And now you have a perfect image of that. You might be able to use that for some of the cases that you were using Draw Control in current port for.

And lastly, if you've ever called Auto-Embed Control, it just isn't going to work as expected. This was invented for the Dialog Manager, where we had a flattened little list, and we didn't really have a hierarchy. We were trying to build one from that flattened list. So we kind of had to figure out where things went and automatically embed things in one another. And it also assumes they're all important coordinates and all these other things. It just isn't really going to work at all in composited mode. So just use, you know, HIView at Subview or even Embed Control directly.

You might be asking yourself, I have a dialogue manager dialogue. How do I make it support HIView? The answer? Use a nib. Yeah, good answer, huh? I mean, I don't really have a good answer. This is the answer. You should switch to using nibs because basically they're a lot better than the dialogue manager.

You don't have to worry about Pascal strings. I mean, everything's Unicode savvy. It's a lot more localizable. Plus you don't have to deal with all of the -- you can only deal with a certain subset of controls, and when you can't deal with those, then you need to create CNTL resources, and then you have to put a PROC ID in there.

And what does the value in a max mean again? I don't remember for this specific control. It's really just a big mess. So with the advent of nibs and Carbon events, even at the beginning of Mac OS X, we said dialogue manager, you know, start moving away from it. And we're still saying that, especially in the context of HIView. So now we're going to talk about how you can write your own views.

Essentially, every view you do, or any constant content that you have, has to be wrapped in an HIView. It's the law. And the reason is we want to have a consistent behavior. We want to have one draw pipeline, you know, and everything goes through the same bottleneck. This is important, or else you can't do things like overlapping views, because you could have people stomping on one another. It has to be all through the same pipeline.

So let's talk about how you write one. It's basically like you'd do anything else that's HIObject-based. You register your view class via the HIObject mechanism, so HIView, HIObject register subclass. And you can subclass HIView or one of our existing controls as of Panther. In Jaguar, you couldn't really do that.

We didn't export the class IDs. In Panther, we do export the class IDs. And don't go getting fancy trying to use those class IDs on Jaguar. They might not work because we actually twiddled the names between those two releases. So Panther forward, you can actually subclass existing controls.

And then all you have to do is just create your view which HIObject create. And you pass an event handler into your register. So once you create your object, then you're up and running. And then what you need to do is just handle the Carbon events. Pretty easy.

Let's first talk about the draw event. Chances are, if you're running an HIView, you want to draw. So we send you a Carbon event so you can do that. K event control draw. It takes two parameters, or it receives two parameters. The first is a context ref. This is where you draw your bits. You should always draw in this context ref. Do not create a context of your own. This will be important as we move toward being able to print the view system, which, honestly, we're not too far away from, but it won't make Panther.

The second parameter we give you is a draw region. You are actually clipped to this region. So once your draw handler is called, clipping is set up, and so is the G-state. We save G-state for you. You can draw like crazy, transform, rotate, do all types of crazy stuff, and leave the G-state trash. We're going to restore it right after the draw event, so it doesn't matter. And actually it would be faster overall if you don't save and restart a G-state yourself.

The other thing about the draw region that's important is you might want to use this if you have a complex control. I mentioned earlier that you might want to use HIView set-needs-display-in-region. If you use that to just invalidate a portion of your control, when you get called back, you might be called back to just redraw that portion of the control, and that will be reflected in your draw region. So you can use that region to kind of intersect it with the parts of your control and just draw the pieces of your control that intersect that.

Next, we want to talk about calculating regions. We have a Carbon event for that. K event control get region. In general, there are two regions that we really care about: structure and opaque region. There's a new one in Panther called the clickable region. This actually gets used for things like async dragging, especially on metal windows where you want to be able to click through things, like text and other things like that. Regions are always in HIView bounds coordinates, never in frame coordinates.

Structure region is essentially where you're going to draw, and it can extend outside your bounds. That's okay. It's a little weird to some, I agree, but it's kind of the way things have always been. And it's also convenient in a way, because we can do things like focus rings and have adornments that draw outside of the natural view bounds. And this can be helpful, because then the metrics of the actual view don't change, even though we might change the metrics of how we adorn things.

So, for example, as the edit text field gets and releases focus, you might want to extend outside and then contract back in. If you don't respond to this event, we will assume that your structure region is just your bounds. That's fine. You don't need to respond to the getRegion event. But if you do need to do things like focus rings and you are changing your shape, that might happen like at setFocusTime.

So we'll call you with a setFocusPart event and you'll maybe say, "Alright, I'm focused now. I know I'm going to be bigger, so I'm going to call HIView reshape structure." And that just tells us, "Oh, something's changed. We need to re-query your structure region and re-compute and re-invalidate everything." And we just do it automatically for you. And there's the API. Very easy to call at the bottom of the slide.

The opaque region is used to help us determine what's basically visible below you in the hierarchy, so what's behind you. This is used to help us optimize drawing. And also is used to determine what the window's opaque region. Basically, we take the aggregate of all of the opaque regions of all the views in the window, and we just kind of ship that off to the Windows server so that it knows, all right, these regions of the window are opaque, and I can use that information to maybe do fast splitting for that section of the window. And I know that nothing can show through.

If you don't handle our request to give us this region, we will assume you are transparent. That does not mean you do not draw. That just means that you might have holes in yourself, or maybe sometimes you are transparent, or you're small enough that it doesn't matter. In general, if you have an opaque region, in general, you should make views opaque that are rather large.

Smaller views being opaque actually chop up the views behind it, and that makes for a very complex clip region. And what happens oftentimes is that it ends up being more expensive to draw with the clip than without the clip. So unless you have a really big region, I mean a big view that's going to span most of the window, you might not want to do this.

But we will use it, and we will know, all right, we don't need to draw the metal background of the window. Metal's very expensive to render. So if you have a data browser or something that's spanning it that you wrote, we know that we don't have to draw that metal behind that big area, and that saves some time.

If your opaque region changes on the fly, and it can, you can call either HIView Reshape Structure, which is available in Jaguar, or in Panther, you can call a newer API called HIView Region Change. It's a much more specific API. You're going to tell us, "My opaque region changed." And we'll go, "Okay." And we'll recompute everything, and we'll know that next event loop time, we're going to readjust the opaque region of the window. But that API won't invalidate. So you can do, like, multiple things, including HIView Region Change, and then maybe invalidate yourself if you really need to.

One thing about regions is that you generally want to be square for maximum performance. The squarer the better. And how can you be more square than square? The idea here is you want to use Alpha to define complex regions and not a complex region. It's much easier, and the graphic system is designed this way. So really take advantage of Alpha and just use square regions. Every window that we have has a square structure region. We don't use complex shapes anymore. We just use Alpha to punch out the corners. It's much easier, too.

But, you know, for hit testing and things like that, if you have a view that is essentially a circle in the corners where you would normally punch through, you might want a click region that just represents a circle. In those cases, it's perfectly fine to have a complex region to describe that because, you know, hit testing that view happens much less frequently than re-rendering that view.

I want to touch on control features. For a long time, we've had this concept of features, ever since Apparence 1.0. Ultimately, what they do is they advertise the abilities of a view. So, for example, you would tell us, "Oh, I support embedding. I have, uh, I want focus on click," things like that.

And we have some new features that we're introducing in Panther as well, like the isOpaque bit, which I'm going to cover in a little bit. On Jaguar and Prior, The only way to specify these features was at K event control initialize time. We would send you this thing. This is kind of like the analogous, the Carbon event that's analogous to the old control init event or message from the Defproc days. And in response to this initialize event, you're expected to give us your features, and then we would hold onto that, and they were immutable.

These days in Panther, that's changed. You can change your features on the fly. You can say, "I'm going to better." No, I'm not. Yes, I am. So you can write your own schizophrenic views. And the other thing that you can do is -- I mean, this comes in really handy, because, yeah, you might want to say, "I'm opaque now," or, "I'm not opaque. I want clicks to go through me. Wait, no, I don't." And we use this, for example -- consider a document So, you have a document window, and the content region is opaque, and clicks hit it. Okay? It's like a solid, you know, thing.

When you specify the metal attribute, do you see that area anymore? No. And that's because we actually take--it doesn't go away. It's still there. But now we make it transparent. It's no longer opaque. And we allow clicks to go through. And we do that on the fly via this mechanism.

So three interesting features that I want to touch on for various reasons are, number one, KHIView is opaque. This marks your entire view is opaque. And this is just kind of a clue to us so that we don't have to ask you for it. I mean, asking you for the region basically involves us creating a Carbon event, stuffing some parameters in, sending it off to you, getting back the result, and then, "Oh, hey, a region. Great." But if you know that your entire bounds are opaque or your entire structure region is opaque, just tell us that via the feature bit, and we save some time.

Likewise, KHIView ignores clicks. As I mentioned, we have this clickable region. This allows you to now specify that area of your control where you accept clicks. Anything that -- any point that might hit outside that region will go through to views behind you. This was never the case before.

But if you know that none of me is clickable, you can specify khiview ignores clicks. And again, like the opaque region, we won't ask you. So like our text control specifies this rather than handling the event. The last one is KHIViewDoesNotDraw. Now, I did say before that chances are you have a view and it wants it to draw. The answer is, well, sometimes. You might have a view that's just a container view, and it doesn't draw.

It's not supposed to respond to clicks. It's just there to group other items so that you can operate on them as a unit. So the combination of things like ignores clicks and does not draw allows you to accomplish that. And that's That's exactly what our content view does when it goes on to a metal window.

and we also save work because we know, oh, you don't draw. So, hmm, I don't have to actually call you to draw anymore, and I don't have to set up a G-state for you or any of that sort of thing. And we also know that we can be more efficient when we move you. Normally, when we move your view, we're going to invalidate the old area and the new area.

With this, we say, "Oh, hey, wait, you don't draw, so I'm just going to invalidate the areas that your subviews are, which might be a lot smaller, so we can make things more efficient that way." Can we do code in the demo? I'm getting the thumbs up. All right.

Will anything explode? Everything looks good there? Swear to God, hope to die, stick an Italy in your eye? Yeah, we're good. Alright, I won't go through Rebounder, but I did want to go through some of the other things that I was unable to go through a little earlier.

The first is this little app. I think I showed this last year. But basically, what it is is just proof that I'm not lying to you, and indeed, the hierarchy of a window is deeper. So what we're looking at here is the representation of the hierarchy of that window that the chasing arrows is running in there. So basically we have our window frame here. Stop that.

All From there we have the window title, and you'll notice that they'll highlight as I go through them. We have the grow box, which you can't see right now. And then we have the window buttons up there. They're all real views. What does that mean? Well, it means that I could just take it and put it right here. Maybe I want it bigger. You know what the best part is? It still works.

So that, I mean, this is pretty cool. So now, you know, everything's a view. And you can do really cool stuff with that. But basically, you know, realize that we do have, you know, a deeper hierarchy now, and there are diff--you know, the old get root control does not mean the real root anymore. That's the important thing to take away from that.

The other thing I wanted to show was something we also showed last year about some of the things that we do with views these days. There are some views that are compositing only. One of them is ImageView, the other is ScrollView. One of them is TextView. ImageView we may try to make work in composite and non-composite, but some of them are a little more complex than that and are... the problem's a little harder to solve.

So... Here's our image view. It's pretty exciting. Look, an image. Okay. Well, that was fun. All right, so now ScrollView. ScrollView is a little more interesting because it manages all the stuff you need to deal with with scroll bars, including things like auto-hiding the scroll bars when the image is full-size.

So we take care of all of that for you, and we supply this protocol called the K-event -- what is it called? K-event scrollable protocol, and you just handle a Carbon event, a couple of Carbon events that you will ask, like, a target view for. How big are you? What's the available view size? What's your line height? Things like that, so that the ScrollView knows how to operate on it.

We highly recommend that people use the ScrollView because then as we make changes to the way things scroll in the interface, everybody will get that stuff for free, rather than everybody rolling their own. One thing I just want to demonstrate just for grins is that -- Last year I had a BMW roundel in here and I got in trouble for that, but anyways, I won't get in trouble this year. Or maybe I will.

Alright, so... The cool thing about this really is that that view is composited at the same-- it's a sibling of the scroll view. And you notice that it's actually drawing over the scroll view, in fact, over the scroll bars, and that it even composites fine. And I get great performance out of this. So it's really fast. That is one of the things you want to take away from this. HIView is fast. We've really optimized it, especially in Panther.

Now let's look at some of the problems that can happen. Do we have Xcode? Yes, we do. So here's a little... We call character edit, which apparently is building right now. And while it builds, I'll just tell you that what it is, it's a visual description of what is wrong with the past, okay, and how the future is pretty bright.

Eventually it will build. Okay, I gotta keep talking. Did I mention that HIView was fast? Yes. But, you know, more importantly, in the past there were these problems with drawing order. Drawing order was always different than click order. And we're going to actually see an example of this in about ten years.

Linking, all right. All right, so here it is. So we see Guy here. But I can't click on any of these things. Nothing works. And that is because in order to get the picture visually behind all of the other controls, it also happens to grab the clicks first, which really sucks. So let's fix that.

So we're just going to run Interface Builder. And here is the window. And if I select the window and turn on compositing, Whew, that was tough. OK. So anyways, it works now. And I can click, and everything's great. Now, notice that it kind of lags, doesn't it? Yeah, that's not so great. Let's fix that, too, while we're in here.

So we'll go back to the nib and we'll look at this thing. And what you see is that we have a picture control in here. Let's get rid of that. And instead, what we're going to do is we're going to drag in an image view. And we'll just stick that there.

Hopefully I'll give it the right ID. I just want 440 by 440. Exactly. And now, we just need to send this to the back, take our user pane, and go back into position. Save it. It's close enough. And run it again. Now, as you can see, I indeed used the wrong ID.

So we can fix that very easily thanks to Interface Builder. 1. Save that. Build. Run. Oh, I hate you. Now I have to look at code. That makes things There's a couple of things that, yeah, image one. If it doesn't work, well then, oh well. Let me just check. It was image one.

[Transcript missing]

This guy pinged. Oh, guy looks like Homestar Runner. I don't know why. There he is. OK. So the interesting thing is we have this running. But now, look at that. It sticks right to the mouse. Now I'm in a PowerBook so it shows it. I was afraid I couldn't show this demo because we're on these fast machines. But on this PowerBook, on this nice, slow PowerBook, I could do this. This also happens to happen on my machine at work. But anyways, that's another story.

But the cool thing is that ImageView is really optimized for HIView. It's just much faster than using a picture or drawing using CGImageRef itself. So we highly encourage you to use ImageView wherever possible. Now the other thing that it didn't show is that, hey, we've actually embedded all of this stuff into a scroll view. We just didn't show you that. So that's pretty neat. I can also scroll everything right over the picture and you get full compositing. Beautiful. Everything's cool. Alright, so that took longer than it should have.

One of the other things I wanted to show you was just about the pervasiveness of the view system. So, you know, we showed views, and those are great. But we also touched on, in the toolbox session, things about being able to draw in menus. So let's take a look at some of those. And this is a standard menu views example. It's there. It's on the website right now. You can download it. You can play with it. But in case you haven't, let's just take a quick look.

Menus can now be driven through HIView. You don't have to use MDEFs anymore. "Oh, hallelujah," you're saying. So here's a standard menu, and that looks great, right? But now you can do cool things like have pictures in the background. You can have custom views like this. I mean, this is basically the basis for what the Finder ended up using. Just a simple custom menu with some text, color grid, all types of neat stuff.

And the coolest of all, the volume control, right? So here we have a menu that just has other views in it. And yes, it actually kind of sort of works. And in fact, I found out earlier that if I click the Press button, It actually opens up the sound panel. So that's cool. So you can use these things anywhere. Now, that was views, menus. What about windows? Well, we can show you a window.

There you go. That is a completely custom view written completely in HIView with controls embedded within it. I wrote this last night. It only took me about-- I won't even tell you. Anyways, but here's the other thing. This is that same view embedded in a window. It's the same view.

And whenever you would click on this view, I have a handler in there so that when I get clicked, I call drag window. So if I click in this view, I call drag window. So it just kind of works. But the important thing is it's just a view. You can be deployed anywhere and in any way. And you learn that one model, deploy everywhere. OK.

I just launched the wrong thing? Don't open. Yes, OK. I mean, I didn't speak that high. Okay, one of the things I want to show you about at this point-- See, that was the before demo. Now it's the now demo. What I wanted to do was go through some code here to exactly show you the steps involved in building a view. So we're going to use the simplest case right here, which is called HITestView.

This is part of the sample code. It's on the website. It's even on all builds of Panther and the developer tool stuff. I'm just going to have some fun font stuff here so you guys can all see. I'm just going to increase this to... Can I click it again? Of course.

Now hopefully you'll be able to read this from way back there. Basically, I mentioned that what you want to do is you want to register your class, and then you want to create your view, and then you want to handle events. So we're just going to show you an example of exactly how to do that in real code.

So first we have our register function. And this is kind of a one-shot function. You know, you register once. You can call it ten times. We're only going to register once. And what we do is we set up a list of events that we want to handle. Now, this is an HIObject, like all UI objects in the HIToolbox. So we have to respond to at least two of these events, construct and destruct technically, but we're also listening to initialize, just for grins in this case.

So we have our HIObject protocol. I won't go too much into that. But these are the important ones. Here's where we're actually handling everything that has to do with controls. So whenever you create an instance of this class, that handler, this handler right here, is installed automatically, and all your event handling is set up automatically. And I'm going to say automatically all the time. So we have activate, deactivate, highlight changed, and value field changed. The only reason we're handling those is so that we can call invalidate. All right. HIView set needs to be set up. display.

As I mentioned, you need to do that yourself, so you're going to have to handle these events if you want to actually get redrawn when these things happen. But of course, it depends on your view, what you're doing, et cetera. Below here, we end up calling HIObjectRegister subclass. So we just pass our class ID, which is just a string, and we're subclassing the standard HIView base class. We're installing HITestViewHandler, which is just below in the bottom there. And we're just installing it for all the events that we saw.

Now, once we've registered that, you can create the view. And we have a function to do that called HITestViewCreate. And first thing it does is just register the subclass just in case it hasn't been registered already. Next thing it does is we create an initialized event, and I think what we do If you pass in bounds, we will set the bounds into that initialize event. This is a way for you to get initialization parameters into your view so that you can do interesting things.

And then all we do is we call HIObject create. And bang, you get created, your construct handler is called, you build whatever instance data you want, you give it back to us, standard HIObject stuff. If you don't know a lot about HIObject, I urge you to read documentation that's on the website. I also wrote something for Mac Tech last year or something, end of last year, which goes through this in, you know, insane detail. So you might want to check either one of those things out.

And then if we get one, we'll actually embed it into the window. You don't need to pass a window to most of the HIView APIs. Well, actually, most of the HIView APIs don't take a window ref at all. And all of the older APIs, like create push button control and all of those things, have been modified to accept null as the window parameter. You don't have to bind it to a window. You can have detached views in the HIView system.

So once we've created this thing, we're going to start getting events, and we're going to get them through our handler right there. So first we're handling events of K event class HIObjects to just construct, initialize, And then we handle the control events. So we get told to draw, we draw.

We get told to do hit testing, we do hit testing. Tracking, we do tracking. And here are those other events that I mentioned. So when any one of these things happen, we call HITestViewChanged, which ends up taking us here. And all we do is call HIView setNeedDisplay, passing true, meaning yes, please.

and we'll get invalidated and redrawn in the next draw cycle. We also have a get data set data. It's just here mostly for example. And get region, which is also only here for example. Take a look at some of these. I'm going to skip construct and destruct, that's standard stuff. If you really want to know about it, just look more in the example. Initialize. We get the bounds out that we had passed in the create thing. We set our bounds in here using HIView set frame.

OK, drawing. First thing we do when we're going to draw is we get the context. The context is only sent in the composited mode right now. So we get that context, and again we've been transformed. Top left is 0, 0, and we're clipped accordingly. And what this does is we call it Red Box, Blue Box. So it's a red box until you click on it. Then it's a blue box. It's wild. So it's just a simple example to show how you can do this and how you can tap into the standard tracking that exists in the Control Manager.

So, you know, if we're just sitting there, nothing's going on, we're red, we get clicked, we're blue. And we also listen to these two part codes. So we get our highlight, essentially, and we decide how to draw. If that is zero, we know we're in a standard state.

If it is the inactive part or disabled part, we can draw in the disabled state. An even better way to do that is to actually check to see-- which is to call is control active or is control enabled. In general, we don't like to overload the part codes these days. And if it's any other part code that's non-zero, we'll assume that we've been clicked and we draw blue.

Okay, hit testing. In the hit test scenario, what we do is we just extract the mouse location, we test to see whether that point is in our bounds, and then we just give back the correct part code. If it's in our bounds, we return a part code of one.

The part code space is your space. You can return anything you want. The only thing you need to keep in mind is that if you pass zero back to us, we will assume you don't want to track at all, and maybe that's what you want. Maybe it's not what you want, but just be aware of that.

Technically, from a technical, pure standpoint, you don't have to even check your bounds because we wouldn't have asked you in the first place unless somebody was calling just something like the old test control API. You might get called that way. But again, for proper checking, you might want to at least check your bounds. So if the click is in us, we're hit. If it's not, we're not hit.

And we report the answer back. And again, that answer ties into the standard tracking loop in the toolbox. The minute we click on a view, we'll ask it, was there a part hit? Oh, yes, it's part one. Okay. Start tracking that part. And then we'll do standard tracking for you. And as we track in and out of your control, we will set the highlight, and that's how you draw.

You can also do custom tracking. I don't think that this specific example is set up to do it right now, but Instead of having the toolbox do the tracking, you can do it. There's even another level of tracking beyond this, which, the minute the click gets to you, you can do whatever you want.

Once you get into just kind of like two levels of tracking, I think we explained it in the Carbon events .h header. But basically, you can do anything you want. In this case, we're doing basically what the toolbox does. You know, we're just saying, "Oh, were we inside when we started? Well, we probably were." And then as you move out, we set our variable.

You've probably all written this loop. But the point is you can write it yourself if you want to. You don't have to use the tool box and stuff. And that's the great part about Carbon Events and the way it ties into HIView. You can override just about anything you want.

Again, test view change, we invalidate, we already saw that. Get data, set data, I'm not going to go into those. Not very exciting. Get region. That's something I want to talk about. This is the get region handler. And we extract the part code and a region handle. And basically, based on the part code, we want to manage that region handle. It's important that if you get a part code that you have no idea what it is, return event not handled error. You may trip us up.

We did experience a couple of applications that were not doing so, and they were giving us empty regions, and crazy things started to happen. So we had to implement workarounds in the toolbox. In general, we try to avoid doing that because there is a performance penalty in that regard. So really try to be correct in how you handle these things.

In this case, all we're going to do is if we get the content or the structure meta part, we're just going to return our bounds. Like I said earlier, you don't have to do this. If it's just going to be your bounds, don't even handle it. That's even simpler.

But it's just a simple example of what you would do. So we just get our bounds. We convert it into a quick draw rectangle because we need to call rect region. As I said before, we're trying to move toward these new shape riffs that we have. We kind of introduced them.

introduces a bad word because you couldn't use them, but we put them in Jaguar, but you couldn't use them. We're starting to use them now in the HITHeme API, so you'll see HISHapeRefs used there. Okay, so if we were to run this, you see a very exciting application. Red, blue, red, blue, red, blue, red, blue, huh? Okay, so that's cool. Now you notice there's one next to it, and that's what I want to cover next.

That's okay. All right. C++. Everybody's friend. Everybody's enemy. But here it is. We like C++ on the toolbox. We use it internally. All of our stuff follows this thing. We have HIView internally. It's a C++ class, and we just export it through this HIObject layer. Well, what we also did when we were writing these sample codes is we came up with this TView class, which is kind of a mirror of our HIView class, only it's a little more generic.

So what we do have is this thing called... I wonder if it's in a nice-- oh, there it is. Beautiful. We have this thing called HIFramework. There's a series of classes which allow you to write views really, really easily. And that's how I wrote even that sample app with Homestar Runner and all that.

Very easy to write this stuff. It's part of all of our sample code. All of our sample code relies on it. We urge you to look at it. It may grow into something bigger. We're not sure right now. The important thing is you might be able to use it directly, you might not.

If you can use it directly, great. If you can't, you might at least be able to borrow the ideas and the implementation strategies that we put in them and try to merge those into your own framework. So we really urge you to look at it because we figure that most people are actually programming in C++ these days. So we won't go too much into that. Suffice it to say that we have this ttestview that derives from our tview class. We can see just exactly the same basic control that we just looked at, only in C++.

So these lines are interesting. I mentioned that if you change some aspect of your view, So, in this case, all we need to do is just say on highlight and on activate, we want to be auto-invalidated. We also do this internally in our HIView class, and it's so helpful that we may end up exporting it as a real API in the future.

But basically, this code's a lot simpler. And you can actually look at the guts of the code. So like, create is just register class, create, and then embed, just like you saw in the earlier example. Register class is even simpler, because basically, tview, or ultimately, tobject, which tview derives from, just takes care of it for you. So you just give it your class ID, and give it your construct proc.

And Construct is like a static proc on your class. It gets called whenever HIObjectCreator is called, and all we do is create a new instance of ourselves and return it. Very easy. Draw is pretty much the same code, only we don't have to do all the CarbonEvent stuff. That's the beauty of using the C++ stuff, is that all the CarbonEvent mechanism is taken away, and you can just worry about what you're doing.

Hit test is simpler. We don't have to deal with taking the part out-- I mean, taking the point out and returning the part. It's just a method call like anything else. And get region is simpler, a lot simpler. So we urge you to look at stuff like tview. Unfortunately, I'm running short on time, so I'm going to cut back to slides.

Okay, I want to talk briefly about performance tips. First off, as I mentioned, try to invalidate only those portions of your view that need to be redrawn. If you have a small view, you can probably just invalidate the whole thing. It's not going to be a big deal.

But if you have a really complex view, a table view, or some type of spreadsheet stuff where only cells are changing, you might want to just invalidate those portions. Obey the incoming limit region, that draw region that I mentioned in your draw handler, if you do have a complex control, to limit the area that you draw. This makes a huge difference in the performance of your views.

If you're moving or resizing views, the best, best solution in Panther is to use layouts. The next best solution is to watch ContentView BounceChanged events, not WindowBounceChanged events. Turns out that WindowBounceChanged events come too late. The way the window mechanics are is that when you resize a window, we reshape the whole frame view, which reflows all of the window widgets and the like, and then we paint it.

Then, after that, you get called with a window-bounce-changed event, and then you go, "Oh, I have to reflow," and then you move everything around, and then we paint it. So just by listening to window-bounce -- I mean, the content view-bounce-changed, as opposed to window-bounce-changed, makes a big difference in your live resize speed. You will definitely get a few frames per second in that. And the best solution, as I mentioned on Panther, is to just use layouts, because then you don't even have to worry about that stuff.

I should go back and just-- and those feature bits that I mentioned that help us to avoid having to call you for reasons, make sure you set them as appropriate. So, in summary, I want you to know that HIView is our focus, and we're really concentrating all our efforts on making it perform great.

And it's changed our world, seriously. Writing things like the sidebar in Nav just took days. I mean, it's just, it's amazingly quick. You know, I can write demos in hours. No, that's what's, that's what's, no, that's not a good example. But, seriously, we've been able to just tear through views these days. I mean, the amount of work that we can get done is amazing. And I guarantee you that if you start to use this stuff, you will realize that, oh my god, this really is different.

And you can throw away a lot of code in your application. I kid you not. So, and at the same time that it makes everything better, it's still familiar to you. It's still an extension of the control manager, okay? And it's still, you know, it's kind of like it's still home.

And that was funny. I was serious. So, yeah, and as I mentioned, it's pervasive, so you don't have to have all of these different, you know, MDEFs, CDEFs, WDEFs, just views, and you're done. And above all, when we make changes to the renderer and the compositing stuff, you'll all benefit from that. So we're all going to share in this HIView adventure or so. And as I mentioned, it's very fast. This is a fast view system. It is not the control manager. Trust me.