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

WWDC02 • Session 209

Accessibility and Carbon

Carbon • 48:38

This session teaches developers how to make their Carbon applications accessible, covering the standard accessibility features found in HIToolbox controls, windows, and menus. Developers also learn how to override and augment the standard functionality to make custom controls and frameworks available to the new Accessibility APIs.

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.

which is Accessibility and Carbon. So just in case you weren't aware or you weren't unable to attend the previous session, 009, as it applies to the Carbon framework. So if you didn't see it, please take the opportunity to join us. So what I'd like to do now is welcome Guy Fullerton on the stage so he can present today's content. Thank you.

Yeah, there we go. I'm Guy. I'm one of the high-level Toolbox engineers, or HI Toolbox engineers. And in particular, I've been working on the accessibility side of Carbon for the last month or two. And I want to show you basically how you can take a Carbon application and make it accessible.

[Transcript missing]

So we solve that with a set of Accessibility APIs, which abstract the notion of an interface into these UI element objects. And the Assistive API set is basically the green boxes on the slide you see. But in particular, since this is a Carbon-focused session, I'm just going to show you how the green boxes work on the Carbon side and how you can make a Carbon app fully accessible.

So there's three basic things an application needs to do in order to be considered accessible. The first is it needs to be able to explain to an assistive application what its interface is. What windows are in my app, what menus, menu items, controls, buttons, text fields, and all that stuff. It needs to be able to communicate that stuff. Likewise, an assistive app might say, well, tell me what widget is underneath a given screen point, something like that.

The second thing your application would need to do is to allow an assistive application to press buttons, choose menu commands, or invoke other sorts of functionality within your application. Finally, the third thing your app would need to do is send out notifications to the assistive app saying, hey, this part of my interface changed, and a user's probably going to want to know about that part, so please communicate it to them.

And the way this all basically works is through Carbon events. I mean, Carbon events are the future. We've been saying this for the last couple of years. We've been saying you're going to need to learn about Carbon events to adopt all the new features. And this feature is no exception. The suite of Carbon events that we use to implement the accessibility stuff on our side is, they correspond pretty directly to the C APIs.

So if you saw last session, you know, assistive apps can say things like, tell me what UI elements under this point. Give me the focused UI element. Once I've got a UI element, you know, they want to say, give me some actions and attributes and things like that. And so we broke up the suite of Carbon events into three basic sets. The first set allows the assistive application to pull UI elements out of your application so that it can see what the interface looks like. It can get a given UI element at a point.

It can find the focused UI element and look at your containment hierarchy. It can find out about a given UI element's parent or children. The next suite of Carbon events allows the assistive app to pull individual pieces of data out of a UI element. You might want to find out what a button's title is or a window's title, things like that. Likewise, you also want to find out what actions a UI element supports. You know, a button might say that, hey, I can be pressed. Or a menu item might say, I can be pressed. Say, I can be picked.

And finally, the third set is ways for the assistive application to trigger functionality in your app. That's done in two basic ways. As we showed in the previous session, some attributes of a UI element are modifiable, so of course that's one way to invoke functionality. But the other way is to ask UI elements to perform actions.

So some of you right now are probably thinking, oh, geez, Carbon Events, you know, this is going to be such hard work. Do I really want to do this? We've got all this old legacy code. You know, I'm in education. We've had this code working for ten years. We haven't modified it. We were lucky to get it just working on ten. How am I going to deal with this? Well, it's actually really simple.

Carbon Events are adoptable in a piece-by-piece fashion. You don't have to rewrite your entire application. And they work with WaitNextEvent. You can take your existing code and just wire up accessibility support into it and leave most of the rest of your code alone. I'll talk about some of the other minor changes you'll need to make later.

But in particular, you can find out more details about migrating to Carbon Events by checking out session 203, which happened, what, two days ago, Tuesday or Wednesday? Get the DVD if you weren't able to attend that session. But it talks about the basics on how to adopt Carbon Events and shows you some techniques you can use.

So these Accessibility Carbon Events need to get sent someplace, right? We need to make these requests into certain places in your application. And if you're familiar with Carbon Events, you know that you need a target for a Carbon Event to be sent to. And in particular, the Accessibility Carbon Events will be sent to potentially any HI objects in your interface. As we explained in our HI Objects session, windows controls and menus are all HI objects.

