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: wwdc2004-424
$eventId
ID of event: wwdc2004
$eventContentId
ID of session without event part: 424
$eventShortId
Shortened ID of event: wwdc04
$year
Year of session: 2004
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC04 • Session 424

Developing Accessible Applications

Application • 1:12:41

This session will cover the best practices for ensuring your Carbon or Cocoa application works well with the Mac OS X Spoken Interface. Carbon developers will learn methods to easily add support for the Accessibility API into existing applications. Cocoa developers will take away techniques for adding accessibility support to custom controls. This session is invaluable to developers who want to make their applications accessible to all users.

Speakers: Mike Engber, 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.

Good morning. I'm Guy Fullerton, manager of the high-level toolbox, Xleaf Node. Hello, my name is Mike Engber, and I'm an engineer in the Cocoa group. And just to make things more confusing, we're going to change sides now. So hopefully everybody got a chance to see the voiceover session yesterday. And hopefully you got a really good feel about how important accessibility is to Mac OS X and how powerful it can be.

So in this session, we're going to build on yesterday's session and describe some of the implementation details you need to go through with your applications to make them accessible. We're going to give you a brief architectural overview of how accessibility works. We're going to talk about how to accessorize two types of widgets. We're going to tell you how to add stuff to a standard widget, augment it with a few extra things you might want to augment it with. And then we're going to tell you how to accessorize a custom widget that derives from one of our standard ones.

And finally, we're going to have a long section on troubleshooting and giving you tips and tricks for accessorizing your application. So I think Mike's going to start out with the conceptual overview. Okay, so before we get into the details on what you need to do to make your applications work with accessibility, I'm going to give you a little bit of background on the Accessibility APIs.

So when we talk about the Accessibility APIs, there are really three things to consider. First, there's the Accessibility APIs themselves. And in this slide, they're shown with the green box. And these are the APIs that we introduced back in Jaguar and that assistive apps use to examine the user interface and interact with other applications on the system.

The second part of the story are the assistive apps themselves. And those of you who went to session 424 yesterday got to see a prime example of an assistive app, which was VoiceOver. And the third part of the story are the Cocoa apps and the Carbon apps, which the assistive apps examine.

And that's what we're going to be focusing on today, specifically what you need to do to make sure that your application works properly with the Accessibility APIs. Now one thing to bear in mind, even though we talk a lot about VoiceOver as being a prime example as an assistive app, nothing in this presentation is specific to VoiceOver. The things you do here are going to enable your application to work with all kinds of different assistive apps that have been developed for the Macintosh OS.

Now this slide is an abstract representation of an assistive app trying to examine the user interface of either a Cocoa app or a Carbon app. And the question marks show one of the key design problems that we had to make a decision on when developing these APIs. How are the applications going to represent their user interface to the assistive app? Now, an obvious choice would be to use the natural way each application implements its objects. So in a Cocoa app, that's NSWindows and NSControls. And for a Carbon app, that's WindowRefs and ControlRefs.

But the problem with doing it this way is it puts an extra burden on the assistive app. You have to do twice as much code. You have to have one set of code that knows the Cocoa objects, and then you have to have another set of code that understands the Carbon objects. And the situation only gets worse if we introduce other frameworks into the mix.

So in this slide, we outline our solution. And the basic way we represent the user interface is through something called a UI element. Everything's a UI element. Windows are UI elements. Buttons are UI elements. Even the application itself is a UI element. So both Cocoa apps and Carbon apps are going to return to the through the Accessibility APIs their UI represented as a bunch of UI elements.

So, to summarize, a UI element is an abstract object we use to represent a part of the user interface. And the applications represent themselves as a whole hierarchy of UI elements. Assistive apps will look at a UI element which has attributes. And the assistive app can use these attributes to get information about the UI element. For example, its title or its value, or it could use the children attribute in order to start traversing the hierarchy of UI elements.

UI elements also support actions, and these are what assistive apps use to actually drive applications. For instance, to press a button or to increment a slider. So to sort of bring these concepts alive, Guy is going to demonstrate one of our tools called Accessibility Inspector and do a live demonstration of inspecting one text edit. Yeah, so if I could have the second computer please.

Perfect. Thank you. All right. So what I have running here is TextEdit, and I've got a document open that has just a little bit of text in it. And I'm going to run the Accessibility Inspector application, which is part of the developer's Tools install. And when I run the Accessibility Inspector, you get this floating window that describes whatever the mouse is over. Now, by default, the font size is really small, and I can see it fine, but something tells me you won't be able to read it unless I make it bigger.

That should be okay. So now as I move my mouse around the screen and point at different things, the Accessibility Inspector updates with information about each of the elements that I roll over. So if I stop on this text area, for example, you can see the hierarchy that contains that text area.

It's inside the application TextEdit, inside the window Document, inside a scroll area, inside a text area, and then it's a link. As you can see in the Attributes section, there's a number of attributes it supports. It's got a role, ax-link. It has a role description, which looks like it's a bug, to tell you the truth. It's got a sub-role, and it's got a parent, various other pieces of information about it.

And of course, the attributes that an element supports varies based on the element. So if I go and hover over a push button, you can see a different set of attributes, and even some actions in this case. One cool thing that the Accessibility Inspector can do is lock onto a UI element.

So if I press F5, you see the text in the Inspector go red, and you see another floating window appear. And now when I move the mouse, the text in the Inspector no longer updates. So I'm locked onto the UI element, and I'm locked on that push button, which lets me do a couple really cool things.

The first thing is I can potentially manipulate other parts of the UI with my mouse without disrupting what the Inspector's showing. But the second cool thing I can do is potentially go up and down the containment hierarchy. In the second window that shows up, there's a Go To menu, and I can choose other things from within that Go To menu.

I can go to the button's parent, which is the Preferences window as a whole. Now this is really important if you need to get to an element in your application because you don't really have much of a visible UI widget that you can point at, but you can point to some child of that.

So if you can point to a child of it, you can continually traverse up the parent hierarchy until you get to the element you want. Not only can you go up, but you can also go down. So now that I'm looking at the Preferences window element, I bring up the Go To menu, and I can see that there's a list of children.

So I can go to any of these groups, and from within those groups, go into the children of the group, and so I can find all kinds of different things out about elements. So let's go ahead and get back to the button, real quick. I want to show you another cool thing you can do.

