//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package mx.accessibility { import flash.accessibility.Accessibility; import flash.accessibility.AccessibilityProperties; import flash.events.Event; import flash.system.ApplicationDomain; import mx.accessibility.AccImpl; import mx.core.UIComponent; import mx.core.mx_internal; use namespace mx_internal; /** * UIComponentAccProps is a subclass of AccessibilityProperties * for use by various UIComponents. * It is used to provide accessibility to Form, ToolTip, and Error ToolTip. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public class UIComponentAccProps extends AccessibilityProperties { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Class methods // //-------------------------------------------------------------------------- /** * Enables accessibility in the UIComponent class. * *

This method is called by application startup code * that is autogenerated by the MXML compiler. * Afterwards, when instances of UIComponent are initialized, * their accessibilityProperties property * will be set to an instance of this class.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public static function enableAccessibility():void { UIComponent.createAccessibilityImplementation = createAccessibilityImplementation; } /** * @private * Creates a UIComponent's AccessibilityProperties object. * This method is called from UIComponent's * initializeAccessibility() method. */ mx_internal static function createAccessibilityImplementation( component:UIComponent):void { component.accessibilityProperties = new UIComponentAccProps(component); } /** * @private * Determines whether a component should avoid producing MSAA information * directly. Components that are represented otherwise, such as * a form field's Required Field indicator (which causes "Required field" * to be included in the field's accessible name) should be made silent * in this way. * * @param component The component to check. * * @return true if this component should be silent. */ mx_internal static function componentShouldBeSilent(component:UIComponent):Boolean { // All tests below require Group to exist and to be under a FormItem. var groupClass:Class = Class(AccImpl.getDefinition( "spark.components.Group", component.moduleFactory )); if (!groupClass) return false; var formItem:UIComponent = AccImpl.findMatchingAncestor(component, AccImpl.isFormItem); if (!formItem) return false; // This catches labels for FormItem fields, // and also one object related to the Required Field indicator. // TODO: We might want to silence all Group components under a FormItem, // but this theory remains to be tested and so is not implemented here. // component.parent should be a FormItem skin (possibly a custom one), // so the parent of that should be the FormItem. // The try/catch block prevents an RTE if // component.parent.parent doesn't exist. try { if (component is groupClass && component.parent.parent === formItem) return true; } catch (e:Error) { } // This catches the Required Field graphic itself. // Sought structure: Image in Group in skin in FormItem. var imageClass:Class = Class(AccImpl.getDefinition( "spark.components.Image", component.moduleFactory )); if (!imageClass) return false; try { if (component is imageClass && component.parent is groupClass && component.parent.parent.parent === formItem) return true; } catch (e:Error) { } return false; } //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * * @param master The UIComponent instance that this * AccessibilityProperties instance is making accessible. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function UIComponentAccProps(component:UIComponent) { super(); master = component; if (component.accessibilityProperties) { silent = component.accessibilityProperties.silent; forceSimple = component.accessibilityProperties.forceSimple; noAutoLabeling = component.accessibilityProperties.noAutoLabeling; if (component.accessibilityProperties.name) name = component.accessibilityProperties.name; if (component.accessibilityProperties.description) description = component.accessibilityProperties.description; if (component.accessibilityProperties.shortcut) shortcut = component.accessibilityProperties.shortcut; } if (AccImpl.getMatchingDefinition(master, AccImpl.getDefinitions("ScrollBar", master.moduleFactory) )) { silent = true; return; } if (isFormItemLabel(master)) { // TODO: Why is name set here? Is this name used somewhere? name = AccImpl.getFormName(master); silent = true; return; } // Silence various subparts of Spark forms besides FormItem labels. if (componentShouldBeSilent(component)) { // We can't set silent=true or whole FormItems and their fields // will disappear from MSAA, but if we set name="", the Player // will filter out these items for us. name = ""; return; } // In complex layouts, the text of a FormHeading might not appear // where it should for assistive technology, because the FormHeading's // tabIndex does not also get applied to the text item. // That is fixed here when the FormHeading is being constructed. if (AccImpl.isFormHeading(master) && master.tabIndex > 0) { // Spark-specific solution. try { if (Object(master).labelDisplay.tabIndex == -1) Object(master).labelDisplay.tabIndex = master.tabIndex; } catch (e:Error) { } // TODO: Not solved for MX even though the problem does apply there. } var formName:String = AccImpl.getFormName(master); if (formName && formName.length != 0) name = formName + name; if (master.toolTip && master.toolTip.length != 0) if (!component.accessibilityProperties || (component.accessibilityProperties && !component.accessibilityProperties.name)) { oldToolTip = " " + master.toolTip; name += oldToolTip; } if (master.errorString && master.errorString.length != 0) { oldErrorString = " " + master.errorString; name += oldErrorString; } master.addEventListener("toolTipChanged", eventHandler); master.addEventListener("errorStringChanged", eventHandler); } /** * @private * Determines whether a component is a formItem label (Spark or MX). * * @param component The component to check. * * @return true if this is a label for a formItem. */ protected function isFormItemLabel(component:UIComponent):Boolean { // This handles Spark labels on forms. var thisName:String = master.name; if (thisName == "labelDisplay" || thisName == "sequenceLabelDisplay" || thisName == "helpContentGroup" || thisName == "errorTextDisplay" ) return true; // This handles MX FormItemLabel objects. return Boolean(AccImpl.getMatchingDefinition(master, AccImpl.getDefinitions("FormItemLabel", master.moduleFactory) )); } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private */ private var oldToolTip:String; /** * @private */ private var oldErrorString:String; //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // master //---------------------------------- /** * A reference to the UIComponent itself. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected var master:UIComponent; //-------------------------------------------------------------------------- // // Event handlers // //-------------------------------------------------------------------------- /** * Generic event handler. * All UIComponentAccProps subclasses must implement this * to listen for events from its master component. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function eventHandler(event:Event):void { var pos:int; switch (event.type) { case "errorStringChanged": { if (name && name.length != 0 && oldErrorString) { pos = name.indexOf(oldErrorString); if (pos != -1) { name = name.substring(0, pos) + name.substring(pos + oldErrorString.length); } oldErrorString = null; } if (master.errorString && master.errorString.length != 0) { if (!name) name = ""; oldErrorString = " " + master.errorString; name += oldErrorString; } Accessibility.updateProperties(); break; } case "toolTipChanged": { if (name && name.length != 0 && oldToolTip) { pos = name.indexOf(oldToolTip); if (pos != -1) { name = name.substring(0, pos) + name.substring(pos + oldToolTip.length); } oldToolTip = null; } if (master.toolTip && master.toolTip.length != 0) { if (!master.accessibilityProperties || (master.accessibilityProperties && !master.accessibilityProperties.name)) { if (!name) name = ""; oldToolTip = " " + master.toolTip; name += oldToolTip; } } Accessibility.updateProperties(); break; } } } } }