You can make HI objects of your own. And any of these could be or can be the target of an Accessibility Carbon Event. The really cool thing here is that all of the standard system controls and menus and windows and stuff like that, they already have the accessibility basics wired into them. So you won't have to do any work to make a button accessible. You might want to augment the button's accessibility to add a few extra features. And you can do that if you want. But all the basic functionality is there.

Okay, so I'm going to get into a weird, subtle concept thing here, so bear with me. So like I said, these Accessibility Carbon events and requests are made onto windows controls and menus, and that makes a lot of sense, right? You might -- if you've got a window, you might want to know what children UI elements it's got, or like it shows on the slide, you might want to find out a push button's title or a menu's size.

But that doesn't answer a couple other questions. You might want to find out something about an individual menu item, like what's its title, or something really weird like a scroll bar's arrow. That, from the accessibility standpoint, needs to look like a button. But there's nothing to represent an individual arrow in a scroll bar in terms of an HI object. So we don't really have a way to send a Carbon event directly to a menu item or a scroll bar arrow.

The way we solve that is by saying that the owner of that subcomponent, the HI object that has that subcomponent in it, is the target of the Accessibility Carbon event. And that's the way we solve that. So for example, if you want to find out the title of a menu item, that Accessibility Carbon event is going to be sent to the menu ref, which is an HI object, and which therefore can receive Carbon events.

The other main goal is we need to have a really, really flexible representation for this notion of a UI element, for this notion of an accessible object. Obviously, it's got to be able to represent all the things I've talked about so far, windows, menu items, things like that. But it also needs to be flexible enough to represent completely custom parts in your application.

We know there's a lot of Carbon apps out there that have maybe their own text editing engine, or you don't use the control manager a lot because you've got some custom-looking stuff, and so you just hand-roll that on your own. We need to make sure that our Carbon mechanism for allowing accessibility lets you make every part of your application accessible.

And the way we do that is with a type that we talked about in the previous session, the AX UI element ref. Like all the Accessibility APIs and types, this one starts with AX. And so when I refer to a UI element, what I'm actually talking about is one of these AX UI element refs. These are CF types. You retain them and release them and put them in CF collections and stuff, all like that, just like you would do with any other CF type. And this type allows you to represent any accessible widget on the screen, anything you want.

And most importantly, one of these UI element refs is a proxy. So this is what one might look like in memory. It's got a process ID in it pointing to the application to which it came from. And it's also going to have some framework-specific references that point into some data within your application. So, for instance, on Carbon, these pointers might look like a control ref or a menu ref, something like that, whereas for Cocoa, they're going to point to the various Cocoa structures.

So on Carbon, a UI element's framework-specific data has two fundamental pieces. It's obviously got an HI object. You know, like I said, we need a place to which we send these Carbon events. But it's also got a 64-bit identifier associated with it, and I'll talk more about why we need that in a minute. You manage them with three basic APIs.

There's ax-ui-element, create with HI object and identifier. I tried to make a really, really long API name because I like those, but you use that to create one of these UI elements. Once you've created one and you're hanging on to it for whatever reason, you can pull out the HI object or the 64-bit identifier with one of the other accessor APIs.

Okay, so like I said, the UI element has an HI object in it for a Carbon app. And fundamentally speaking, this is just the target of the Carbon events that might correspond to requests for information or requests for action on one of those UI elements. And generally, it's going to be one of the Toolbox data structures, like a control ref or a menu ref or something like that.

But it can be any other HI object you might desire. If you feel like expanding the toolbar HI object's functionality, hey, you can throw Accessibility Carbon event handlers on that. And start returning UI elements based on the toolbar if you want. Now, granted, the system's already going to support accessibility for the toolbar, but you might want to augment that in some fashion.

So the 64-bit identifier is what allows a UI element to represent a subcomponent of an HI object. And I've got some graphical examples here in a sec, but bear with me. If you've got a UI element--well, you know what, let's just jump right into the examples. It'll be easier to explain. If you've got a UI element that contains the framework-specific data of a menu ref and zero, that's going to represent the whole menu, right? Zero generally means the whole thing, the whole HI object that I'm talking to.

Whereas if I've got a UI element that has a menu ref and an identifier of two, that might represent the menu item, the second menu item of that menu. The one important thing to note here is that this 64-bit identifier space is privately definable by the HI object. So if you have a system ref like a menu ref, you don't want to make assumptions that that menu ref plus two equals the UI element that represents the second menu item, right? It's up to the menu ref's implementation to decide what the 64-bit identifier means.