So also in this secondary window, there's a pop-up with all of the-- let me bring this up a little bit-- all of the attributes that the button supports. So you can choose one, and it changes the little value in the text area here to represent the value for that attribute. Now this doesn't show it really well. Let me see if I can find one here. If I lock onto a text field, Indeed, I am in a text field and I choose the value.

Actually, when I pop up this menu, you can see that there's a W next to the AX value attribute. That means the value is writable. So now, when I choose value from the pop-up, you can see that the text is now writable inside the secondary window and I can change that to say 80, press set value, and you see it actually updates in the interface.

Another cool thing you can do with the secondary window is click the highlight checkbox. This is an easy way that you can test to make sure the size and position you're returning for an element is correct. So I've locked onto this text field and when I click highlight you get that pink window that shows what you're highlighting or the bounds of the thing you're highlighting.

And in fact highlight sort of stays on once you've clicked it. You can click it again to turn it off. But the cool part about that is you can hit F5 again to unlock, go point at some other object, and it now highlights that other object. So it's really good for testing those kinds of problems, bounds related problems.

Let's go back to the button again because there's one more thing I want to show you. Don't need the highlight anymore. Alright, so I hovered over the button and it's revert to default settings. And you see that I had already changed the window width to 75. So I've already varied from the default. Another thing in the secondary window is a set of actions that an element supports. Buttons support a press action because you can press buttons. And the cool thing about the palette is I can perform that action.

And when I perform that action you see the revert button actually flash and then the window width gets updated back to 75. So Accessibility Inspector is a really powerful tool. Probably the most common tool you'll use when you're accessorizing your application to test that you're doing things correctly. So I'd like to go back to slides now.

So that's a brief overview of one of the tools. So now it's time to dive down into the nitty gritty of how you can sort of implement this in your own applications. But some of you are probably wondering, how hard is this going to be? So we have this fancy little chart here that can illustrate that for you.

OK, colors and jokes aside, the basic principle is the more standard stuff you use, the far easier it is to become accessible. The more custom stuff you use, the more work you have to spend making it accessible. And that's just, you know, it's regardless of whether you're a Cocoa app or a Carbon app.

For example, if you're a modern Cocoa app or a modern Carbon app that's based on HIV and compositing, it's really, really easy to accessorize that application. But most apps can't use just the standard system widgets, right? Most apps have a few custom things. And so those apps, you know, they'll need to do a little bit more work to accessorize those custom widgets. And that's not too tough either.

The real problem case is when you're at the far other end of the spectrum and you're a completely custom app doing all kinds of old stuff. Let's say you're on a really old class library and you ported your application to 10 and made it run, but it's not using the very modern technologies of either framework.

Well, that's going to be really challenging to accessorize because you're pretty much going to need to add accessibility information to everything in your interface except for the few standard widgets you use. So the take home is really maybe this is the time not just to accessorize but to adopt other Apple technology like HIV in your application.

Sure, it's going to take a little bit of effort to adopt HIV, but that effort is going to pay off in two ways. You're going to get a bunch of other benefits from HIV, and of course it's going to make your accessibility implementation a lot easier as well. So you should seriously consider that when you're accessorizing your apps.

Okay, so I showed you some attributes in the Inspector, but let's talk a little bit more about some of them. So fundamentally, attributes provide information to an accessible application about the elements in your app. And an assistive application will want to make four main requests of your app with respect to its attributes. Given an element, an assistive app needs to know what attributes does that element support.

It might want to get the values of certain attributes. And of course, it'll want to see if an attribute is settable. And if it's settable, it's going to want to set the attribute. Now all of the attributes, at least all the standard attributes, can be seen in the HI Services header I've put on the slide.

But when you open that header, don't be scared by the sheer quantity of attributes in there. There's a lot. There's dozens and dozens and dozens. Don't let that make you think it's really hard to accessorize. Most elements only support a small handful of attributes. As I was showing in the Inspector, you know, push buttons have about 10. Various other things might have about 15. So it's really not that tough. So don't feel daunted when you open up the header.

So I want to talk about some of the very most common attributes that you're going to have to use and you're going to come across. The most fundamental attribute is the role. Every element must have a role. A role describes an element's basic purpose. And in fact, a role is what an assistive application uses to determine how it should present this interface to the user in some other way. VoiceOver, for example, when it sees a button, it knows the button's got certain properties associated with it. And it knows it can communicate with the user in a certain very specific way about the button.

However, an assistive application can't use that role as sort of a spoken description to the user about what the element is, because mainly the role strings are prefixed with AX, and they're just there to be sort of switched on at runtime by the application. So if an assistive application wants to present a human readable representation of that role's purpose, it uses the role description attribute, which is a localized string that can be spoken to the user or shown to the user in some fashion.

Now another really common attribute is the title attribute. It's generally obvious when to use this. The "Don't Apply" button up there, its title is "Don't Apply." The checkboxes in the other window, the title's right next to them. Those are pretty easy. But the thing to realize is that the title attribute is only for visible text.

Don't try to put a title in place for a push button that just has a picture on it. That doesn't make sense. The assistive applications assume that if there is a title attribute, there is visible text associated with that element. If you have an icon button, there are other ways to provide information to an assistive app, and we'll cover that a little bit later.

Another really common attribute is the value attribute. Again, this is self-explanatory for the most part, but there's a really cool aspect, and that is that, unlike a lot of other attributes, the value attribute can be of just about any type. Of course, the type depends on the role of the element you're talking about. The checkboxes on the furthest back window there on the screen, they're going to have a value that's a number, and it's going to be either zero or one based on whether the checkbox is checked.

By contrast, the text field on the terminal info window, its value is the text inside the field, and that's going to be a CFStringRef generally, possibly some slightly different representation. But again, it's two completely different data types that depend on the role of the element you're talking about.

Now, as Mike said, elements are hierarchically organized within an accessible application. You've got an application that has, as children, probably a menu bar, several windows, and each of those windows are going to have child elements that are various views and buttons and things like that within the window.

And then, of course, the other thing that's going to be a parent element. So every element has a parent. Now, that's not technically true. There is one element that doesn't have a parent, but that's something that we write, so you'll never need to worry about that in your applications. So for your purposes, just remember that every element has a parent.

The children attribute, however, are not necessarily supported by every element. The children attribute only needs to be supported for elements that obviously contain other elements. A window is going to have children. It's going to be the views within the window. But a push button, for example, doesn't have children. It doesn't make sense because the text in the button is already represented as its title.

