Application • 56:29
Whether you've been using the Toolbox for many years, or you're a new developer looking for a C/C++ widget set, this session is for you. After a short architectural overview, we explain through a set of examples how to adopt HIView and take advantage of other new HIToolbox features in your Carbon application.
Speaker: Guy Fullerton
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Hi, everybody. Welcome to session 422, which actually we'd like to call the HIV toolbox day or HIView day. A lot of things have been evolving inside Mac OS X, and this week you've probably learned a lot of these evolutions. First, like you probably heard, Quick Draw is being deprecated. We talked about being ready for resolution independence.
So what does that mean for you as a Carbon and C/C++ developer? What does that mean for you as an HI Toolbox user? Well, that means a couple of things. And first, that means that your code is going to have to evolve as well to play great in the next operating system of Mac OS X, the next version of Mac OS X.
The good news is that we have a lot of technologies that we're going to be announcing this week here in the HI Toolbox, and a lot of things with HIView that we've been doing that should really help you. And we have really packed sessions that should really help you move forward.
We're going to start today with Guy, who is our representative from France, who's going to be actually talking about switching to a Modern HI Toolbox, and he's going to teach you really the architecture of HIView and how you as a developer can take advantage of it. How you're going to get there and all the features that you're going to be able to take advantage of. So I think it's a great first step for you to understand the foundations of where we're going here.
After that, please don't miss the session that we're going to have at 3:30 with Kurt, Monsieur Kurt, where actually we know that a lot of you guys are still based, have dialogues and Windows resources. And so we're going to take a full hour to kind of like give you a basis and help you switch your resources based user interfaces to Nib files. And then we'll teach you as well how to kind of like link them together using Carbon events. Very, very cool session. I think you'll like it.
Now, getting ready for resolution independence, we're going to teach you tomorrow, we're going to have a session that's going to explain to you how to switch your custom drawing content and how to switch, you know, like if you're using C-DEVs, for instance, to use custom HIViews. This is a very, very, very important session.
If you have custom controls, I really encourage you to attend that session. So you're probably thinking, well, that means, you know, I'm going to have like still to like, you know, like rave some old code that I have that has been working. Yes, you're correct. You're going to have to revise and like, you know, move some code. If you're using Quick Draw, you're going to have to switch, you know, to core graphics.
But what's very, very important to understand is that this is very, very good for like our users because your user interface and your application is going to gain in performance and in features. And this is really where we're moving towards. So I think we'll be very pleased with the work that we've been doing. And let's start with Mr. Guy, who's going to be taking you for the next hour with like the new things we'll be doing with HIView. Mr. Guy. Thank you, Xavier.
So good afternoon. I hope everybody had a great lunch. Get ready for a very packed session. Maybe my fast speech will help me for once here. Okay, so what I'm going to cover, in essence, is the meaning of HIView and how that can help your applications. And then I'm going to show you a step-by-step example of how you can take advantage of HIView in your application.
Then I'm going to have a section on resolution independence. It got mentioned at the graphic sessions already, but I'm going to show you the Carbon story for resolution independence and how you can start thinking about that for your applications. And finally, I'm going to go through a potpourri of various new features in the HIToolbox for Tiger.
So we use this word HIView a lot. Xavier probably said it 15 times as he was talking there. And a lot of people wonder what HIView is. I mean, I'm sure you've seen the typedef, and then we say adopt HIView. Does that mean I just need to grab one view? Well, no. What HIView means when we say adopt HIView is it means get on board with everything we've been doing for the last three or four years in the Toolbox. HIView is this conglomeration of cool new features we've been adding since Jaguar, essentially.
So technically, HIView is our object-oriented view system. It's based off the Control Manager. If you've seen the Control Manager and you're familiar with it, and I know most of you are, HIView is also going to feel very, very familiar. It's so familiar and so similar, in fact, that it's the same type ref-- type def. A Control Ref is an HIView ref.
It's the exact same type. They are literally type def to one another in the header. It means you can use HIView refs in Control Manager APIs, and you can use Control refs in HIView APIs. Now our object model is based, of course, on HIObject. We've talked about that in the past. It's a very cool subclassing mechanism for Carbon clients. And we use Carbon Events as the messaging model.
Now, HIView also includes all the new widgets we've added over the last few years, like the search field and the toolbar and things like that. HIView also represents a much more advanced drawing model where you can take advantage of all the cool quartz features. And there's some other side perks in there. HIView gives you much better drawing support in the sense that the APIs now make sense when you use them, and they don't do things you're not exactly expecting, and they don't force you to take workarounds.
Now the reason you want to adopt HIView is it's because what your customers are asking for can really only be granted by HIView. HIView can give you all those buzzwords like the library sizing, quartz transparency, various forms of animation. Going to HIView is also the only way to adopt certain new Toolbox features we've added over the past couple years. The Toolbar is one. Our TextView and ScrollView, which is our full-featured text editing view in Carbon, is another. We've got an ImageView. We have HIView layout mechanisms, which are like springs and rods on steroids. And all that only works under HIView.
Now another reason you should use HIView is it's just going to make your apps faster, right? We did a lot of things much more efficiently. We learned from our mistakes in the Control Manager and just made it better. And the best thing about HIView is it actually makes your jobs a lot easier. The code you end up writing to do something custom in your app is a lot simpler when you use HIView to implement it than if you have to hand-roll it yourself the old way.
Some of you may be looking at your applications these days and feeling like it looks a little bit like this house. It's kind of got a crummy lawn, the paint's chipping here and there, the windows are kind of ugly, the roof's maybe got a couple leaks. Well, one reason you might want to adopt HIView is because it can allow your house to look like this. Put a little work into it, give it some new paint, clean up the lawn, clean up the yard, and suddenly it looks a lot better.
But then there's another side perk: that house isn't exactly the same as it was in the other picture. We've added some things, right? There's a new fence there, there's new windows. In fact, one of the windows was expanded a little bit. So HIView is going to not just let you polish up your application, but it's going to let you expand it and grow it.
So there's five basic steps to adopting HIView, or adopting all the technologies in HIView. So let's jump right into the first one. That's using the standard windows and controls. This is something we say every year, and we need to repeat it because it bears repeating, right? There's a lot of good reasons to do it, and we know some of you still need a little bit of a push to keep adopting the standard windows and controls.
So we've added a lot of new views over the last few years that can do basic things that your apps used to have to do for themselves. If you want a list view that interacts a lot like Finder's list view, go ahead and use the Data Browser. In fact, Finder uses the Data Browser to do its list view.
Like I mentioned before, we have a TextView and a ScrollView that together can be a full-featured text editing engine for Carbon applications. But you don't have to use the ScrollView just with the TextView. You can use it to scroll image views or any other custom view you might want to make.
So take a look at the controls we offer, and take a look at your application and find out what you were doing custom. And see if now there's some parity between what we offer and what you do, and see if you can replace your custom implementations with our new features.
Also, be sure to take advantage of view embedding or control embedding. This is something that we've had a long time, since Mac OS 8.0, but we know a lot of people like to use controls in a very flat hierarchy. They have a window and they have a bunch of controls in it, and then they try to manage the overlapping controls themselves and try to manage the containment themselves. Well, HIView can do all of that for you, so you might as well use the built-in infrastructure. That's also going to give you a lot of benefits with respect to accessibility. An app that's been revved to use HIView is going to be mostly accessible.
In addition, going to HIView insulates you from future interface changes that we might make. The best example of this is probably the tabs. On Jaguar, the tabs looked a lot different than they started to on Panther and continued to look on Tiger. But there were a lot of applications that rolled their own tabs.
And sure, they used the appearance primitives to try to get the standard look, but because of how the appearance needed to change in Panther, those same primitives could not draw the new look. So people that did their custom tabs on Jaguar and got a good Jaguar look, unfortunately carry that Jaguar look into the future, into Panther and Tiger. So that's just another great reason to use our standard tabs. If you use the standard tabs, it's going to get the new looks as we rev the user interface.
That's really the first step. It's pretty straightforward, right? But it's going to give you a lot of good wins. And most of you already know how to do that and are already at least partway through doing that in your application. So that one should be pretty easy. The next step, which is probably the most time-consuming step, let's say, is taking your custom user interface in your application and wrapping that inside individual HIView objects.
The first step as part of that process is to look at your interface and factor your custom areas into individual sub-components. It's standard engineering design methodology. You want to make a view around the simplest, smallest thing possible in most cases. If you do that, it's going to be a lot easier to write HIViews around those individual small widgets, and you'll be able to reuse them in various places as well, which is cool. Then, using our HIObject subclassing mechanism, you create a subclass of HIView to represent your custom object.
Then, when your custom object is put in a view hierarchy, it will receive Carbon events to handle things like mouse clicks, keyboard events, all sorts of other user interaction, accessibility, activations, deactivations, all that stuff. So you just handle those Carbon events, do the right thing. We have lots of HeaderDoc in Carbonevents.h for all of the Control and View related events. You can look in there, it tells you what you need to do, what events you want to handle for certain situations, and it's really straightforward.
Then, if your view needs to communicate state changes to other parts of your application, like let's say when a button is clicked, right, you need to carry out some action someplace else in your app. You can use HI commands to do that. We have convenient APIs to send out HI commands. In fact, the view system already has built in support for HI commands. You can associate a command with a given view. It'll automatically get sent out when the view is clicked.
But if you have more complex communications needs, you can use custom Carbon events to communicate between different pieces of your app. One good example of this is our scroll view. The scroll view is based on our scroll view Carbon event protocol, which is just a set of simple Carbon events that it can use to communicate with its scrollable canvas. If you have similar needs in your application, there's no reason you can't use Carbon events to implement them. They're really flexible, really full-featured. You can put all sorts of attributes in them. You can queue them. You can direct dispatch them. And it's just--it's really powerful.
Then it bears repeating: keep using view embedding. You might get partway through this step and say, "Okay, well, I wrap my custom content in some views, but I'm still going to use a flat hierarchy." Well, I really recommend not doing that. I really recommend using the full embedding hierarchy, because it's going to make subsequent steps a lot easier.
Finally, we have a bit of sample code called HIFramework. It's on the developer website. We have a bunch of sample applications based on HIFramework. And this is a C++ class library that manages the kind of boring details of HIobject subclassing and the HIView mechanism. You can just derive a subclass from our TView class, and it'll automatically set up the HIobject subclass, the event dispatcher, and all that. Then you just override whatever virtual methods you need to and do whatever work you need to in those.
Now we're going to be covering some of these steps in more detail because I just don't have time to talk about them today. So we're going to cover them in two other sessions. As Xavier mentioned, session 421 immediately follows this, and it talks about taking an old style dialog or some old window code and replacing it with a nib file, and as part of that, adopting HIView in the process where you can. And then tomorrow, we have a session entirely on writing an HIView from the ground up to represent some custom area in your application. So I strongly urge you to attend these sessions to get more details on this step.
So that's two of the five steps. The next is adopting the standard window handler. This is probably the most fun step, mainly because it gives you kind of this cool bang for the buck. It gives you this cool win. Now the standard window handler has been around for several releases, and essentially what this is, is it's a Carbon event handler that can automatically be put on your windows to do all the normal event dispatching that you used to have to do manually when you called waitnextevent.
So you no longer have to call waitnextevent, see if it's a mouse event, call findwindow, see if it's in your window that you're interested in. If so, call findcontrol, route it to the control. If it's not a control, you know that whole rigamarole. You don't have to do any of that if you turn on the standard window handler.
So the standard window handler is designed to work directly with HIView. And what I mean by that is the standard window handler only knows how to deal with views. It can route mouse events, keyboard events, all that stuff to views. But if you have completely custom stuff in your window and it's not represented by an HIView, you have to jump through hoops to make that work. You have to install a bunch of Carbon event handlers on your window and deal with all that rigamarole. So the right thing really to do here is if you're going to use the standard window handler, use HIView. They work together really, really well.
The standard window handler is also the most important key to using the Toolbar. The Toolbar essentially puts a system-driven piece of code, or a system-driven piece of interface in your window. And the only way that can receive events is if you jump through some hoops to find the right events to send to it, or if you just use the standard window handler.
You might as well use the standard window handler. So, to get the standard window handler for your window, you need to do a few things. In Interface Builder, which is our preferred way of creating your windows, there's a checkbox in the window inspector palette. It says "Standard Handler." Just check it, the standard handler will automatically be turned on for your windows.
But, if you need to programmatically instantiate your windows, there is an attribute, the case "WindowStandardHandler" attribute that you can specify at creation time. Once you've turned that on for a window, the standard handler will automatically be in effect. As soon as your application runs, it'll start servicing events, which means you can go back into your WaitNext event loop and rip out all the code that had to deal with routing events to that one window. You can just get rid of it. It's generally not necessary.
But the standard handler can't do some sort of functional things on the window that you're going to need to respond to. For example, when the Close box is clicked, a window gets sent the KEventWindowClose CarbonEvent. And by default, the window will tear down. But if you need to save changes or do something else, you'll want to hook into that CarbonEvent to handle it completely.
There's a bunch of other Carbon events like this. You can think of them as hooks. They're not strictly necessary, but they're good ways for you to hook into the system's processing and provide custom behavior where you need to. CarbonEvents.h, again, has lots of documentation in that header. So I urge you to take a look at it, see what sorts of other hook events are in there, and take advantage of whatever makes sense for your application.
So that's step three. Step four is probably the coolest because it's the flashiest. That's switching on compositing mode and starting to draw with Quartz. This is cool because it gives you radically better performance, and it just makes your life a heck of a lot easier. One of the ways we sped up the HIView subsystem through compositing mode is that we no longer draw a pixel twice unless we absolutely have to. And what I mean by that is when a view draws, we know whether or not it's opaque, and we know what part of it needs to be redrawn. And so we make sure to just redraw the parts of the views that intersect that dirty area.
In addition, now that we are not redrawing right when you call various APIs like set control value--. Well, let's step back for a second. So in the old control manager, every time you called an API like set control value, the control redrew. So if you did a typical setup of a scroll bar where you call set control value minimum, maximum and maybe view size, that's four redraws of the scroll bar right there.
That's a big waste of performance. HIV eliminates that. Now instead of redrawing in those APIs, we mark the appropriate areas of the scroll bar as dirty and needing redraw later. And so sometime later, right before you get back into the event loop, that view will automatically be redrawn.
The other cool thing compositing mode does is eliminates the need to erase. If you've ever written a custom CDEF, you knew it was your responsibility to call setup control background and erase before your control actually drew. Which meant that if you had a complex view hierarchy of controls, there was a lot of erasing and redundant drawing of the window background going on, and that's just crummy. So we eliminate that in HIView, which speeds up the drawing pipeline.
Now, of course, compositing mode means you can use Quartz really, really easily, which gives you access to all the cool Quartz features. And additionally, the compositing draw model is far easier to write than the old draw model. In the old draw model, every time your control received certain messages, it was your responsibility to execute your own draw code, sometimes clipped differently or who knows how.
Well, in the compositing mode, you only draw at a very well-defined time in the K event control draw event. So suddenly your code is way simpler. You just handle the draw event, and in your other events, you just handle your mouse tracking or whatever else you need to, and you don't need to worry about weird cross-pollination of functionality. So, let me give you a quick demo of some of the cool things Compositing Mode can do for you.
Okay, so to hammer home a point, a lot of people made requests over time for the ability to draw controls on top of an arbitrary picture. And this is how it would look if you tried to do that in non-compositing mode. The controls try to erase behind themselves, so they blow away whatever picture was there.
And sure, there were workarounds, right? You could hook into the Quickdraw bottlenecks on your port and try to patch things up, but those even broke as our views in the Toolbox started switching over to Quartz. So this is what you ended up with, which is pretty crummy. Instead, with compositing mode, you get something like this. Obviously a lot nicer.
Now you can see the controls are animating, and some of them, the disabled button up at the top left, you can just barely tell that you can see the picture through it, because that button itself is partially transparent. In addition, the cloud and the sun is somewhat transparent. And as I resize, you might actually be able to see the background moving underneath them and stuff.
So one thing that David did when he wrote this app is he just put a little bit of animation in there, right? So clearly not smoke and mirrors. This stuff is clearly all live. It's very fast. It's very easy to use. It works really intuitively. One other thing I want to show off with this application is if you make this really small, all those views are overlapping, but they're all still properly Z-ordered, and they all still draw in the proper Z-order. So you can see, you know, various things going over top of the stop button.
And, you know, I can still click on things when this is going on, and tracking still works. You know, I can track on -- oh, hit testing is -- I'm getting blocked by other views Z-ordered on top of this one from hit testing. There we go. So now I'm actually tracking in the -- the other button. And that all just works. It's very cool. So not many other view systems on this platform or otherwise can do anything like this.
So here's a fun little app. This is not something I'd necessarily recommend you do, but it might give you some idea of the power of HIView and compositing mode. So like I said before, every view draws in a very well-defined place, the KEventControlDrawHandler. And that handler is passed a Core Graphics context reference to do its drawing with, and the view must do its drawing with that context. This means that anybody can come along to your view, intercept handling of the KEventControlDrawHandler, and manipulate the context a little bit if it wants to for cool reasons.
So what Eric did is he overwrote the draw handlers on a couple of these controls and transformed the context to scale up and down the various widgets here. Now granted, there's some artwork problems. This is why I don't exactly recommend that you do this technique. But you can see from the scroll bar in the checkbox that you actually get kind of reasonable results. So these are the kind of cool things you can do with compositing mode.
So let me leave that open. And fire up transparent windows. OK. So Ignignoct here wants to show you just how cool it is to do transparent windows on Carbon. This is something we get requests for a lot. How do I do a splash screen with transparency or something like that? This is an example of how you might achieve that. This is an overlay window that inside of it has a couple views embedded.
It has a view that's drawing that image of the character from "Aquatine Hunger Force." And there's also a push button in that window, and also this slider. And they're all together in the same window. I can move it around. It's got a shadow and everything. And I did this just by creating a custom HIView class to render that image, and then put a couple other controls in that window as well.
But that doesn't exactly meet the goal. People want a transparent window. So what I did is I wired up this alpha slider to talk to the other view and change its alpha value. So I can do stuff like this. And it's all live. It all keeps working. It's really easy to write and it's really fast. And it's all still a window.
In this button, I put the standard minimize command. So when I click it, window goes away just like you'd normally expect. So this stuff is really powerful and really, really easy to use. It only took me about an hour to write. OK. So I can go back to slides, please.
Okay, so to turn this on, there's a few things you need to do, and I want to cover this from two different perspectives. One is from an app's perspective, as in they're going to be a client of the HIView APIs. The other is as a view's perspective, and I'll cover the view's perspective later. So from an app's perspective, what you need to do is go into Interface Builder with your window and turn on the compositing mode checkbox for that window. If you create it programmatically, of course, we have the kWindowCompositing attribute that you can set.
Once you've done that, all drawing in that window must be done with HIViews. We will no longer send update events to you. You will not get update events for that window out of WaitNext Event. So any drawing that you're expecting to do through WaitNext Event isn't going to happen. You've got to put everything inside HIViews.
The other major change you need to worry about is the notion of frame coordinates. So if you think back to the Control Manager, when you want to position a button at the bottom right-hand corner of your window in the Control Manager, you figured out the coordinate, the top left coordinate for that button, as if it were port relative. You find out the quick draw port for the window, you find out the appropriate offset from the top left of that port, and you use that as the button's top left. It changes a little bit in HIView mode and compositing mode.
Now you specify the button's top left relative to its parent view. So it's just a little change, something we had to worry about when we rev navigation services in Panther to start using compositing mode, but it's also something pretty easy to get through your head. You just do it a couple times and suddenly it becomes intuitive.
Another change you need to make is pretty much elimination of code. In compositing mode, don't call APIs that draw right away. Don't call draw one control the draw control in current port. They just don't make sense. Compositing mode is all about invalidation, deferring drawing until later. So anytime you call draw one control, you're defeating the purpose of HIView and you're slowing your application down.
Auto embed control is another API you want to avoid, but that's mainly because it doesn't make sense really in compositing mode. It uses a different notion of coordinates and it tries to figure out heuristically what the right, you know, And it tries to figure out heuristically what the right, you know, what the proper parent view would be. And it was really there for dialogue manager compatibility. So just avoid it. Instead, use an API like HIView add subview and figure out the parent. You'll usually know what the parent is anyway. Just associate the child with the parent.
So there's also a few steps from the views perspective, and I want to cover them individually. So the first thing you can do is test to see whether compositing mode is on for that window. You don't have to do this, right? Some people write HIViews just to work in compositing mode. In fact, that's what I did in that last demo app. I knew I was only going to use it in compositing mode, so it was really easy to write. I didn't bother testing.
But if you're going to rev to compositing mode over time in one window and then another and another over a couple of your application's releases, you may find it useful to package your custom content up in views that can work in either compositing mode or non-compositing mode. So if you want, you can take a look at the views owning window, see whether the compositing mode attribute is on, and if so, you can behave one way in compositing mode and another way in non-compositing mode.
The next step is to make sure you invalidate appropriately. Don't draw on those state changes like I was talking about. We send out a handful of Carbon events when appropriate state changes occur in various views. We tell you when a view activates or deactivates, when the highlight part changes, when it enables or disables, or when its value changes. We also send out Carbon events any time a view's bounds change, so that you can make intelligent decisions about invalidating exactly the portions that need to update. This is really useful for a list, like Data Browser.
When Data Browser resizes, it knows all the stuff in the upper left-hand corner of itself doesn't need to get redrawn. So it'll just invalidate sort of the L-shaped region that gets revealed, and maybe a few other bits here and there, and just those areas get repainted. So when you get one of these notification APIs, you just inform the HIView subsystem what parts of your view are dirty. If the whole thing is dirty, you can just call HIView set needs display, pass the view, pass true. Marks the whole thing as needing repainting, and the whole thing is not.
Marks the whole thing as needing repainting, and the whole thing will get repainted later. But if you know just some subpart needs to get redrawn, you can use an API like HIView set needs display in rectangle. And there's a couple other variants of that API, so you've got more fine-grained control.
The next step is to switch your view over to draw with Quartz. You pretty much have to draw with Quartz in compositing mode. Now, that's not literally true, but that's really the recommended solution, and it's the only way you're going to be able to get the free resolution--well, I can't say free--the low-cost resolution independent support, which I'll talk about later.
But the key here is that you only draw on a K-Event controlled draw handler, like I mentioned before. And that event comes with the core graphics context ref that you're supposed to draw with. Don't try to manufacture one for the Windows port. It won't be clipped right. It won't necessarily be transformed right. Just use the one in the event. It's there for a reason. Draw with it. Once you've done that, you can start leveraging whatever Quartz features you want. You know, start taking advantage of setting the transparency on the context. Use Bezier. Use whatever you want.
But the key to remember is never erase. Only draw. You can count on the fact that the HIView subsystem has painted the views behind you already. That's already taken care of. And if you want to make a view that is aqua-compliant and aqua-savvy, we have a bunch of HIView subsystems that are already painted on the screen.
And if you want to make a view that is aqua-compliant and aqua-savvy, we have a bunch of HITHeme drawing primitives which are designed to be used with core graphics or with a core graphics context. You can take a look in HIToolbox/HITHeme.h for a bunch of different ways to draw the standard button looks, the standard tabs looks, and things like that.
So that's step four. The next step is maybe the most gratifying step, because it lets you toss out all kinds of code from your application. That's switching to runApplicationEventLoop. So over the course of time, you put the standard window handler on your various windows, you switch them over to compositing mode, and you finally just finish the last window.
So now you look at your waitNextEventLoop and you say, wow, this isn't doing a whole heck of a lot. I'm tracking the menus here, and I'm calling aeprocessAppleEvent, and that's about it. Well, runApplicationEventLoop can do that for you and allow for more future growth as well. So take your waitNextEventLoop, toss it away, put runApplicationEventLoop in its place.
Now one thing that waitNextEvent also did that a lot of you rely on is give you periodic idle time. The problem with doing that through waitNextEvent is you had to sort of centralize your notion of idle time, and your objects had to communicate with waitNextEvent. You had to find the smallest time slice that was needed by any given object that wanted to animate it. It was just a big mess.
So instead, we recommend using CarbonEventTimers. CarbonEventTimers are an easy way to get either periodic processing or like one-shot deferred processing off in the future. And it allows you to encapsulate all of your code with an animating view right there in that view. That view doesn't need to know it's operating under waitNextEvent or runApplicationEventLoop or whatever.
Similarly, WaitNextEvent also allows you to detect mouse movement based on a region. Well, again, that had the same kinds of problems. Your views that wanted to communicate with WaitNextEvent had to know WaitNextEvent was there. You had to figure out the minimal region, and it was just a big mess.
So instead, a couple of releases ago, or maybe last release, we gave you tracking areas, mouse tracking areas. And these tracking areas are designed to manage a window-relative mouse region, and you'd get informed of enters and exits into that region. So it's a very handy way of doing something like rollover or cursor changes.
So once you've switched over to RunApplicationEventLoop, the thing you need to realize is that RunApplicationEventLoop will not return until it's quit. And the way you quit it is with QuitApplicationEventLoop. So you put that in an appropriate place in your application, probably like your QuitAppleEvent handler or something. And if you have various places in your app where you call modal dialog or you call waitNextEvent in the fashion where you're trying to simulate modality, the way you would do that now is with RunAppModalLoop for window. You can pass that API a window. It will make the window automatically modal. And it quits similarly to RunApplicationEventLoop. You just call QuitAppModalLoop for window.
So that's all five steps. Once you take advantage of that, you should be able to transfer your application so it looks something like this. Really make it modern, really make it great, start taking advantage of other new Toolbox features, and be prepared for the future. The future might be something like Resolution Independence. Hey, speaking of that. So, Resolution Independence is kind of a big, big subject. So let me see if I can explain better with a demo. It's very visual, so that should do the trick.
So maybe I should give a little bit of background about resolution independence first. So we don't really have high DPI hardware yet. We know that the industry is moving that way over the next couple years. And we'd much rather have applications that are going to be ready for that hardware when it becomes available. So we don't want you waiting to start thinking about resolution independence until after the hardware shows up. We all know the hardware is eventually going to show up. So let's start thinking about these topics now. Start considering how you can adopt resolution independence in your application.
All right. So I've got this app. It's called Resolution Modes. Right now, it looks pretty normal. These are three windows. The windows are basically identical, except for one slight change I made to each of them in Interface Builder. So that's how they look now, but if you launch Quartz Debug and you go up to the Tools menu, there's a new item called Show User Interface Resolution, which gives you a little slider with a multiplier on it.
If you change the value on this slider, it multiplies the resolution that apps will run in. So if I move it up to 1.25, relaunch my resolution modes application, you can see that the windows got a little bit bigger. But you don't have to stop at 1.25. I'm actually going to go really high here. So you can see it a little bit better. OK, so this window, which is titled Magnified-- let me see if I can get these other windows out of the way.
This magnified window has not been modified at all. In fact, this application's core event processing has not been modified at all. And this is the result. Sure, you get a bigger window, but it looks a little grainy, right? I don't know if you can see it on the big screen, but the magnified text is a little blurry. It certainly looks like it's been blown up. Well, that's because it has been blown up.
Magnified mode is what you will get by default for any non-savvy window in your application. How we do this is we tell the Windows server, look, this is a window that doesn't know it's in resolution independence, or that it's under a scale factor. So please blow it up for me. Your Windows backing store is still very small. So when we blow it up bigger, you get kind of grainy.
Now, there are two resolution-independent savvy modes you can set on a window. First one I want to talk about is Framework Scaled Mode. Now, obviously, this window looks a lot better. You can see the text in the text area is a lot crisper. The picture still looks kind of crummy, but where are we? Can I not switch? Oh, we have bad things happening. Oh, we have really bad things happening. All right. Cool. All right, I've got to reboot the machine from back here.
And I can't get to the front. Alright, well, strictly speaking, the demos are not necessary, so I'll just, uh... I'll just cover more detail here, I guess. So Magnified Mode. Magnified Mode is the fallback case. This isn't really what you want to have your application look like, but it'll work in a pinch.
It'll allow users to get something reasonable with your application, even if your application has not revved for resolution independence. Like I showed, it looks kind of crummy, particularly compared to the other mode, but it doesn't take any effort on your part. Now, unfortunately, I didn't actually get to show this, but David's going to try to troubleshoot it, so maybe I can. The next mode is Application Scaled Mode.
This mode is not the second window I showed. It was that little small one that came up. Application Scaled Mode is a mode where you do all the work. Well, you do most of the work. The benefit of Application Scaled Mode is that you can actually use Quick Draw in that mode, unlike the third mode.
So when you specify you want your window to work in Application Scaled Mode, you're responsible for making all parts of that window bigger, except subcomponents of things driven by the toolbox. So if we can get this back up, I can show you what I mean. The Application Scaled Mode window was still at the same size as it was when the scale factor was 1.0, but the title bar was a little bit bigger. Well, the title bar was bigger because it's handled by the system, and we know to apply the appropriate multiplier to it.
And another thing you could have seen there is that the text was a little bit bigger. We know the system font, when somebody requests the system font, isn't just 13 pixels tall anymore. It's 13 times some multiplier tall, so we made it bigger. But the problem was, all the views inside that window that the application put there were still the same size, and that's because that app hadn't modified yet. An application that uses Application Scaled Mode on its windows is responsible for sizing the window content area bigger as well, and then you resize all the views inside of it.
This is the price you have to pay for using Quick Draw. Now, we really would prefer you to not use Quick Draw. If you can just switch over to HIV, switch over to drawing with Quartz, you can take advantage of Framework Scaled Mode. Cool thing about Framework Scaled Mode is you get a really good appearance, and it's really, really easy to do. The second window that showed up there that I was comparing the magnified window to, that was an Application Scaled Mode.
The only change I made to that window relative to the magnified mode is I went into Interface Builder and changed the value in a pop-up menu to say, "Please Framework Scale this," and you get all the rest for free. Events still get routed right. So speaking of event routing, this is something I need to show off there.
That was one application, had three windows, and each of those windows was in a different mode. Now, we don't necessarily know how that application is getting events. It might be in a wait-next-event loop, it might be in run-application-event loop, who knows? It may be savvy, it may not.
So what we have to do is, since by default we magnify windows, we need to make sure that all global coordinates that come through APIs, that get passed into APIs, and that come out of structures like an event record, all map to the coordinate space that a magnified window uses. So essentially what we do is we magnify all the coordinates that come out of those sorts of APIs.
Now, this is still a little bit of work in progress here, and I've got another slide that talks about this, but what we're going to try to do is make it so that APIs that talk specifically about a window that we know is either in application scaled mode or framework scaled mode can be used with coordinates that are not magnified. So we're going to do as best we can there.
So really, my point here is that these coordinates may be coming back in a way that at first feels a little counterintuitive to you, but it'll make sense eventually. And we have a bunch of APIs that can help you convert points from the sort of magnified sense of global to the true pixel sense of global.
Okay, so to turn on resolution independence for your application, start with a window. Go into Interface Builder. There's now a little pop-up menu there that says Scale Mode, I think. It's got three choices: Magnified, App Scaled, and Framework Scaled. If you can't use the pop-up menu and you need to create your windows programmatically, we've got creation attributes for those, of course.
Once you do that, your application may or may not want to take advantage of that knowledge inside your code. Obviously, if you're in application scaled mode, you have to take advantage of that knowledge because you're required to do a bunch of work. So you can determine a window's scale mode at runtime by calling hiwindow.getScaleMode.
It passes back whether or not the window is running in app scaled mode or framework scaled mode or magnified mode. And additionally, it hands back the scale factor in case you need to do multiplication to figure out an appropriate size for a view. But if you want the user scale factor without a window, we also have a convenience API for that.
And like I mentioned before, we have a whole bunch of coordinate conversion APIs in HIGeometry.h, which were originally intended for use with resolution independence. But they actually end up being really cool because they allow you to do things like translate between a view and window coordinates, or a view's coordinates in one window with a view's coordinates in another window, which in rare circumstances might prove useful. But I found a couple of cool things to do with those.
Oh, the other cool thing about these conversion APIs is up until now, the toolbox didn't have a great way, like a one-stop shopping way, to take a global coordinate and convert it to a window coordinate. Most of the time, I think we recommended people convert that point to the window's port coordinates, and then you can call QD local to global and stuff like that. It was a little bit nasty. So now this set of APIs can also achieve that for you. So hang on. Is it good? Bad? Bad. Okay.
All right, so first thing to realize, and the demo would have shown this as well, is resolution independence for Tiger is a work in progress. It's intended as a developer-only feature. You should not ship your applications with this stuff turned on. Things could even change, right? We're not done developing and engineering it internally, so we could have a few new paradigms here and there. We just don't know yet. The least ready mode is application scale mode, right? I did magnified mode first, and that was really easy to do. And then we did framework scale mode, and that was pretty easy to do.
Application scale mode is taking a lot more change throughout the Toolbox, so we'll get there. But it's not quite ready for prime time on the Tiger seat. But still, I encourage you to try it out. You'll see very obvious visual artifacts. Just blame the Toolbox, keep moving forward, see if you can continue exercising the APIs and developing on it.
But probably the coolest thing here is, unlike previous WWDCs where we've given you this little window of time saying, "Please give us your feedback," like, in the next two days so we can implement it. This, because it's not going to be a user feature for Tiger, we actually have a little bit window, a little bit bigger window of opportunity to hear your feedback.
And this is really important, because there's a huge variety of applications out there, particularly the ones that are going to need to use Application Scale mode. And I need to hear from you what sort of things we could do with convenience APIs, what extra support you could need. You know, maybe you need a few new APIs to help with, you know, blowing up your--blowing up a window after you instantiate it from a nib or something like that.
So please, try this stuff out. Start making a little brainstorm list of things we might be able to do for you. Write bugs, contact us on the mailing list. I want to hear your feedback. We also have a document referenced by our HIToolbox release notes on your developer seed, which is Carbon Resolution Independence Notes.
It takes advantage of some knowledge that's also in a sort of overarching resolution independence document that's also in the release notes. So I encourage you to read both of those. It's going to go into more detail. In fact, it'll cover some of the stuff I would have been able to cover in the demo. So please check that out.
All right, so the rest of our time, we've been doing things that you've asked us for. We pay attention to the mailing list. Obviously, we have Eric the Android on the Carbon development list, responding to messages as soon as he can. And we hear what you ask for.
And we've done a few of those things for Tiger. The first thing, which I think is the coolest, is that we actually now have text notifications that you can get one-stop shopping knowledge whenever a text in one of our Unicode fields changes. Doesn't matter if it was pasted in.
Doesn't matter if it was typed in, doesn't matter if there's inline input, who cares? You just get this notification. Similarly, there's the text should change in range event that's sent out so you can have one-stop filtering. You can filter the text, you can change it, block it, whatever you want, doesn't matter how the text was put in there, you can do it.
And we send this to all of our Unicode-based edit fields. It doesn't work with the old text edit, or edit text control, but everything more modern than that. The search field, the combo box, the HITextView, the Unicode edit field, works with all of those. But there's a catch. That's not really a big catch. That's probably a little too scary to say. On the seed, it doesn't quite work right for a few edge cases. So while I encourage you to try this out, please, you know, start switching your applications over to it.
Take a look at the release notes, see those couple edge cases where we know there's a problem, and, you know, it'll save you time from having to write the bug and we'll say, "Oh, no, no, we already fixed this, we already know about it." So just take a look and see what those edge cases are. But other than that, I encourage you to adopt this as soon as possible.
Now we also expanded the set of HIView APIs. Our first set wasn't really complete. I mean, it was functionally complete because you could always rely on the control manager equivalence. But what we did is we put together a whole new--another set of HIView APIs to sort of fill in all those missing holes. Well, all but one percent of those holes. But still, we've got APIs like HIViewSetValue, HIViewCountSubViews. So your code is a lot more readable.
And in addition, we took the chance to modernize a few of the APIs so that, you know, there was some clunky mechanisms in the control manager side. We smoothed those over in the HIView equivalence. The coolest part about this though is that we had a big request to give back latent state information about our views. I'm sure some of you have switched on compositing mode and you have an invisible window and you have a view that you just called HIViewSetVisible. True, you know it's visible. It's definitely logically visible.
But if you called HIViewIsVisible, you get back false. Well, that's because the compositing subsystem tells you a view's physical visibility, not its logical visibility. So now we return the latent states. We have a new API to deal with the latent visibility. We had to do a new API for that one because we already had HIViewIsVisible, which obviously just returned the physical visibility.
But we added two new HIViewIsActive and HIViewIsEnabled APIs, which tell you the physical enabled and active state as well as the latent states. So it should make dealing with some of your edge cases a lot better. We also had a request for more fine-grained control over exactly which windows in your application were supported by Expose. So we have the HI Window Get and Set Availability APIs that allow you to control whether or not a window gets handled by Expose.
Now, I know I mentioned tracking areas already. And I'm sure some of you were thinking, yeah, yeah, right, tracking areas. They're a big pain. I try to use them with my HIViews. But HIViews are parent relative. And when they move, I don't really know it. And then I've got to try to figure it out, which means I have to install hand event handlers. Anyway, we know. We know. We've had to do it ourselves. It really sucks. So what we did is we made HIView-based tracking areas.
Exact same concept, very similar usage model, except the tracking areas are bound to a view. Now, we know when a view moves in a window relative space. So we can automatically adjust the tracking area for you automatically. Automatically do it automatically? Anyway. So we send Carbon events directly to the view whenever the mouse moves in or out of that view. So they're really easy to use. Suggest you adopt these wherever you can. They're a great way of doing rollover or mouse tracking.
Other people that started adopting compositing said, "Compositing is great, until I try to scroll a view greater than the Quickdraw coordinate space? That doesn't make any sense. It's supposed to be quartz-based." Yeah, yeah, I know. The internals of the control manager used to be based on Quickdraw coordinates, even though we supported this cool compositing mode. And unfortunately, that was prevalent throughout various events. We sent events to views saying, "Tell me your structure region." Well, that's a Quickdraw region. Quickdraw regions are inherently limited to 16-bit integer coordinates.
It wasn't so good. So what we did is we came up with HISHAPE. HISHAPE now has full integration into the HIView subsystem. You can now communicate region-type concepts in a way that breaks the 16-bit Quickdraw limit of coordinates. HISHAPE also allows us to improve performance throughout the HIView subsystem. We don't need to do as many translations back and forth from Quickdraw coordinates anymore. more. So it's a boon there.
We also added a few new accessibility APIs. For those of you who got a chance to see the accessibility sessions and the voiceover sessions, these should help out a lot. We have a very easy way to add extra attributes to an existing element, like, say, a push button.
You want to give it a description, you can use the SetAuxiliaryAccessibilityAttribute API to do that really easily. In addition, those of you who are implementing accessibility for your own objects, but you need to use the standard roles and you want the standard role descriptions, we have a way that you can query the standard role description that the system uses.
We also have a way that you can easily override the accessibility hierarchy provided by an object. This is kind of an edge case. I bet most of you won't need to use it, but if you do run into one of those situations, we have an API that can help out.
And finally, one thing we realized when we were working with Finder to provide accessibility information for their ListView is that, you know, Data Browser is really great, but-- and Data Browser's accessibility implementation is really good too, but we need to interject some extra info in there, right? What is just a row in one application might need to be called something else in my application, so I want to be able to customize what Data Browser provides.
And so we have a set of APIs that allow you to modify the information sent out or passed into Data Browser for any given element. It's pretty cool. Take a look at controldefinitions.h for that. In fact, let's see, that was this morning. Right, so if you want more details on this, see if you can travel back in time a few hours and check out session 424. We covered a lot of accessibility. information there.
One thing we did to the toolbar to kind of expand the possibilities for what you can do is allow for per window enabling and disabling of toolbar items, as well as selection of toolbar items. So what I mean is you can have two windows that use the same toolbar object, you know, they both bind to the same toolbar, but you want, let's say you want the delete button in the toolbar to be enabled in one window but not in the other.
Well, up to now, there wasn't really a way to do that. What you ended up having to do is create two different toolbar instances for each, or one for each window, and that was kind of a pain. So we built in per window selection enabling and disabling support.
Now the cool thing is that the Toolbar can manage the selection for you automatically. All you need to do is have your Toolbar delegate respond to a Carbon event that says, "Hey, tell me the selectable items." If you respond to that, the Toolbar can manage it for you automatically. It'll just track the selection, change the selection, send out a Carbon event when that selection change has happened that you can listen to if you need to.
There's lots of other stuff that I just don't have time to cover today, though since my demos crashed, I could probably talk about stuff. Let's see. Let's see what I can do here. HIobject archiving. I don't think this is actually turned on. Is that true? Yeah, unfortunately, there is a runtime problem. We couldn't actually turn this on. But essentially what HIobject archiving is, it's a way to take any HIobject, which means a window or a control, and say, hey, flatten yourself out to a stream. I want to write you someplace.
And it grabs all the information from those views. And you can hook into this. This is achieved through new Carbon events that get sent to views in Windows or any HIobject, really. And you can hook in and provide your own data into this flattened stream. So you can store it out wherever you need to.
We also expanded the hotkey APIs. Let's see, some cool stuff for the ComboBox. Ah, yeah, the ComboBox, we were asked by you guys for better control over when the list is shown and hidden. You know, you want to programmatically invoke it and not just have the user click on it. So we gave you that. There's an easy way to make the list show up.
The search field, we were seeing places in the interface where people wanted a search field with an icon on both sides of the search field, but they didn't want that left icon to actually bring down a menu. They just wanted it to act like a button. So you can do that now in the search field. We added a bunch of new menu glyphs to the menu manager so you can support those if you want to.
Eric did a ton of work in Carbon to kind of give us a lot better integration between Carbon and Cocoa Windows. If you've ever tried to integrate them yourself or use system services that did the integration for you, like the color panel or something like that, you notice that sometimes there were some activation problems and it wasn't clear which text field was focused. Well, we did a bunch of work in that realm to clean it up. And we did it by using proper window levels.
So there are some things we changed with respect to window levels. I think we always used to use window level zero. And now we actually use the proper window level so we can interact with the Cocoa Windows better. We cover this in the HIToolbox release notes in detail.
I urge you to take a look at it just in case there's any compatibility concerns there. But see if there's any of this new functionality you can take advantage of with the new APIs we offered. We also sent out a couple of new Carbon events as--. I think it was sheets open and close, I believe.
Is that right, sheets open and close? I'm looking for Eric. I don't see him. Okay. No. No worries. So like I said, HIToolbox release notes are the key. It goes into more detail about everything I've talked about already, as well as the stuff on this slides and even more.
But there's three other things I want to talk about. These are sort of compatibility concerns. These are changes we made to Tiger that may affect your ways--your application in ways that you don't exactly anticipate right now. The first change was to support resolution independence. I don't know if you can remember, but the machine before it crashed had a bigger menu bar when I increased the scale factor.
Well, that was achieved by switching the menu manager over to using a compositing window for the menu bar. Now, since it's a regular old compositing window, this window is going to show up in the window list. So if you're calling APIs like getWindowList or getFrontWindowOfClass, the all classes constant, you're going to get the menu bar window.
So you got to be prepared to expect that. But better yet, don't use either of those APIs. We don't prefer those anyway. What you really want to use is active non-floating window. That generally gives you the front most document window, which is what you want nine times out of ten anyway.
Another change we made is we brought the user pane control sort of up into the HIV space, so at least it can be used as a grouping view, but that required a few changes. One of the changes we had to make is we no longer store its private--did I say private? Private, that's stuff internal to Apple, instance data in the controls data handle.
We found a few applications out there that access that data, or at least query it, and we had to do some weird workarounds to make those applications continue to work. So, don't ever access the private data of a system control. In fact, don't ever access the private data of any control you're not in charge of writing. It's a bad idea, and don't even to reference it.
Now the third change, which is something we haven't made yet, but it's something we're going to do by the time Tiger ships. There are some APIs like Event Avail, Still Down, and Wait Mouse Up that currently flush window buffers, meaning you could do some drawing in a window and then you could call Event Avail and that drawing would be flushed to the screen right away.
That's kind of convenient, but it killed performance in many ways. A lot of people use Event Avail or Still Down as part of a very tight mouse tracking loop. And so any painting that happened was either a visual inconvenience because they didn't want it to happen or it just slowed down that tracking loop, which is kind of crummy.
So by the time Tiger ships, we're going to make changes to those three APIs such that they no longer automatically flush window buffers with the big parentheses, which is only for applications linked on Tiger and later. So if you built your application on Panther and you run it on Tiger, those three APIs are still going to flush the way they always have. It's only if you link on Tiger and then run on Tiger.
So if you find some situations where suddenly, wait, my window's not drawing, I'm expecting it to draw, what's going on? It's probably because of this change. Look to see if you rebuilt for Tiger and surely that's going to be what's going on. So if you really depend on this behavior, what I suggest doing is find those tracking loops where you depend on the behavior and make manual calls to QD flush port buffer or something like that, and that'll get the flushing behavior back. But maybe a better solution is to stop doing your own tracking loops in those places where it is a tracking loop and instead switch to an API like track mouse location, which does flush because it's documented to flush.
So, what I want you guys to go do now, well maybe not right now, because I need to attend the next session. Install the seed. Take a look at the toolbox release notes. Start looking at the new things we've added. Start playing with HIV if you haven't already.
Try switching on compositing mode in the standard handlers for some of your Windows. You may actually find out that it's a lot easier to adopt this stuff than you may have thought. Then take a look at the resolution independence release notes. There's a lot of cool info in there. It's really fun to play with.
I mean, even if you don't start developing for it right away, turn it on, run your application, see what looks good, see what doesn't look good. Start making lists of things you might need from us, from the toolbox. And then maybe start fiddling with it in your app and see if you can't make it work.
It might actually end up being easier than you thought, particularly if you've adopted HIV. Also start looking at supporting accessibility. If you're supporting HIV, it's just a tiny additional little step to support accessibility. If you're in there and you're modifying your nibs, hey, by all means, when we get support in IB for adding extra attributes to widgets, sorry, extra accessibility attributes to widgets, start throwing that information in there. It's only going to do good.
And then take a look at all the other Tiger features we've added and see what you can do to make your application look great. So we've got a bunch of information out there on the web and in the release notes. The top item here, this upgrading to the Mac OS HIToolbox, is one of the featured articles on the developer Applecom website. It's really, really good. It's a great article. It covers all kinds of aspects of HIView and HIToolbox. A little bit of older documentation is introducing HIView.
It takes you through some of the basics. If you need a little refresher course, go check that out. We have the reference material for HISHape. It's going to be critical to start using HISHape once you become HIView, because it's going to break various quick draw limitations. And finally, the release notes. Did I say release notes enough? So now I'd like to bring Xavier back up and the rest of the Toolbox crew, and we can go through some Q&A.