Just another example here, if you've got a data browser control and you build a UI element that contains the control ref and zero, that's going to represent the whole outline view, which is our term for a data browser and list view in the accessibility space. Whereas if you've got a UI element that contains the control ref and some hex identifier of some fashion, it might refer to some cell within the data browser.

So to make this, just to hammer this point home, this is probably a technique you will want to take advantage of within your own application. And here's one way you might want to do that. Let's say you've got a custom text editing engine in your app, and it's not implemented in terms of controls or anything like that, and you want to make it accessible, you can use this same technique.

If you need to generate an accessible, sorry, a UI element that represents the entire text engine in that window, you're going to create an HI object to represent that text engine and create a UI element wrapped around that HI object and zero. And that will point to the whole thing. But let's say you want to go a step further and you want to make individual sections of your document accessible. You know, you want to expose paragraphs or words or something like that as individual UI elements.

You can do that, too. And so one thing you might do is say, all right, if I want to make this accessible, I'm going to have to make it accessible. If I want to represent the 12th word of this document, I'm going to create a UI element wrapped around that engine's HI object and the identifier 12 or something like that.

So now we know, you know a little bit about representing your application, but I want to talk about it from a higher level. The UI elements that an accessible application looks at, sorry, that an assistive application looks at, form a containment hierarchy, right? You've got an app UI element.

That app points at a couple windows as its children and also has the menu bar as a child. Each of those windows might have a root control as its child, and any of the controls in there might contain lots more controls and things like that. So as you think about how to represent your app, keep those basic concepts in mind. You want to form a hierarchy.

So, the examples I showed on the other side, other slide obviously, the system's going to handle most of the stuff for you. We already make windows accessible. We already make menus, the menu bar and individual menus accessible. You know, we already make the control hierarchy accessible. And the important part for you is that you're going to want to do work to make the custom areas of your application as accessible as possible. And to do that, fundamentally, you just need to create HI objects to represent those parts.

Better yet, create HI views to represent those parts. And then you're going to fit in with our new HI view model as well. So, it's a double win. The one trick, if you do not turn your, represent your application with HI views or control rafts, and instead you're just building HI objects and using those to represent your app in an accessible manner, in order to make sure your app's HI objects are represented in the right way, you're going to want to do a lot of work to make sure that your app's HI objects are represented in the right way. So, if your app's HI objects are represented in this hierarchy, you're going to need to override certain default handling.

In particular, if you want, let's say you've got a text editing engine. You want to make sure that engine is represented as a child of the window. You're going to need to make sure to override that window's handling of the get children Carbon event, right? So, you can put your UI element in that array of the window's children.

And this is another weird note that I want to talk about. Sometimes as you're thinking about your hierarchy and looking at how the system does things, you might be worried that too much information is going to be thrown in the face of an assistive application. The picture on the left there shows what an actual control hierarchy in a window might look like.

Obviously you've got a window ref on the outside. That window ref is going to have a root control. You might have a couple other controls that ultimately wrap a user pane. And inside that user pane is a push button. And as it turns out, the root control and the user pane aren't really important.

They may not actually represent any information that the user ever sees. Obviously the root control is there just because the system needs it. And the user pane might be there just as a convenience for your development or some other grouping mechanism. So you don't want an assistive app to actually see those. You want the assistive app to see what we show in the picture on the right, which is just, hey, there's a button in this window. And the way we allow that on Carbon is through the use of the assistive app. of ignored UIElements.

Any HI object can be marked as accessibility ignored. And if you mark it as accessibility ignored, it can still be returned as a child or a parent of various other accessible objects. But the layer that actually services the request from Carbon and hands something back across to the assistive application makes sure to filter those out transparently. It does all the hard work for you.

Okay, so let's get down into some nuts and bolts here. The first thing an assistive application is going to ask you for is UI elements. It can't start anywhere without them. So you need to know how to send those over to the assistive application, or more correctly, how to service requests for those UI elements. They can ask for them three different ways.

They can ask your app, hey, tell me what UI element is under this particular mouse position on the screen. Or they might say, hey, tell me what focused -- sorry, what UI element is focused in your app right now. Or they might be tunneling down from the root, right? They might have grabbed your application's UI element and start traversing the hierarchy, and ultimately they're trying to find some grandchild or child of some window or something like that.

So the first way, which is finding something by position, is done via the K-event accessible get-child-at-point Carbon event. This Carbon event contains two parameters. The first, obviously, is the UI element that's being queried, and it'll contain a global screen point that they're interested in. So one other note I should probably make about these, the Carbon accessibility engine will send these sorts of events recursively.