So Mike is now going to take you through the easiest case of accessorizing an application. Okay. So as Guy said, if you stick to the standard widgets, you're going to get most of your accessibility for free. And in fact, all the attributes Guy was just talking about are things that the frameworks, you know, either Cocoa or Carbon are going to provide for you. And there's lots of other attributes like the position and size, which he didn't go over, that the frameworks are going to provide for you.

But there are some things that we can't figure out. And the example Guy gave of a graphical button, a button that just has a picture in it, is one case where we cannot figure out what that button means. And that's what we're going to discuss in the next few slides. A handful of attributes that you're going to need to implement because there's just not enough information for us to provide them at the framework level.

So the first of the, for lack of a better name, I'm going to call these instance attributes. And that's because you have to go in by hand and for each instance of these kind of widgets, you're going to have to provide this sort of information for us. And the first one I want to talk about is the description.

Now the description is a string that describes what a widget does, but it doesn't describe what the widget is. Now let me reiterate that. It describes what the widget does. What the widget is is covered by the role. And so, for instance, a button is known to be a button because of the role attribute. And if this button happens to cause the text to be left aligned, That's the information we want to be provided by the description attribute.

So if we take a look at the DVD controller, we can see that a lot of the buttons there don't have any text. And so in this case, the assistive app is going to rely on you to provide a description of what they do. Otherwise, all it's going to know is that this is a button. And the users, you know, will be able to press it, but they'll have no way to figure out in advance of pressing it what the heck it's going to do.

Now even buttons that have titles are sometimes hard to figure out. Take a look at the calculator. All the buttons there have a title, but a good percentage of those titles aren't particularly informative. So for instance, you know, the MC button or the period button. You know, if you don't provide a description, it's going to be pretty mysterious as to what these buttons do.

So in case all this is a little bit confusing, all these attributes, let's just go over an example. Let's take the MC button in the calculator. That button is going to have a role. The role is going to be AX button, and that's provided by the framework. The button's also going to have a role description. And that's going to be button. And this is a human readable string. It's going to be localized. And again, we provide that in the framework.

It's going to have a title, and the title is going to be a two-character string, the letters MC. And once again, that's provided by the framework. And then there'll be some other attributes like its position and size that are also provided by the framework, but that's it. And we're going to rely on you guys to go in and give that button a description, which would be a string, something like memory clear.

The next instance attribute I'm going to talk about is the Title UI element attribute. Now recall that we mentioned before that the Title attribute is a string that a widget actually displays. And take a look at the lock button. The string next to it isn't part of it, so the lock button will not have a title.

But yet, there's this piece of static text next to it that a sighted user can figure out serves as the title for that lock. In order to make this relationship available for assistive applications, you need to implement the title UI element attribute, and its value will be a reference to another element that serves as its title, in this case, the piece of static text next to it.

In order to make this relationship available for assistive applications, you need to implement the title UI element attribute, and its value will be a reference to another element that serves as its title, in this case, the piece of static text next to it. In order to make this relationship available for assistive applications, you need to implement the title UI element attribute, and its value will be a reference to another element that serves as its title, in this case, the piece of static text next to it.

There's a related attribute called the serves as title for UI elements attribute. This is the back pointer. This could be useful if an assistive app allows the user to mouse over a piece of text and it wants to decide what purpose does this text serve in the user interface.

If it has a serves as title for UI elements attribute, then it will know that this piece of text is the title for this list of UI elements. The value for this attribute is an array because a single piece of text often serves as the title for multiple widgets.

Another very important instance attribute is the Linked UI Elements attribute. A common model in user interfaces is to allow the user to make a selection from a list or a table or a radio group, and then another part of the user interface reconfigures itself to reflect that selection.

Now, just because you make a selection in one spot and other widgets change, there's no way for the frameworks to sort of detect this relationship. And that's why we rely on you to give the list or the table a linked UI elements attribute that shows us that the selection in this object affects these other widgets.

Now the linked UI elements attribute is an array because often this selection affects a whole bunch of other UI elements. And it might look like in this slide that that's the case here, but it really isn't. In this case, the array would contain just one element, the tab group. Now if the tab group weren't there, then it would contain the whole host of elements you see on the screen. And I think in most cases you'll find that the linkage will be to a single sort of grouping UI element.

The last instance attribute I'm going to talk about is specific to sliders. Another common practice is to create a slider and then create separate

[Transcript missing]

All right, so now we've talked about some of these instance attributes. And the next question is, well, how do you go about implementing them? And before Tiger, the answer was you had to actually subclass the widget in order to add these attributes. And that's pretty cumbersome. And now that we're trying to encourage developers to add all these attributes, it's not a really nice story to say, well, you have to create a subclass for every widget in your user interface just in order to provide descriptions.

So in Tiger, both Cocoa and Carbon have introduced APIs that allow you to take a particular instance of a widget and associate a new attribute with it. And that's what we're going to talk about in the next few slides. First, I'm going to discuss how it's done in Cocoa.

Guy's going to cover how it's done in Carbon. And then we're going to give you a sneak preview of how this can be done in IB, Interface Builder, without writing any code. But that doesn't quite work. It doesn't quite work yet in your Tiger seed. So that's sort of a future direction. But the slides we're going to present next on how to do it in code work today with your Tiger seed.

So I'm going to start by just giving you the API in Cocoa that performs this. It's called Accessibility Set Override Value. And you pass it the attribute and the value you want it to have. And that's pretty simple. Well, it's not quite that simple. The first thing is, even though this is a method on NSObject, you can't use it on any old NSObject. You can only use it on objects that implement the accessibility protocol.

And the second point goes back to another thing I alluded to earlier. One of the big advantages of the whole scheme of UI elements is that we squish some extraneous detail out of the natural representation and we don't expose it to accessibility. So for instance, in a Cocoa app, there's a button on the screen actually consists of a button object and then inside is another object called the button cell. And the real meat of the button is implemented in the button cell and that's all that's exposed to accessibility.

So if you set the attribute on the outer button, not the button cell, it's going to have no useful effect because the button cell-- the button is not exposed to accessibility, only the button cell. And if that sounds a little confusing, I'd like to refer you to the AppKit release notes. And there's a long section that's written about how, you know, these sorts of gotchas.

