[orca] Add custom support for ARIA switch role
- From: Joanmarie Diggs <joanied src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [orca] Add custom support for ARIA switch role
- Date: Tue, 24 Jan 2017 14:40:15 +0000 (UTC)
commit d83325d4d945b01ce734330a09e70e647a82022d
Author: Joanmarie Diggs <jdiggs igalia com>
Date: Tue Jan 24 15:38:44 2017 +0100
Add custom support for ARIA switch role
src/orca/formatting.py | 18 ++++++++
src/orca/generator.py | 22 ++++++++++
src/orca/object_properties.py | 17 +++++++
src/orca/script_utilities.py | 3 +
src/orca/scripts/web/script_utilities.py | 6 +++
src/orca/sound_generator.py | 13 ++++++
src/orca/speech_generator.py | 11 +++++
test/html/aria-switch.html | 8 ++++
test/keystrokes/firefox/aria_switch.params | 1 +
test/keystrokes/firefox/aria_switch.py | 64 ++++++++++++++++++++++++++++
10 files changed, 163 insertions(+), 0 deletions(-)
---
diff --git a/src/orca/formatting.py b/src/orca/formatting.py
index 23ff21f..c524e87 100644
--- a/src/orca/formatting.py
+++ b/src/orca/formatting.py
@@ -59,6 +59,7 @@ formatting = {
'insensitive': object_properties.STATE_INSENSITIVE_SPEECH,
'checkbox': object_properties.CHECK_BOX_INDICATORS_SPEECH,
'radiobutton': object_properties.RADIO_BUTTON_INDICATORS_SPEECH,
+ 'switch': object_properties.SWITCH_INDICATORS_SPEECH,
'togglebutton': object_properties.TOGGLE_BUTTON_INDICATORS_SPEECH,
'expansion': object_properties.EXPANSION_INDICATORS_SPEECH,
'nodelevel': object_properties.NODE_LEVEL_SPEECH,
@@ -77,6 +78,7 @@ formatting = {
'invalid': object_properties.INVALID_INDICATORS_BRAILLE,
'checkbox': object_properties.CHECK_BOX_INDICATORS_BRAILLE,
'radiobutton': object_properties.RADIO_BUTTON_INDICATORS_BRAILLE,
+ 'switch': object_properties.SWITCH_INDICATORS_BRAILLE,
'togglebutton': object_properties.TOGGLE_BUTTON_INDICATORS_BRAILLE,
'expansion': object_properties.EXPANSION_INDICATORS_BRAILLE,
'nodelevel': object_properties.NODE_LEVEL_BRAILLE,
@@ -89,6 +91,7 @@ formatting = {
'insensitive': object_properties.STATE_INSENSITIVE_SOUND,
'checkbox': object_properties.CHECK_BOX_INDICATORS_SOUND,
'radiobutton': object_properties.RADIO_BUTTON_INDICATORS_SOUND,
+ 'switch': object_properties.SWITCH_INDICATORS_SOUND,
'togglebutton': object_properties.TOGGLE_BUTTON_INDICATORS_SOUND,
'expansion': object_properties.EXPANSION_INDICATORS_SOUND,
'multiselect': object_properties.STATE_MULTISELECT_SOUND,
@@ -392,6 +395,12 @@ formatting = {
'ROLE_STATIC': {
'unfocused': '(displayedText or name) + roleName',
},
+ 'ROLE_SWITCH': {
+ 'focused': 'switchState',
+ 'unfocused': 'labelOrName + roleName + switchState + availability + ' + MNEMONIC + ' +
accelerator',
+ 'basicWhereAmI': 'labelOrName + roleName + switchState'
+ },
+
pyatspi.ROLE_TABLE: {
'focused': 'leaving or (labelAndName + pause + table)',
'unfocused': 'labelAndName + pause + table',
@@ -687,6 +696,11 @@ formatting = {
+ (required and [Region(" " + asString(required))] or [])\
+ (readOnly and [Region(" " + asString(readOnly))] or [])'
},
+ 'ROLE_SWITCH' : {
+ 'unfocused': '[Component(obj,\
+ asString((labelOrName or description) + roleName),\
+ indicator=asString(switchState))]'
+ },
#pyatspi.ROLE_SPLIT_PANE: 'default'
#pyatspi.ROLE_TABLE: 'default'
pyatspi.ROLE_TABLE_CELL: {
@@ -841,6 +855,10 @@ formatting = {
'focused': 'percentage',
'unfocused': 'roleName + percentage + availability',
},
+ 'ROLE_SWITCH': {
+ 'focused': 'switchState',
+ 'unfocused': 'roleName + switchState + availability',
+ },
pyatspi.ROLE_TABLE_CELL: {
'focused': 'expandableState',
'unfocused': 'roleName + expandableState',
diff --git a/src/orca/generator.py b/src/orca/generator.py
index 500cde1..c1bbd22 100644
--- a/src/orca/generator.py
+++ b/src/orca/generator.py
@@ -587,6 +587,20 @@ class Generator:
return []
+ def _generateSwitchState(self, obj, **args):
+ result = []
+ if not args.get('mode', None):
+ args['mode'] = self._mode
+ args['stringType'] = 'switch'
+ indicators = self._script.formatting.getString(**args)
+ state = obj.getState()
+ if state.contains(pyatspi.STATE_CHECKED) \
+ or state.contains(pyatspi.STATE_PRESSED):
+ result.append(indicators[1])
+ else:
+ result.append(indicators[0])
+ return result
+
def _generateToggleState(self, obj, **args):
"""Returns an array of strings for use by speech and braille that
represent the checked state of the object. This is typically
@@ -1170,6 +1184,8 @@ class Generator:
return 'ROLE_MATH_TABLE_ROW'
if self._script.utilities.isStatic(obj):
return 'ROLE_STATIC'
+ if self._script.utilities.isSwitch(obj):
+ return 'ROLE_SWITCH'
if self._script.utilities.isBlockquote(obj):
return pyatspi.ROLE_BLOCK_QUOTE
if self._script.utilities.isLandmark(obj):
@@ -1214,6 +1230,9 @@ class Generator:
if self._script.utilities.isMenuButton(obj):
return object_properties.ROLE_MENU_BUTTON
+ if self._script.utilities.isSwitch(obj):
+ return object_properties.ROLE_SWITCH
+
if self._script.utilities.isLandmark(obj):
if self._script.utilities.isLandmarkBanner(obj):
return object_properties.ROLE_LANDMARK_BANNER
@@ -1246,6 +1265,9 @@ class Generator:
return Atk.role_get_localized_name(atkRole)
def getStateIndicator(self, obj, **args):
+ if self._script.utilities.isSwitch(obj):
+ return self._generateSwitchState(obj, **args)
+
role = args.get('role', obj.getRole())
if role == pyatspi.ROLE_MENU_ITEM:
diff --git a/src/orca/object_properties.py b/src/orca/object_properties.py
index a9a11ff..45a9f72 100644
--- a/src/orca/object_properties.py
+++ b/src/orca/object_properties.py
@@ -139,6 +139,12 @@ ROLE_SPLITTER_HORIZONTAL = _("horizontal splitter")
# dictate which keyboard keys should be used to modify the value of the widget.
ROLE_SPLITTER_VERTICAL = _("vertical splitter")
+# Translators: This string should be treated as a role describing an object.
+# Examples of roles include "checkbox", "radio button", "paragraph", and "link."
+# The "switch" role is a "light switch" style toggle, such as can be seen in
+# https://developer.gnome.org/gtk3/stable/GtkSwitch.html
+ROLE_SWITCH = C_("role", "switch")
+
# Translators: This is an alternative name for the parent object of a series
# of icons.
ROLE_ICON_PANEL = _("Icon panel")
@@ -240,6 +246,14 @@ STATE_CHECKED = C_("checkbox", "checked")
# Translators: This is a state which applies to a check box.
STATE_NOT_CHECKED = C_("checkbox", "not checked")
+# Translators: This is a state which applies to a switch. For an example of
+# a switch, see https://developer.gnome.org/gtk3/stable/GtkSwitch.html
+STATE_ON_SWITCH = C_("switch", "on")
+
+# Translators: This is a state which applies to a switch. For an example of
+# a switch, see https://developer.gnome.org/gtk3/stable/GtkSwitch.html
+STATE_OFF_SWITCH = C_("switch", "off")
+
# Translators: This is a state which applies to a check box.
STATE_PARTIALLY_CHECKED = C_("checkbox", "partially checked")
@@ -344,6 +358,7 @@ INVALID_INDICATORS_SPEECH = \
STATE_INVALID_GRAMMAR_SPEECH]
RADIO_BUTTON_INDICATORS_SPEECH = \
[STATE_UNSELECTED_RADIO_BUTTON, STATE_SELECTED_RADIO_BUTTON]
+SWITCH_INDICATORS_SPEECH = [STATE_OFF_SWITCH, STATE_ON_SWITCH]
TOGGLE_BUTTON_INDICATORS_SPEECH = \
[STATE_NOT_PRESSED, STATE_PRESSED]
@@ -353,6 +368,7 @@ INVALID_INDICATORS_BRAILLE = [STATE_INVALID_BRAILLE,
STATE_INVALID_SPELLING_BRAILLE,
STATE_INVALID_GRAMMAR_BRAILLE]
RADIO_BUTTON_INDICATORS_BRAILLE = ["& y", "&=y"]
+SWITCH_INDICATORS_BRAILLE = ["& y", "&=y"]
TOGGLE_BUTTON_INDICATORS_BRAILLE = ["& y", "&=y"]
TABLE_CELL_DELIMITER_BRAILLE = " "
@@ -362,6 +378,7 @@ CHECK_BOX_INDICATORS_SOUND = ["not_checked", "checked", "partially_checked"]
EXPANSION_INDICATORS_SOUND = ["collapsed", "expanded"]
INVALID_INDICATORS_SOUND = ["invalid", "invalid_spelling", "invalid_grammar"]
RADIO_BUTTON_INDICATORS_SOUND = ["unselected", "selected"]
+SWITCH_INDICATORS_SOUND = ["off", "on"]
TOGGLE_BUTTON_INDICATORS_SOUND = ["not_pressed", "pressed"]
STATE_CLICKABLE_SOUND = "clickable"
STATE_HAS_LONGDESC_SOUND = "haslongdesc"
diff --git a/src/orca/script_utilities.py b/src/orca/script_utilities.py
index 05b7e81..623d2fd 100644
--- a/src/orca/script_utilities.py
+++ b/src/orca/script_utilities.py
@@ -1291,6 +1291,9 @@ class Utilities:
and not state.contains(pyatspi.STATE_EDITABLE)
return readOnly
+ def isSwitch(self, obj):
+ return False
+
def _hasSamePath(self, obj1, obj2):
path1 = pyatspi.utils.getPath(obj1)
path2 = pyatspi.utils.getPath(obj2)
diff --git a/src/orca/scripts/web/script_utilities.py b/src/orca/scripts/web/script_utilities.py
index f29473d..a24464e 100644
--- a/src/orca/scripts/web/script_utilities.py
+++ b/src/orca/scripts/web/script_utilities.py
@@ -2461,6 +2461,12 @@ class Utilities(script_utilities.Utilities):
self._hasUselessCanvasDescendant[hash(obj)] = rv
return rv
+ def isSwitch(self, obj):
+ if not (obj and self.inDocumentContent(obj)):
+ return super().isSwitch(obj)
+
+ return 'switch' in self._getXMLRoles(obj)
+
def isImageMap(self, obj):
if not (obj and self.inDocumentContent(obj)):
return False
diff --git a/src/orca/sound_generator.py b/src/orca/sound_generator.py
index c8cbafe..e96c27d 100644
--- a/src/orca/sound_generator.py
+++ b/src/orca/sound_generator.py
@@ -236,6 +236,19 @@ class SoundGenerator(generator.Generator):
return []
+ def _generateSwitchState(self, obj, **args):
+ """Returns an array of sounds indicating the on/off state of obj."""
+
+ if not _settingsManager.getSetting('playSoundForState'):
+ return []
+
+ filenames = super()._generateSwitchState(obj, **args)
+ result = list(map(self._convertFilenameToIcon, filenames))
+ if result:
+ return result
+
+ return []
+
def _generateToggleState(self, obj, **args):
"""Returns an array of sounds indicating the toggled state of obj."""
diff --git a/src/orca/speech_generator.py b/src/orca/speech_generator.py
index f9c1cf0..ff15644 100644
--- a/src/orca/speech_generator.py
+++ b/src/orca/speech_generator.py
@@ -532,6 +532,17 @@ class SpeechGenerator(generator.Generator):
result.extend(acss)
return result
+ def _generateSwitchState(self, obj, **args):
+ """Returns an array of strings indicating the on/off state of obj."""
+ if _settingsManager.getSetting('onlySpeakDisplayedText'):
+ return []
+
+ acss = self.voice(STATE)
+ result = generator.Generator._generateSwitchState(self, obj, **args)
+ if result:
+ result.extend(acss)
+ return result
+
def _generateToggleState(self, obj, **args):
"""Returns an array of strings for use by speech and braille that
represent the checked state of the object. This is typically
diff --git a/test/html/aria-switch.html b/test/html/aria-switch.html
new file mode 100644
index 0000000..545ac74
--- /dev/null
+++ b/test/html/aria-switch.html
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+ <div>Line 1</div>
+ <div>Line 2: <input type="checkbox" aria-label="Power saving" role="switch" /></div>
+ <div>Line 3</div>
+</body>
+</html>
diff --git a/test/keystrokes/firefox/aria_switch.params b/test/keystrokes/firefox/aria_switch.params
new file mode 100644
index 0000000..6e3b4a9
--- /dev/null
+++ b/test/keystrokes/firefox/aria_switch.params
@@ -0,0 +1 @@
+PARAMS=$TEST_DIR/../../html/aria-switch.html
diff --git a/test/keystrokes/firefox/aria_switch.py b/test/keystrokes/firefox/aria_switch.py
new file mode 100644
index 0000000..ad1ffdb
--- /dev/null
+++ b/test/keystrokes/firefox/aria_switch.py
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+
+"""Test of ARIA switch presentation."""
+
+from macaroon.playback import *
+import utils
+
+sequence = MacroSequence()
+
+#sequence.append(WaitForDocLoad())
+sequence.append(PauseAction(5000))
+sequence.append(KeyComboAction("<Control>Home"))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Tab"))
+sequence.append(utils.AssertPresentationAction(
+ "1. Tab to switch",
+ ["BRAILLE LINE: '& y Power saving switch'",
+ " VISIBLE: '& y Power saving switch', cursor=1",
+ "SPEECH OUTPUT: 'Power saving switch off'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(TypeAction(" "))
+sequence.append(utils.AssertPresentationAction(
+ "2. Change state of switch",
+ ["BRAILLE LINE: '&=y Power saving switch'",
+ " VISIBLE: '&=y Power saving switch', cursor=1",
+ "SPEECH OUTPUT: 'on'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(TypeAction(" "))
+sequence.append(utils.AssertPresentationAction(
+ "3. Change state of switch",
+ ["BRAILLE LINE: '& y Power saving switch'",
+ " VISIBLE: '& y Power saving switch', cursor=1",
+ "SPEECH OUTPUT: 'off'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("KP_Enter"))
+sequence.append(utils.AssertPresentationAction(
+ "4. Basic whereAmI",
+ ["BRAILLE LINE: '& y Power saving switch'",
+ " VISIBLE: '& y Power saving switch', cursor=1",
+ "SPEECH OUTPUT: 'Power saving switch off'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Up"))
+sequence.append(utils.AssertPresentationAction(
+ "5. Up arrow",
+ ["BRAILLE LINE: 'Line 1'",
+ " VISIBLE: 'Line 1', cursor=1",
+ "SPEECH OUTPUT: 'Line 1'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Down"))
+sequence.append(utils.AssertPresentationAction(
+ "6. Down arrow",
+ ["BRAILLE LINE: 'Line 2: & y Power saving switch'",
+ " VISIBLE: 'Line 2: & y Power saving switch', cursor=1",
+ "SPEECH OUTPUT: 'Line 2:'",
+ "SPEECH OUTPUT: 'Power saving switch off'"]))
+
+sequence.append(utils.AssertionSummaryAction())
+sequence.start()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]