So your responsibility when serving, servicing one of these events is actually just to pass back your immediate child that's under that point. And the Carbon accessibility engine will take care of any recursion that needs to happen. So fundamentally, when the assistive app wants to find a UI element under a given point, a bunch of these Carbon events are going to get sent out.

One's first going to get sent to the application accessible object, and it'll say, oh, okay, this window's under the point. Then that window will be sent the same event, and it'll say, okay, this first-order control is under that point. Then we'll send the event to that first-order control, and it'll say, okay, this sub-control's under that point.

And we'll keep going until somebody finally says, oh, not handled, there's no child under that point. And at that instance, the engine knows, okay, the last person I ask is the leaf node. So you want to make sure when you handle this event, only return your immediate children.

So, okay, I want to give you an interesting code example here, and this kind of plays into the notion of injecting one of your HI objects into the hierarchy. So for purposes of this example and some of the others, I'm going to assume we're trying to make an application with a text editor accessible, and that text editor is not based on any controls. You've got your own custom text editing engine, but you've built an HI object to represent that, and you want to make sure that that HI object is reported as a child of the window.

So to do that, like I mentioned before, you want to override the window's handling of the getChildAtPoint Carbon event, right? Because your engine is going to be in the window, and if the mouse is over that engine, you want to make sure to return that child, not the one that the window normally would have returned, which is probably going to be some control or something like that.

Okay, so this code in here would be in the handler that you write and install on the window to override its handling of the getChildAtPoint Carbon event. The first thing you would do is pull the accessible object out of the Carbon event. This is the accessible object that the request is being made of. In this case, it's going to be the window.

Here's a subtle point that you want to remember when you're overwriting somebody's handling. You've just got a UI element out, and presumably, well, that UI element will contain the HI object that is the window ref to whom the Carbon event was sent, but that UI element might also have some identifier in it, some non-zero identifier.

Remember, like I was saying, a non-zero identifier refers to some subcomponent of the window. I'm going to get the identifier out, and if that identifier is not zero, I'm going to say, okay, I don't want to handle this. I don't want to override the window's handling of fetch me a child of, you know, the close box, because I don't want my engine to be a child of the close box.

So I'd return event not handled error if I get a non-zero identifier. So assuming I don't get a non-zero identifier, I fetch the next parameter out, which is the global screen mouse location, convert it from global to local, and then I'm going to ask my text editor, hey, do you contain this point? And if it says no, then, again, I don't want to handle it. I want to let the windows default handling of this event take care of the situation. You know, maybe the window might return, oh, some controls under the mouse or something like that.

But assuming it is within the text editor, you would call your text editor to create a UI element. So you've got a hold of that. And then you put that in the Carbon event in one of the return parameters. In particular, you say, okay, this UI element, this is the accessible child parameter.

And at that point, you can release the UI element and return no error because you've completely handled it. You just said, hey, my engine is a child of the window. Carbon event savvy people who've used them a lot might see something really scary in this batch of code.

In particular, I do an allocation. I stick that allocation in the Carbon event, and then I deallocate the memory. And then when I return, you might be thinking, oh, problems are going to ensue because, you know, the subsystem is going to try to pull this parameter out and act on it, and I've disposed it, and we're going to blow up. Well, no, we're not. I actually fixed Jaguar. Oh, whoops. I guess I don't know if it's live. If we see a CF type come in as a Carbon event parameter, we'll retain it.

That reference will be valid for the life of that Carbon event. So you can do these kind of convenient things. So this will work for any CF type ref that is put into a Carbon event parameter with one of the type CF type ref types. Oh, that's kind of confusing. But on the slide, you can obviously see type CF type ref up there. But it'll also work for the various other type CF refs that we've defined in the headers. It's like a type CFStringRef and type CFArrayRef. We do all the magic with those two.

Okay, so the next Carbon event I want to talk about is the GetFocusedChild Carbon event. Just like the GetChildAtPoint Carbon event, this one tunnels down automatically, so you want to make sure you only return the leaf node child that might be focused of you. And if you don't have a focused child within you, just say event not handled error. And again, that just lets the subsystem know that, okay, they asked you, you're probably the focused one, and it can make sure to return you as the focused thing.

The third way that they can ask for UI elements is through familial references. They might ask for your children or your parent or maybe some other attribute of you which is implicitly a child. Like we showed before, the application supports an attribute, the menu bar, right, and that's implicitly a child of the application. So all this is fundamentally just a specific case of fetching attributes in general. So attributes of a UI element are just pieces of data that you can record and use to fetch attributes. So all this is fundamentally just a specific case of fetch attributes.