Another thing that might cause some confusion is the fact that there's a couple of these methods that both have the word set and attribute in their names. Now, there's an entirely different concept in accessibility, and that is allowing assistive apps are allowed to set the values of certain attributes.

So, for instance, an assistive app might want to move a window, and it does this by setting the value of the position attribute. But that's an entirely different thing than giving the window a position attribute. The window already has a position attribute, and the giving it of it, the method I'm describing here allows you to add attributes to things. And this is something you do. Whereas the method accessibility set value for attribute, that's where the assistive apps come in, and they set the attributes values for the attributes where you allow it.

So here's a bit of example code on how you might accomplish this in a Cocoa app. We're going to give a description to a back arrow button that otherwise has no text associated with it. The AwakeFromNib method is called on the file's owner when this widget is loaded. So that's a natural place to try to do this.

And notice the first thing we do is we extract the proper thing. We extract the button cell out of the button. And then we make our call. Accessibility set override value. We pass it the string back and the name of the attribute we want to set. And that's all there is to it.

and now Guy is going to explain how this works in Carbon. So before I dive into this new API that we added for Tiger, I want to cover one other detail. And that's the notion of what is exactly accessible in Carbon. As you can see, this API's first parameter is an HI Object Ref. HI Object Refs are the key to accessibility on Carbon. Any HI Object Ref and controls, windows, menus, they're all HI objects. Each of those constructs is inherently accessible.

In fact, controls, windows and menus already have a bunch of built-in accessibility infrastructure. And that's how we're going to provide all the default attributes. So you'll see some of our accessibility APIs take HI Objects. And essentially what you can do is you can take any window Ref or control Ref, menu Ref, cast it into an HI Object and just call these APIs. So the equivalent API to what Mike just showed is called HI Object Set Auxiliary Accessibility Attribute. Wow, I actually got that out.

First thing you need to pass in is the HI object and an identifier that represents the element you wish to add the attribute to. Then you give it a name and you give it the data you want to associate. So again, same sort of example. There's a back button in your interface, but it just has an icon on it, no text. You want to give it a description. Here's how you do it. Let's say you happen to create your window from a nib. You can call hiview findbyid to look up your back button. So now you've got a reference to your back button.

Then you call this new API, pass the back button and zero. See how I've casted the back button, which is normally an hiv ref, into an object ref. Pass that back button and an identifier of zero, which says, "Hey, I want to associate some data with the element that represents the whole button." You give it the attribute you want to add and you give it the data you want to add, which is the string back. And once you've done that, the accessibility infrastructure in Carbon will take care of the rest for you automatically. It's really pretty cool.

So now I'd like to bring up Aaron Haney to give you a brief demo of Interface Builder and how it can make your lives a lot easier. Or will be able to make your lives a lot easier. Thanks, Guy. OK. So this is very similar to the demo I did yesterday during the voiceover session, but I'm going to show a few additional things.

So what we have here in Xcode is a small Hello World style app that just has a couple of text fields and a button. And right now, it doesn't have anything other than the default accessibility information in all of the UI elements. As Guy and Mike say, a lot of the standard widgets will give you a lot of the stuff for free, but it doesn't quite give you everything. So let me just show you real quick what happens if you look at this with an accessibility application.

: The spoken interface is inactive. Press Control Option F6 to learn more. Demo app application. Window accessibility demo onto edit text field. Now you can see it's just saying edit text field. In a text field. And down here. Button. That's not very descriptive. It tells you what kind of control it is, but it doesn't say anything else about it. Let me turn this off before it gets too chatty.

So to fix this, we can open the nib. And you'll notice in the info window, there's an extra item, Accessibility. So let's select these edit text fields. Now this is a case where we don't want to enter a title for this UI element itself. What we want to do is link it to another UI element and have it use the title of the one that it's linked to. What we can do is just control click and drag and create a connection and select title UI element and connect that up. Now let's do the same thing for the last name field.

And while I'm here, I'm going to take a look at this button, which is an image button, a fairly common case. As Guy mentioned, a title is for a case where there's actual text on the control. And in this case, there is no actual text, so a title is inappropriate. What we want to do is add a description. So we'll go to the description field here and type in some text.

Now the fact that it's a button comes out of the role description attribute. So you don't want to put button in this field because then it would read, OK, button, button. So we just want to put just a description of the control itself, not what kind of control it is. So let's try this again.

Spoken Interface is inactive. Press Control Option F6 to Interface Builder Application. Window Accessibility Demo onto First Name Edit Text Field. And there it is. Last Name Edit Text Field. OK button. So now with just a little bit of work in Interface Builder, we've added accessibility to this small Hello World application. Now a couple of other things that I want to show you is, An accessibility inspector. Guy already mentioned that in the locked view, let's lock on one of these text fields. And we'll highlight it just so you know which one I'm locked on.

[Transcript missing]

You can also go to any linked elements such as the title you want. And now you can see it jump over to the first name field. And from now on, whenever I update the text in that static text field, it will automatically get picked up. As far as accessibility is concerned, the title of these edit text fields will and Mike Schumacher will be presenting their presentations.

So that's it. Now we want to emphasize that the support that's on the CD/DVD is incomplete. This is just to show you the direction that we're moving in. We advise you to not use it as it is right now. We hope to get that addressed as soon as possible. But anyway, this is where we're headed. We want it to be as easy and convenient as possible so that you can add accessibility information from all sorts of places, including Interface Builder. And that's the demo. Thanks, Guy. Thanks, Aaron.

Okay, so now we've talked about the easy case and what you have to do with standard widgets. And we anticipate that's going to be the vast majority of the work that's needed by applications to support accessibility. But now we're going to bump things up a notch and we're going to talk about what you need to do for custom views.

And in the example we're going to be working through, we're going to be talking about if you created your own button class and instead of starting from NSButton or a button ref, you start from just a plain view. And Guy and I are each going to show how you do this in Carbon and Cocoa and how you give a title to this widget and a title attribute and how you'd implement a press action.

Now the first, some of you might be thinking, "Okay, we just showed you how to add attributes. Can't I just add a title that way?" And the answer is yes, that will work just fine. And if you only have a couple instances of your custom button in your app, that might be good enough.

But maybe you're providing this class for other people to use, and wouldn't it be nice if the title attribute came along for free instead of making everyone implement it? or another thing to think about is maybe it's some other attribute like the value in your widgets a slider.

The methods we just showed you take a name of an attribute and a constant value and that's not going to really work so well for the value of a slider. I mean I guess you could every time the slider changes value reset that attribute but that's not really very elegant. So we're going to show you how to get some code to run in order to implement attributes and then we're also going to show you how to do this for actions.

Now in Cocoa, there's a protocol or a suite of methods that you implement on an object to support accessibility. And there's four of those methods relating to attributes. And they correspond to the four things that assistive apps can do with attributes. One of them is to get a list of the attributes supported by a UI element. The second is to get the value of a particular attribute. The third is to test if a particular attribute is settable. And the fourth is to finally set that value.

And this code on this screen shows how you implement the first of those methods, Accessibility Attribute Names. Now here, I've chosen to cache the attribute names in a local static variable just so I don't have to compute them over and over again. And the first thing we do is check if the cache is full because if it is, we don't have to do anything else. It'll get returned.

But if we haven't computed the list of attribute names yet, the first thing we do is go to the super class and get the standard list of attributes. Then we create a new array by adding the attribute we want to add, the title attribute, to the end of the list. And finally, we tuck that away in the cache. And that's all we need to do.

All right, this is the code that supports returning the value for an attribute. The method name is AccessibilityAttributeValue. And the first thing we do is check if the attribute being queried is the attribute of interest, in this case, the title attribute. And if it is, we return our notion of the title. Otherwise, we let the superclass handle it.

And the last method I'm going to show you is the method that handles returning whether or not an attribute can be set. And once again, we test, is this the title attribute? And if so, we return no, because we don't allow users to change the title of our button.

and if it's not, we let the superclass handle it. Now those of you who have been paying attention might realize I said there's four methods. Well, the fourth method involves actually setting the attribute and since we don't allow the title ever to be set, the code for this method would always just defer to the superclass, so there's really no point in implementing it.

So from Carbon, it's a very similar protocol to what Mike described. In Carbon, accessibility is achieved through, I think it's nine different Carbon events. You can install those however you would normally install Carbon events. In this example here, I happen to be doing it in an HIA Object subclass registration time.

You don't have to do it that way. If you use Create Custom Control or Create Custom Window or something like that, after you create the control of the window, you can call Install Event Handler and install your event handlers afterwards. It's all going to work just the same. So three of the Carbon events that we have up here are the Get All Attribute Names, Is Named Attribute Settable, and Get Named Attribute Carbon Events. Again, there's also an event that's used for setting. There's some events that we'll talk about later used for actions.

There's also some events used for hit testing. We're not really going to cover that today. But there's lots and lots of documentation in CarbonEvents.h on exactly how you need to handle these events, what parameters you need to look for, and what parameters you take out. So let's dive into the example. Thank you.

Okay, so the same example Mike was talking about. You've got a custom view that has a title and you want to provide that attribute because it's not being provided for you automatically by the view system because it doesn't know your custom view has a title. So here's what your handler for get all attribute names might look like.

The Carbon event comes in. You need to call next event handler to let the inherited HIVU implementation pre-populate it with a bunch of the standard attributes, like the size and position and stuff like that, all the stuff you want the system to provide for you. Then you extract the mutable array of attribute names from the event, and you add your attribute to that array, and you return. And that lets the accessibility system know that, hey, there's this additional attribute that's been added in.

The next event I want to talk about is the Is Named Attribute Settable Carbon Event. When this Carbon Event comes in, fetch the attribute name out of the event and see if it's the attribute you support. If so, in this case, obviously the title is not settable, so we just stuff an event parameter into the event indicating that no, it's not settable, and then you return.

Let me jump back here. Something I left out of this code example. Obviously, if it's not one of the attributes you support, call next event handler or return event not handled error, and that will let the inherited implementation provide all the right information for it. So when an assistive application wants to query a value, again, very simple.

Extract the attribute name out of the Carbon event. If it's the one you support, stuff an event parameter in the event with the value. In this case, we're stuffing the title in. Presumably, the title's a CFStringRef. And the Carbon event manager's going to do all the write, retain, release semantics for CF types.

So now Mike's going to talk a little bit about actions. OK. So we've shown how to add an attribute to your custom view. Next we're going to get into how to add an action. But first, a little background on what actions are. First, actions are very simple things. They're about approximately what you could accomplish with a mouse click.

And they're generic things like pick or press. Now often developers think, "Oh, I've got a print button. I need a print action," or "I have a cancel button. I need a cancel action." And that's not the case. If it's a button, it should support the press action. The fact that this button prints can be discovered by looking at its title or its description. So don't fall into the trap of thinking you need a lot of actions. In fact, there's less than 10 actions to find and I don't anticipate that list growing very much.

The Accessibility APIs support three basic operations with actions. First, you can ask a UI element for a list of all the actions it supports, and in most cases it's the empty list or just one thing. Then you can ask to get a human readable string for a particular action, that's its description. And finally, you can try to perform that action.

Now, Cocoa's accessibility protocol has three methods that correspond to those three operations, and this slide shows an implementation of the first, returning a list of action names. And it's very similar to what you do with an attribute. First, you call the superclass to get a list of inherited actions, and then we slap our action on the end of the list and return the augmented list.

This is the method that handles returning the description for a particular action. As with attributes, the first thing we do is check, is this action the one of interest? Because if it's not, we'll let the superclass handle it. But if it is, we're going to call the NS Accessibility Action Description function.

And this is something new in Tiger. Before Tiger, if you were to return an action description, you sort of had to look around and say, well, how did we describe the press action? And you might use Accessibility Inspector and discover, you know, the buttons had a press action.

And then you also had to maybe guess how might they localize that? So we're sort of taking the mystery out of that process and giving you a function that's going to return what our standard widgets do. For action descriptions. And I refer you to the AppKit release notes where this function is described in detail, along with some related ones for role descriptions.

And the final method in this action suite is for actually performing the action. And first, again, we check, is this the press action that we care about? If so, we do the appropriate thing. Otherwise, the super class handles it. And now Guy's going to show what you do in Carbon.

So three of those nine Carbon events are pretty much identical to the AppKit methods for performing actions. The first one is getAllActionNames. It's a lot like getAllAttributeNames. And in fact, you handle it the same way. You call the next event handler to let the inherited implementation add any names. You extract the mutable array of names and you add your new action to it.