So all this is fundamentally just a specific case of fetch attributes. Yeah, so we saw the examples. Now, certain attributes we consider required, and we're still working on exactly what the required set is, but my current thinking is this. Every UI element must have a role. The role describes a UI element's basic purpose, right? Are you a button? Are you a checkbox or a window or a menu or something like that? And this allows the assistive application to make some internal logical decisions about what the user might be able to do with that particular UI element.

But it's important to note that even though the role is a CFStringRef, it's not intended to be communicated to the user via speech. In our case, all the standard roles that we return are prefixed with K-A-X, or sorry, just A-X and then some name. So you generally don't want to read that.

If you do want to present something readable to the user, you would ask for the role description, and so it's up to you guys to supply a readable role description for your various items. For instance, if you have a button, its role description might actually be button or something similar.

Likewise, well, and this is kind of a subtle point, the size and position attributes, So if I step back for a sec, UI elements, pretty much, not pretty much, they should only represent what's on screen. It would be kind of silly to have a UI element that represents something that the user couldn't see or that a conventional user couldn't see.

So any UI element, since it's on screen, is going to have a size and position. We've got a couple new types to talk about those, AX size and AX position. Those are defined in the accessibility headers. But if you like, for convenience, and since HITooLbox supports them, you can also pass those back in terms of an HI size or an HI point.

And likewise, any attributes that should be represented as UI elements are UI elements, right? You can pass back, if somebody asks you for your parent, you're going to want to pass back an actual AX UI element ref to represent the parent. And likewise, if they ask you for your children, you want to pass back a CFArray of AX UI element refs that represent your children.

Okay, so the first Carbon event having to do with attributes is get all attribute names. It's got two pieces of information in it, the UI element to whom the question's being asked, and a CFArrayRef for you to fill out with all of the attribute names that your accessible object supports. So that's your job. You handle the event simply by filling up this array with a bunch of strings representing the attributes you support.

We've got a ton, a ton of attribute names in the header. An important thing to realize is that we're still working on this set of attribute names. You're going to see some flux between now and when Jaguar actually, actually ships. We want to kind of refine the set of attribute names down into some reasonable group, at least the default attribute names. But you're free to return whatever attribute names you want. If you want a really, really rich accessibility application, sorry, accessibility implementation in your application, you know, you can pass in 30 or 40 attributes as supported by one of your UI elements.

So let me run through a quick example. Unlike the last example, this is a handler that's installed on your HI object. This is not some patch on some other HI object. But like the other example, this might show you what a text engine would do in terms of reporting the attributes that it wants to support. So we receive the Carbon event. We pull out the mutable array ref that we need to fill up.

And then we're going to put in all the required attribute names, you know, role, role description, parent, children, all those things. And then we're going to put in our more interesting attribute names that are specific to a text engine. You're probably going to want to supply a text attribute, right? You're a text engine.

You need to supply text. You probably also want to support the selection attribute so that a screen reader might be able to tell the user that, yeah, characters 50 through 100 are selected, and maybe others. And then you return -- oh, and then you return no error, and you're done. Thank you.

So the next Carbon event is Get Named Attribute. This will be sent to you when the assistive application has a UI element and it's found out what attributes you support and it wants to find out what the data is behind one of those attributes. So the Carbon event contains, obviously, the UI element to whom the question is being asked, and then CFStringRef of the attribute name that the assistive app is interested in. And your responsibility is simply to pass back the data that represents that attribute.

is an expert in the world of text. So let me give you a quick example of that. You're going to pull out the name that you're interested in. At this point, I've already pulled out the UI element, if that's at all interesting to me. So here I'm pulling out the attribute name, and I'm going to do a test to see if it's the text attribute. You know, I'm going to have a bunch of if else, if else, if else, and this happens to be the first one.

So if I find out it is a request for the text attribute, I'm going to put the data into the event. So I'll ask my text engine, hey, please create a CFString for me. I've got the string, and then I shove that back in the Carbon event as an output parameter in the, what is it, accessible attribute value parameter, and then I release the string to clean up, and I'll return no error. One important thing to note is the suite of Carbon events and the assistive applications really, really, really want you to return CF types.

It's a very generic way of representing data. So when possible, return attribute values in terms of CF types. The toolbox does a little bit of conversion for you for some of the things like H.I. points and H.I. sizes, and we will, you know, likewise, if you happen to put data in the event that's type Unicode text, we'll build a CFString rep out of that.

But, you know, we can't cover all the bases, so we're kind of leaning on you guys to make sure you return the data in the form of a CF type. And this is particularly important because ultimately this data needs to get sent cross process. And CF has a very convenient, easy way for us to send this data across process, and other types don't necessarily.

So attributes aren't just static. Some of them can be changed. And assistive applications might want to find out, hey, which of your attributes are changeable? And they would do that by calling into the Accessibility APIs. And then we would send your UI element the isNamedAttributeSetableCarbon event. Really straightforward. They're going to give you your UI element. They're going to give you the attribute name. And your job is simply to return a Boolean that represents whether or not that attribute can be modified.

If you don't support the event or if you return event not handled error, it's just an assumption that that particular attribute is non-modifiable. So if in your implementation you find, okay, I've got this UI element. I want to support these attributes. But none of them are modifiable. You don't even need to support this Carbon event.

So just as a quick example of this, we're going to get the attribute name out of the Carbon event, take a look, and in this case, it happens to be our text. So we see that it's the text, and we want to allow the assistive application to change the text. We want this to be a modifiable attribute. So we set our Boolean value to true, we stick it in the Carbon event, and return. That's all you need to do for that.

So once the assistive application has found out that, yeah, I've got this settable attribute here, it may present that information to the user, and the user says, okay, change this attribute to some value, you will receive the set named attribute Carbon event. And that's going to contain three pieces of data, the UI element, the attribute name, and the data that the user wants to have inserted into that attribute.

So your job simply is to take the data out of the Carbon event and put it into whatever your implementation is. So in this case, we get the attribute name, we see that it's the attribute name, and we want to allow the user to change the attribute to some value.

So we set our Boolean value to true, we stick it in the Carbon event, and return. That's all you need to do for that. We see that it's the text again. So now we're going to need to extract the data out of the Carbon event and push it into our text editing engine, which is fairly straightforward. The data coming into the Carbon event is in the accessible attribute value parameter. So we pull the data out. You want to check for no error here. Let me step back real quick.

When I'm pulling the data here, pulling the data out here, I'm actually asking for it as generic CF type ref. We do have more specific types in there. But generally, just -- you want to ask for it as the generic CF type ref, and then later, dynamically look and see what real type it is and see if you can handle it.

And I've got an example of that in the slide. Now, it's possible that the data that the app sent you can't be translated to the type you requested. And if that's the case, you're going to receive an error back. And if you get that error, go ahead and stop processing.

: Otherwise, we get some valid data, and I know my text engine can only handle CF types--sorry, can only handle CFStrings for purposes of shoving data into it. So here I want to check the actual CF type ID of the data I got and make sure it's a string. And if it's not a string, I know I can't handle it, so I'm just going to say event not handle error and be done.

Otherwise, I'm going to tell my text engine, "Hey, please set your text to this." It's pretty straightforward. And then you're going to return no error. So one way that assistive applications will want to affect your app is in terms of actions.

[Transcript missing]

: It's lots of things you can do with actions.

Obviously, press is the big example. Picking a menu item is a big example. But you want to think pretty carefully about what actions you support. If something can be better represented as settable attributes, go ahead and do it as a settable attribute as opposed to an action. So this is going to look really familiar.

The first action related Carbon event is the means for an assistive application to find out all the actions that your particular UI element supports. This is virtually identical to the get all attribute names. Just put the word action in there instead and you return different stuff in the array.

We have a bunch of standard actions in the headers and just as with the attribute names, the action names are in a little bit of flux. We want to figure out what the default set of actions are and And so you will see some changes to the header when Jaguar is finally shipped.

So the next interesting thing about actions is the notion of an action description. Action strings are generally not meant to be human readable. Our default strings are all prefixed with AX, right? And it wouldn't be reasonable to say this button represents, or sorry, this button supports the AXPRESS action.

That wouldn't make any sense whatsoever. But assistive apps do need to communicate what the meaning of an action is to the user. And they do that by finding out what the action's description is. So when an assistive app needs to find that out, your UI element will be sent the get named action description Carbon event.

It's going to contain the UI element, the action name that they're interested in, and a modifiable string ref that you need to fill out with a sort of human readable description of the action. Another important thing to note is that these strings that you return that are human readable, they should be localized, and they should match whatever language your app happens to be running in right now.