Get named action description is also simple, but there's one kind of weird catch here. There is a mutable string ref in this event. You need to extract that string ref and replace it with your text. Replace the text in the string with your text and return. And so that's how you provide an action description. I've left out the code here just for simplicity of where we are looking to see if it's the action that we support. And if not, of course, it's going to call Next Event Handler.

Now, this is the only tricky part of the accessibility Carbon events, and that has to do with performing named actions. But before I kind of dive into this, let me give you a little bit of background for why this is complex. When an assistive application is telling your app to do a press operation, that assistive application is blocking, waiting for a response from a messaging protocol to say whether or not the app is alive, right? Your app might be crashed or hung or in GDB or something.

And so the assistive application needs to know if a timeout has elapsed and, you know, it's got to forego the attempt to press. However, that need often contradicts what actually happens in an application when you perform a press type operation on a button. A lot of buttons will bring up some kind of modal interface or might cause a menu to be tracked, which means that your accessibility event handler is not going to return right away, right? It's not going to return until the user's done dealing with the dialogue, which could potentially take minutes depending on what the dialogue's for.

Which means that assistive application who just sent the press request over is going to get a timeout and it's going to think the press failed and it's going to communicate that to the user. So now the user's going to be really confused because they thought something didn't work but now there's this new interface that's showing up and it's just a big mess.

So the way we solve that mess is by a parameter in the perform named action Carbon event. There is a parameter indicating whether or not this event was sent to your element in a queued state. Now by default, and this is mainly for compatibility purposes, the event gets sent to your element initially in an unqueued state. We dispatch it directly. That's just the way we've been doing it since our initial accessibility implementations. So if you see that happen, well, let's go to the other case. Let's say it hasn't been queued.

Or sorry, it has been queued. That's the case you want to handle. If it has been queued, you can go ahead and carry out your action. In this particular situation, we've got a button. We just call HIV simulate click, which will cause the button to flash, send out all the right Carbon events so your application can respond to it. And it's safe to do it now because the event's been queued.

You already know the assistive app has received an appropriate return value saying, yeah, yeah, yeah, it was successful. And if it wasn't queued, you need to return the event for access--sorry, event defer accessibility event error from your handler. And that essentially is a request to please queue this event and send it to me at some time later because I know I'm going to put up some potentially blocking kind of interface. So that's really the only trick to it.

Okay, so, so far we've been talking about fairly easy cases, right? You've got an existing view like a push button, you just want to add a description to it, or then you've got an NHIViewer or an NSView subclass and you need to provide some accessibility information above and beyond what the frameworks offer for you. But now the hardest case is you've got some class, it's not derived from NSViewer or NHIViewer and you have to provide all the accessibility information for it.

Now we're not going to go into tons of detail, mainly because we've already shown you how to do this in the other examples. There's just some things you need to realize. The first is that if you don't inherit behavior from our standard views and objects, you have to provide all the accessibility information for your objects. That means all the attributes. You have to tell size, position, everything else you saw in the accessibility inspector. You have to provide all that. You also need to provide handlers for hit testing and focus testing parts of the accessibility protocol.

This allows assistive applications to figure out what's under the mouse. In fact, that's the key for how accessibility inspector works. It finds the mouse position and calls an accessibility API to try to find out the element at that mouse position. And so if your custom elements don't support hit testing and focus testing, there's no way accessibility browser is going to work with your app.

Another thing you have a responsibility for is sending notifications. There's a handful of notifications. It's in the HI Services headers, so you can take a look there for what needs to get sent out. But essentially it's for things like, "My object's about to be destroyed," or "There was some major state change with my object, like a value change." Well, if you are implementing an element from scratch, you need to make sure you send out those notifications appropriately. And we have both Carbon and Cocoa APIs to do that.

There we go. All right. So now let's talk about some things you need to think about when you're accessorizing your application. Said it before, we've said it actually a number of times in this session. If you can switch over to the standard controls, you'll be much better off because we're going to give you a lot of support for free.

So again, look at your applications interface. See if maybe you're using some custom objects that we've provided some modern equivalents for in recent operating systems, try to switch over to those. It's going to make your applications interface better in a lot of ways. Obviously it's going to meet the latest Aqua guidelines because hey, we're doing it. And it's going to give you accessibility support for free.

Now, a very subtle point is don't invent new stuff. Don't try to come up with new roles. Don't think you need to come up with new roles. And the main reason is that assistive apps won't know how to deal with your new roles. Assistive apps are designed around this fairly finite set of roles that we've already described. You know, there's groups, there's lists and things like that. And if you invent a new role, you're essentially asking every assistive application out there to rev just to support your new widget.

So really what you have to do is kind of-- turn your widget around and think about what your widget is a lot like in terms of the standard widgets. The best example I can give for this is the crayon picker in the color picker panel. As we were going and accessorizing various parts of the system, we got to the color picker and we said, oh, wow, crayons.

What are these things? I don't know. Do we need a new role for that? Is it a crayon role in a crayon box? I don't know. But we took a good hard look at this and said, no, we don't need a new role. Because really, when you click on one crayon, it selects that crayon. And it selects all the other crayons. It unselects all the other crayons in the box.

So really what you're talking about is a radio button inside a radio group. So that's how we accessorized it. Every crayon's role is a radio button. And then the group around all those crayons is a radio group. And suddenly, assistive applications can just deal with that for free the way they've already been dealing with radio groups.

OK, so you've found some part of your application, and you've decided to use one of the standard rules to represent it. Now here's some of your responsibilities after you do that. Take a look with Accessibility Browser-- sorry, Accessibility Inspector-- and find some element in the system that has that same role. And make sure you support all the same attributes.

Assistive applications rely on the fact that a given role implies certain attributes, and actions, in fact, as well. For example, an assistive application sees a button, it knows it's got a press action. It needs to be able to assume that kind of thing. So make sure you support the same attributes and actions as the standard roles.

Now if you run into a situation where your element, yeah, pretty much fits into a standard role, but there's enough subtle difference that you kind of want to call it out. But you don't want to force all the assistive applications to rev to support you. You'd like to work for free with voiceover. But you can do that through a mechanism called sub-roles.

Sub-role is just another attribute on an element. The best example of this are windows. There's a bunch of different types of windows on the system. We've got document windows, floating windows, system windows, sheets, all these kinds of things. Sheets was a bad example because that actually has a different role. But all those other types of windows all use the window role. But we found that in some cases, assistive applications need to tell the difference between a dialogue window and a document window. So we do that with a sub-role.