So finally, an assistive app has found out, okay, yeah, this widget supports some action. All right, invoke it. And when they want to invoke it, your UI element will be sent the form named action CarbonEvent. It's got the UI element in it and the action name that they want to invoke. And your job is simply to make the interface react, do the right thing.

So here's another example. This is pulled out almost verbatim from our push button control. But this is one example where you might want the button to support the press action. You're going to pull out the UI element. And in this case, we're going to also get the HI object out of the UI element. In this particular case, the HI object will be the button's control ref.

And then we call the HIViewSimulateClick API, which is an HIView API that we added in Jaguar to do click simulation. You can think of this as a wrap around the old technique of calling highlight control, delay, highlight control, but it does it in a more Carbon event-based fashion.

And there's some hooks for CDEFs to get in there and modify the behaviors. But the other really cool thing about HIViewSimulateClick is that it has the same post-click behaviors as something like handle control click or track control, in that if the view that you're clicking or the control that you're clicking has a command ID associated with it, HIViewSimulateClick will make sure that you're clicking on the button. sure to send out that command ID so that the rest of your interface can react appropriately.

Okay, so here's another subtle point, but since assistive applications are going to be invoking press actions in buttons that the system has already made accessible, you will not be able to react to that button press unless you've got a handler that handles that button's command. So let me give you kind of a roundabout example.

If you've got an application probably ported from original 9 code, wait next event based, and you're event handling looks something like, okay, wait next event, I got a mouse down event, okay, is that mouse down event in the window? Yeah, it's in a window, okay, find control, oh, I found a control, okay, I need to call track control, I call track control, I found out that K button part was hit, and now I have this big switch statement and a bunch of ifs to say, okay, which control was hit, if that button was hit, go off and do some action.

That works great if you're getting events in through wait next event, but that sort of technique will not work when an assistive application is telling you that you're not getting your button to press, because this button press is going to come in completely behind your back. The system is just going to see this press request, we're going to flash the button, and your app needs to react.

Likewise, if you are used to handling your menu commands by calling menu select or menu event, looking what the result was, and then doing some big switch case statement and figuring out what functionality to trigger, that also won't work with assistive applications that try to pick your menu items, because, of course, that menu selection is also going to happen completely behind your back. So you're going to have to do that. So your only way to hook in to an assistive application's triggering of controls and menus is through H I commands.

We've talked about this in the past. We've got some documentation out there, but basically H I commands are a simple way to put these four character constants into your controls and menus, and you will be informed when a menu item is chosen or a control is clicked by that command ID through Carbon events.

So, you can see here, the button's command ID is sent out first to the button, then to the button's parent, things like that. We have a feature in Jaguar whereby I think controls and menus can have their commands. I'm looking for Eric to nod. Both controls and menus can have their commands sent to the user focus.

Okay. Okay. So menus automatically have their commands sent to the user focus, and I think controls can, too. So just put a bunch of the standard or custom commands in your menu items and your controls, and then take out all that code you do after calling handle control click, where you look at it, see if it was a button part, see what control was hitting, all that stuff.

Take out all the post menu select code that you do with the big switch case statements, and turn that into Carbon event handlers that listen for the k event command process Carbon event. You can put those on your windows or your controls or even the application as you find easiest.

So the third major piece of functionality with respect to accessibility are notifications. These are ways that a UI element can say, "Hey, something happened to me. You know, my value changed if I'm a scroll bar," or, you know, "A window moved," or something like that. An assistive application will register for specific notifications on your app, but you want to go ahead and call the API that I'll show you here in a second whenever anything happens to your HI object.

And the system will make sure to only send a cross-process message if somebody's actually interested in hearing that notification cross-process. And of course, we're going to send out all the normal notifications for all of our standard widgets, and all you need to do is the work for your custom implementations. And again, we've got lots of standard notification strings in the header. And that is probably the most influx suite of constants.

We were just talking about it the other day. We need to figure out what the right set of notifications is that still provides a rich enough set of information for assistive apps, but isn't cumbersome for people to adopt, particularly us. You know, we've got a lot of controls to send out information for. So we need to find the right balance.

So the notification posting example is really simple. It's just one line of code. You don't actually supply a UI element to this API. It would be kind of silly considering we're just going to take that UI element apart in the implementation. So you just call the notify API, pass the notification you're interested in, and then the two separate component parts of the UI element. You pass your HI object, ref, and the identifier. And the engine will do the right thing. It will send it cross-process if it needs to, or it will just be nice and quiet if it doesn't need to.

So some of my examples today were in the text space. And the reason for that is text is probably the most important thing to represent for a truly accessible application. A huge space of assistive applications are completely broken if you can't report the text in your app. So please, when you're implementing your custom accessibility support, make sure all the text in your app is reported as best as possible.

Even if you can just do something really simple, which is, OK, I've got an HI object that represents the entire text of my document, that's going to be more useful than nothing. But if you can make it richer, hey, great. You know, start reporting paragraphs or individual words if you can.

And keyboard navigation is also a big part of accessibility, but it's sort of a separate piece of adoption. We've greatly expanded what parts of the system are keyboard navigable in Jaguar. You can switch up to the menu bar and start choosing items and navigate between menus. You can navigate between windows in the whole system or windows in your application. You can move the focus to toolbars, and you can now focus to a much wider variety of controls. We've tried to make all the system controls keyboard navigable.

And we'd really like you guys to make the custom parts of your application keyboard navigable. I mean, you're going to fit in better on Jaguar, because it's going to look really strange when, hey, I can focus to the standard system push buttons, but I can't focus to this application's custom push buttons.

Why? But it also is going to make assistive apps easier, and it'll make users of assistive apps also have an easier time, because a lot of them can't use the mouse, and they rely on using the keyboard as much as possible. I talked an awful lot about keyboard navigation yesterday in our new controls and services session. If you didn't get a chance to go to that and see all the details, grab the DVD and take a look at that.

I mentioned this a couple times, but this is all still in flux. We really want to hear your feedback, but more importantly, some of the stuff I've talked about today isn't implemented yet. The notification API, the Carbon side of that is not implemented. That will be implemented, obviously, by the time we ship.

And likewise, the ability for you to mark an HI object as ignored is also not exported. And we're going to try to nail down the set of attributes and actions and notifications as soon as we can, and we'd love to hear your guys' opinions. We've taken a look at lots of other accessibility solutions, and there's lots of ways of doing things. We've got our own opinions on it, but your feedback will be really important to us.

And likewise, if you get a chance to try out Jaguar and you play with the various sample apps that they showed in the last session, you'll see that there's a wide variety of levels of support in the system. Right now, the work I've been doing and my fellow teammates have been doing on Carbon, trails a little bit behind Cocoa. So you'll see some applications that report tons and tons of stuff, and you go in other applications and it supports a smaller subset of stuff.

Realize that we're still working on this. We are going to make sure that we have full parity between Carbon and Cocoa, and that if you get a UI element that represents a button, if you're an assistive app, you shouldn't be able to tell whether that's a Carbon or Cocoa button. And we're going to try to make sure of that by reporting the exact same set of actions and attributes and stuff like that.

So to sum up, there's about four things you want to do to make your app accessible. Make sure you switch over to using HI commands. Put those in your menus and your controls. Because without that, assistive apps will not be able to drive your interface. And then make sure to wire up your custom content for the appropriate accessibility Carbon events. And send notifications for any meaningful state change that happened in your HI objects. And get keyboard navigation in there. You know, you may be thinking of that as an accessibility feature, but it's really just a general user feature, and everybody's going to find that useful.

We've already got documentation out there. I think both of these are on the CD. No, it looks like one's on the -- oh, maybe both are on the web. We have documentation that takes you over what I've talked about and what they talked about in the last session, the overview and the C APIs that an assistive application would use. And we also have a white paper that kind of explains what we're doing from a conceptual point of view, gives us some of the background of what we're doing and some of our thinking behind it. So check those out if you can.

And unfortunately, everything on the roadmaps already happened, so if you didn't get a chance to go to these various sessions, get the DVD, check them out. We talk about a lot of important information in there. In particular, session 204 is where we define HI objects and how we show how they're the future, and we explain how controls, windows, and menus all fit into the notion of the HI object. And we'll show you how to-- that session also shows you how to create HI objects and manage them. And you'll need to know how to do that when you're making custom parts of your app accessible.

And session 203 talks about the Carbon Event basics, how to install Carbon Event handlers, how to handle them, and things like that. So if you're unfamiliar with Carbon Events and this is gonna be your first foray in, you want to check out that session, get some of the basics.

The session right before this was the Accessibility Overview, where it talks about maybe more conceptual stuff, and it also shows you the APIs you need to use if you want to write an assistive app, like a screen reader. and Travis Brown is our WWDR contact. So if you have any questions or feedback about this stuff, drop him an email. And I'd like to bring him up now to lead us through some Q&A. Travis.