The sub-role says, oh, it's a document. Oh, it's a dialogue. And now an assistive application can, if it wants, look at that sub-role and make a better decision about what to do with your element. But at the same time, it can keep working just like a window normally could in that app. So you don't force anybody to rev.

Another thing we found is people are very tempted to come up with a special role because they look at an element's purpose in the sense of their overall application. For example, you might have a toolbar and that toolbar might have a print button on it. And you realize that, hey, printing is very fundamental to my application.

So I really want this to be a print role. We need to avoid that temptation, mainly because the notion that this button is a print button is already conveyed by something else. We've given you a couple of ways that that's possible. Obviously, that button might have a title that says print, so the user already knows it prints. If it doesn't have a title, you probably put a description on it that says print. So the user already has these ways for determining a button's purpose. So don't be tempted to give it a new role just because it has some unique purpose in your application.

Again, just like with attributes, you need to use the standard actions. There's a small handful of them, and they support just the most basic mouse interaction. If you're tempted to give an element a new action, see if maybe you can't achieve the same thing by giving it an attribute that's settable.

Best example of this is the selectability of text in an edit field. We could have introduced some complex set of actions so that you can set selection here and then set selection there. No, we don't bother with that. That gets really complex. Instead, there's just a writable attribute that is the selected text range, so the user can set that that way.

[Transcript missing]

So as we implement our interfaces, a lot of us use grouping views. And in fact, some of these grouping views that we put in our windows, they're forced on us by, well, me. You know, I put various views in your windows, and Mike puts Cocoa views in your windows.

And so the real implementation hierarchy might look something like this. You've got a window. Inside the window, there's a frame view. Inside that frame view, there's a content view. Of course, there's other views for implementation details. And there's a button. And in Cocoa, there's even a button cell inside that.

Well, you don't want all that complexity to be seen by a user of an assistive application. Like VoiceOver, mainly because a sighted user doesn't see that complexity either when they glance at the interface. So for accessibility purposes, you want your interface to look like this. There's a window, and there's a button in it. And the way you achieve this is through the notion of ignored elements.

There's a Carbon API called HIObjectSetAccessibilityIgnored. You can pass any HIObject, which means any control. You really only want to use it on controls. And you can say, hey, ignore this control. You know, I just use it for grouping purposes. And I think in Cocoa, the method name is just set accessibility ignored. Accessibility is ignored. Accessibility is ignored. And you can just call that with true to mark an object as ignored.

Now, a similar but subtly different point is the concept of suppressing an element. Some applications that we've played with bring up parts of their interface just at a certain time. A good example might be a download status dialog in an app. When the file's done downloading and the app wants to get rid of that window, some apps just move it off screen. You know, they make sure they move it to some crazy coordinate, 10,000, 20,000, so they know it's not visible to the user.

Well, the problem with that technique is accessibility still knows about that window. It still looks logically visible from our API perspective, so both frameworks report that to the user. And so that's really odd that a user of VoiceOver can get this window that's off in la-la land. You want to avoid those situations entirely.

And it doesn't just happen with windows. Some applications move their controls away to some crazy coordinate just so that they're not visible to the user. So if you are one of these applications or you see your code doing this, make sure to logically hide the objects, logically hide your windows, logically hide your controls. So one thing. Sometimes people new to accessibility. Get these two concepts mixed up, ignored and hidden. So when something's ignored, even though it's not there from an accessibility point of view, its children sort of pass through.

If you hide a view, it's gone along with all its descendants. So those are really different concepts. And usually if something's ignored, it's always ignored. And whereas as your UI, if your UI is dynamics, whether something's hidden or not might change over time. And so try not to get those two things confused.

So you'll also find other cases where you need to provide accessibility information for an element that doesn't have an object to back it. Best example of this is the scroll bar. If you use Accessibility Inspector and you look at a scroll bar, sure, there's a scroll bar element there, but there's also some button elements in the scroll bar to represent the up, down, and left, right buttons. And in fact, there's buttons to represent the page up and page down regions, and then there's an indicator element to represent the thumb.

Well, there's no actual view in our view system to represent those subcomponents. It's all part of the overarching scroll bar view. So if you have a similar sort of situation in your application, the way to deal with that is by creating objects, sort of dummy objects, to represent them as accessibility objects. In Carbon, you'd create an HI object subclass potentially to do that.

When you do this, you're fundamentally creating a new accessible object from scratch. So again, you are responsible for providing all the attributes and actions for that object. But there's some shortcuts you can use to sort of get around that requirement. Generally, you want the instance, the dummy object, to provide things like the parent and the role. But oftentimes, you can either, by routing Carbon events or calling methods on the parent, you can delegate some of the responsibility to the parent code to at least keep your code kind of self-contained.

Another cool thing about this is since they're dummy objects, you don't really need to create them until they're needed. Don't instantiate them at view construction time, because you don't necessarily know that accessibility is on. And even if accessibility is on, maybe the user's never going to go to that scroll bar or whatever.

So create the instances only when they're needed. Maybe the first time the parent object is hit tested, or the parent object is asked for its children attribute or something like that. And the other important point is make sure the parent does the hit testing and focus testing. Otherwise, Accessible--sorry, Assistive Applications won't be able to find those dummy objects.

So now Mike is going to go into some details on troubleshooting some common problems with the accessorization of your applications. Okay, so yeah, I have a few slides and I'm going to talk about some common problems that you might run into accessorizing your app. So the first one's pretty simple. Make sure accessibility is on.

You need to go to System Prefs, and there's a checkbox that says, "Enable access for assistive devices." And you need to make sure that's on. Now, this is not the same as turning on Spoken Interface or VoiceOver, which is an assistive app that requires the access for assistive devices to be on. and David So it's up to you whether or not you want to enable voiceover.

The next point is really specific to Cocoa apps. And this is a pretty good habit to run into if you're implementing accessibility on your Cocoa app. And that is to run with NS Accessibility debug log level of 1. And I usually do this with a command line argument, although you could write it to your system prefs and always run that way. And I'll neither confirm nor deny what values other than 1 will do.

But if you specify one, what's going to happen is, as the Cocoa app runs, it's going to spew informative error messages to the console concerning accessibility. Now, perhaps what's more important than the error messages themselves is the fact that you're going to get a clue that something's going wrong.

One thing that both Carbon and Cocoa do is, when handling requests from assistive applications, is we eat any errors or any exceptions that are raised during the process, because we don't want an assistive app to be able to go in and crash some other application. And because we just eat these exceptions, you may have no clue that something's going wrong, and your app may just sort of mysteriously seem to not work with accessibility. So if you turn on this option, If your app's not working and you turn on this option, it's likely you're going to see all kinds of spewage informing you of what might be going wrong.

Now if we return to the example Guy gave of a scroll bar and has it with its five children, the up and down arrows and the page up and the page down region and the thumb, and say you've gone through the trouble of implementing something like that, and then you go over and you use the accessibility inspector and none of the little pieces of your scroll bar show up.

And that's pretty frustrating, so I'm going to give you some tips on how I would go about sort of debugging that. First off, it could be that there's something going wrong, some exception's being thrown, and you just don't realize it. So if you use the NS Accessibility Debug Log Level of 1, look at that console output, see if some exception's being thrown, and that's the root of your problem.

The next thing that might be going wrong is in Accessibility Inspector, you're using the mouse to sort of mouse over the widgets you're interested in looking at. And so that involves hit testing. Now, in the frameworks, we can do hit testing and we can figure out if this points over a window or even what view it's over. But the fact that there's a thumb in your scroll bar, we have no idea that that's going on.

And how accessibility works is we're going to tell your scroll bar view, look, we think the mouse is in you. And now this is your chance to refine that search and tell us about some children. And if you don't do that, your children are not going to show up. So that's a common mistake.

So, the next thing you might want to look at is, if the children aren't showing up, is actually go to the parent object. Maybe you can get your mouse over that in Accessibility Inspector and look at the array of children. And if you're, you know, if the thumb and the up and the down arrow aren't even in that array, well then you've got even more fundamental problem. Perhaps there's some exception being thrown that's keeping your children attribute from returning the proper value.

And if none of that is the problem, then one other thing you might want to look at is the coordinates you're using for your hit testing. Remember that the Accessibility APIs all deal in terms of screen coordinates. Now, on the framework end, we each have a slight twist on it. In Carbon apps, you're going to get a coordinate in top left relative screen coordinates. And in Cocoa, it's going to be bottom left relative.

But in either case, it's global coordinates. So if you're doing your computations in sort of view relative coordinates, that could easily be what's going wrong. And then the final point is something Guy already touched on, and that's the issue of something being ignored. If the element you're returning is ignored, then it's not going to work properly.

And as an example, in Cocoa, NSViews have no visible, you know, a plain generic view has nothing to show for itself on the screen, no way for the user to interact with it. So by default, views are ignored. And if your object descends from NSView, you need to go in there and have the accessibility is ignored method. You need to override it to return no.

Here's a couple more problems that sometimes arise. It's important that the hierarchy be sort of self-consistent. And that means that if the UI element says that some other UI element is its parent, if I go to that parent and I look in the children list, I better find that child in the list.

And if that's not the case, then you might get sort of weird behavior. You know, the assistive app might work in an inconsistent way because depending on what path it takes through the hierarchy, it may discover the child or it may not. So that's a common thing to look at, and we actually have a tool that makes these checks for you.

And then the last point is once again something Guy mentioned before. Sometimes people's user interfaces are dynamic and you hide things. Maybe you move it way off screen or maybe you obscure it with something that draws opaque white over it. Well, if you do that, the underlying elements are still going to be available to assistive apps.

And assistive apps, you know, they may press a button that no user could normally press. And this may exercise code paths in your app that haven't been tested, rather dangerous thing to be doing. So you want to hide things in a sort of proper way, either rip them out of the view hierarchy completely or use the appropriate method from your framework to actually hide the view.

Our last major section is on performance. I have a couple slides that discuss how should doing all this affect the performance of your application. The bottom line is it really shouldn't affect the performance of your application unless accessibility is on. That's the principle that both Carbon and Cocoa have used in implementing this stuff, that we don't want anyone to pay any price for this unless accessibility is turned on.

And so to help you out with that, you don't have to really worry about making our calls. We're pretty careful. If you want to set an attribute or you want to post a notification, you know, go ahead and do it because we're going to bail out really quickly if accessibility is not on. And sometimes we're even smarter. When it comes to posting notifications, if we're actually going to check, is anyone even listening for notifications on this object? Because if no one is, you know, we're going to do nothing.

Now when it comes to your code, if you want to optimize it this way, you're going to need a way to test if accessibility is on. And you might want to use, in fact, the only way to do this is to test if the accessibility API is turned on using AX API enabled. And this is a call from the accessibility API that you can make. And you should check it out before you do any expensive operations.

Another thing to note is you shouldn't have to worry about accessibility being on. Don't worry about accessibility being turned on while your app's running. If you make this check at the beginning of, at launch, that's fine. I know if you get in there and you play around, it kind of seems to work that you can turn it on while an app's running and things work. But that's not something you can rely on. And it's, you know, it's a little bit of a challenge.

And so, again, that's sort of a warning to the assistive app developers, too. If accessibility gets turned on, users are expected to quit and restart any running applications. And a final tip is something also that Guy mentioned, is sort of be lazy about creating any data structures associated with accessibility.

So if we go back to the example of the scroll bar with all the children, you know, I would create the children when I'm asked for them and then only keep them around long enough to satisfy that request and then throw them out. There's no reason to have these things around and then they'll get out of sync if your scroll bar changes state. So it's much simpler just to create them as needed.

And now we have a couple slides talking about the resources available to you. And the first one I want to plug is our accessibility dev mailing list. And this is a public mailing list that you should subscribe to. You can ask all your questions on it. If you come up with some weird widget and you don't know how to accessorize it, you know, post an email. This list is trolled by Guy and myself actively and some other engineers.

And, you know, we don't promise that we have all the answers. Accessibility has been a--the whole story has been evolving since Jaguar. And more often than we'd like to admit, you know, people come up with weird UIs that we hadn't thought about. How is this going to work with accessibility? But if you post to this list, we can usually steer you in the right direction.

And finally, there's three documents relating to accessibility. One of them is for people writing assistive apps. And even if you're just accessorizing your app, it might be useful to peruse that one. And then there's a Cocoa-specific document and a Carbon-specific document for accessorizing your applications. And now we'll open it up for Q&A.