[orca] Add support for clickables
- From: Joanmarie Diggs <joanied src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [orca] Add support for clickables
- Date: Thu, 14 Aug 2014 22:12:08 +0000 (UTC)
commit d6f1eede88bd1d9ac93a5d8054ed7b02168bb01e
Author: Joanmarie Diggs <jdiggs igalia com>
Date: Thu Aug 14 17:48:39 2014 -0400
Add support for clickables
src/orca/cmdnames.py | 20 ++-
src/orca/formatting.py | 1 +
src/orca/generator.py | 10 ++
src/orca/guilabels.py | 12 ++
src/orca/messages.py | 10 +-
src/orca/object_properties.py | 4 +
src/orca/script_utilities.py | 3 +
.../scripts/toolkits/Gecko/braille_generator.py | 2 +
src/orca/scripts/toolkits/Gecko/formatting.py | 2 +-
src/orca/scripts/toolkits/Gecko/script.py | 8 +-
.../scripts/toolkits/Gecko/script_utilities.py | 20 +++
src/orca/scripts/toolkits/WebKitGtk/script.py | 3 +-
src/orca/speech_generator.py | 10 ++
src/orca/structural_navigation.py | 160 ++++++++++----------
14 files changed, 164 insertions(+), 101 deletions(-)
---
diff --git a/src/orca/cmdnames.py b/src/orca/cmdnames.py
index 01787df..2b71686 100644
--- a/src/orca/cmdnames.py
+++ b/src/orca/cmdnames.py
@@ -736,14 +736,6 @@ PRESENT_INPUT_LINE = _("Presents the contents of the input line.")
# writing functions.
STRUCTURAL_NAVIGATION_TOGGLE = _("Toggles structural navigation keys.")
-# Translators: this is for navigating among anchors in a document. An anchor is
-# a named spot that one can jump to.
-ANCHOR_PREV = _("Goes to previous anchor.")
-
-# Translators: this is for navigating among anchors in a document. An anchor is
-# a named spot that one can jump to.
-ANCHOR_NEXT = _("Goes to next anchor.")
-
# Translators: this is for navigating among blockquotes in a document.
BLOCKQUOTE_PREV = _("Goes to previous blockquote.")
@@ -771,6 +763,18 @@ CHECK_BOX_NEXT = _("Goes to next check box.")
# Translators: this is for navigating among check boxes in a document.
CHECK_BOX_LIST = _("Displays a list of check boxes.")
+# Translators: this is for navigating among clickable objects in a document.
+# A "clickable" is a web element with an "onClick" handler.
+CLICKABLE_PREV = _("Goes to previous clickable.")
+
+# Translators: this is for navigating among clickable objects in a document.
+# A "clickable" is a web element with an "onClick" handler.
+CLICKABLE_NEXT = _("Goes to next clickable.")
+
+# Translators: this is for navigating among clickable objects in a document.
+# A "clickable" is a web element with an "onClick" handler.
+CLICKABLE_LIST = _("Displays a list of clickables.")
+
# Translators: this is for navigating among combo boxes in a document.
COMBO_BOX_PREV = _("Goes to previous combo box.")
diff --git a/src/orca/formatting.py b/src/orca/formatting.py
index 8ebbc35..08ce450 100644
--- a/src/orca/formatting.py
+++ b/src/orca/formatting.py
@@ -64,6 +64,7 @@ formatting = {
'multiselect': object_properties.STATE_MULTISELECT_SPEECH,
'iconindex': object_properties.ICON_INDEX_SPEECH,
'groupindex': object_properties.GROUP_INDEX_SPEECH,
+ 'clickable': object_properties.STATE_CLICKABLE,
},
'braille': {
'eol': object_properties.EOL_INDICATOR_BRAILLE,
diff --git a/src/orca/generator.py b/src/orca/generator.py
index 7254e4b..83023eb 100644
--- a/src/orca/generator.py
+++ b/src/orca/generator.py
@@ -411,6 +411,16 @@ class Generator:
# #
#####################################################################
+ def _generateClickable(self, obj, **args):
+ if not args.get('mode', None):
+ args['mode'] = self._mode
+ args['stringType'] = 'clickable'
+
+ if self._script.utilities.isClickableElement(obj):
+ return [self._script.formatting.getString(**args)]
+
+ return []
+
def _generateAvailability(self, obj, **args):
"""Returns an array of strings for use by speech and braille that
represent the grayed/sensitivity/availability state of the
diff --git a/src/orca/guilabels.py b/src/orca/guilabels.py
index df5d4c6..ba7674d 100644
--- a/src/orca/guilabels.py
+++ b/src/orca/guilabels.py
@@ -372,6 +372,12 @@ SN_HEADER_CHECK_BOX = C_("structural navigation", "Check Box")
# Translators: Orca has a command that presents a list of structural navigation
# objects in a dialog box so that users can navigate more quickly than they
# could with native keyboard navigation. This is the title for a column which
+# contains the text displayed for a web element with an "onClick" handler.
+SN_HEADER_CLICKABLE = C_("structural navigation", "Clickable")
+
+# Translators: Orca has a command that presents a list of structural navigation
+# objects in a dialog box so that users can navigate more quickly than they
+# could with native keyboard navigation. This is the title for a column which
# contains the selected item in a combo box.
SN_HEADER_COMBO_BOX = C_("structural navigation", "Combo Box")
@@ -500,6 +506,12 @@ SN_TITLE_CHECK_BOX = C_("structural navigation", "Check Boxes")
# Translators: Orca has a command that presents a list of structural navigation
# objects in a dialog box so that users can navigate more quickly than they
# could with native keyboard navigation. This is the title of such a dialog box.
+# "Clickables" are web elements which have an "onClick" handler.
+SN_TITLE_CLICKABLE = C_("structural navigation", "Clickables")
+
+# Translators: Orca has a command that presents a list of structural navigation
+# objects in a dialog box so that users can navigate more quickly than they
+# could with native keyboard navigation. This is the title of such a dialog box.
SN_TITLE_COMBO_BOX = C_("structural navigation", "Combo Boxes")
# Translators: Orca has a command that presents a list of structural navigation
diff --git a/src/orca/messages.py b/src/orca/messages.py
index f98e158..1fc1bc7 100644
--- a/src/orca/messages.py
+++ b/src/orca/messages.py
@@ -1147,11 +1147,6 @@ NO_FOCUS = _("No focus")
# has keyboard focus.
NO_FOCUSED_APPLICATION = _("No application has focus.")
-# Translators: This is for navigating document content by moving from anchor to
-# anchor. (An anchor is a named spot that one can jump to.) This is a detailed
-# message which will be presented to the user if no more anchors can be found.
-NO_MORE_ANCHORS = _("No more anchors.")
-
# Translators: This is for navigating document content by moving from blockquote
# to blockquote. This is a detailed message which will be presented to the user
# if no more blockquotes can be found.
@@ -1173,6 +1168,11 @@ NO_MORE_CHECK_BOXES = _("No more check boxes.")
# will be presented to the user if no more check boxes can be found.
NO_MORE_CHUNKS = _("No more large objects.")
+# Translators: This is for navigating document content by moving amongst web
+# elements which have an "onClick" action. This is a detailed message which
+# will be presented to the user if no more clickable elements can be found.
+NO_MORE_CLICKABLES = _("No more clickables.")
+
# Translators: This is for navigating document content by moving from combo
# box to combo box. This is a detailed message which will be presented to the
# user if no more combo boxes can be found.
diff --git a/src/orca/object_properties.py b/src/orca/object_properties.py
index 5d24faa..bd50294 100644
--- a/src/orca/object_properties.py
+++ b/src/orca/object_properties.py
@@ -76,6 +76,10 @@ ROLE_HEADING_LEVEL_SPEECH = _("%(role)s level %(level)d")
# of icons.
ROLE_ICON_PANEL = _("Icon panel")
+# Translators: This is a state which applies to elements in document content
+# which have an "onClick" action.
+STATE_CLICKABLE = _("clickable")
+
# Translators: This is a state which applies to items which can be expanded
# or collapsed such as combo boxes and nodes/groups in a treeview. Collapsed
# means the item's children are not showing; expanded means they are.
diff --git a/src/orca/script_utilities.py b/src/orca/script_utilities.py
index 15430c8..96f4aab 100644
--- a/src/orca/script_utilities.py
+++ b/src/orca/script_utilities.py
@@ -2719,3 +2719,6 @@ class Utilities:
red, green, blue = string.split(",")
return int(red), int(green), int(blue)
+
+ def isClickableElement(self, obj):
+ return False
diff --git a/src/orca/scripts/toolkits/Gecko/braille_generator.py
b/src/orca/scripts/toolkits/Gecko/braille_generator.py
index 7772698..6ccf24e 100644
--- a/src/orca/scripts/toolkits/Gecko/braille_generator.py
+++ b/src/orca/scripts/toolkits/Gecko/braille_generator.py
@@ -175,6 +175,8 @@ class BrailleGenerator(braille_generator.BrailleGenerator):
oldRole = None
if self._script.utilities.isEntry(obj):
oldRole = self._overrideRole(pyatspi.ROLE_ENTRY, args)
+ elif self._script.utilities.isClickableElement(obj):
+ oldRole = self._overrideRole(pyatspi.ROLE_LINK, args)
# Treat menu items in collapsed combo boxes as if the combo box
# had focus. This will make things more consistent with how we
diff --git a/src/orca/scripts/toolkits/Gecko/formatting.py b/src/orca/scripts/toolkits/Gecko/formatting.py
index d82f624..b4ffa93 100644
--- a/src/orca/scripts/toolkits/Gecko/formatting.py
+++ b/src/orca/scripts/toolkits/Gecko/formatting.py
@@ -36,7 +36,7 @@ formatting = {
'speech': {
'suffix': {
'focused': '[]',
- 'unfocused': 'newNodeLevel + unselectedCell + ' + orca.formatting.TUTORIAL + ' + description ',
+ 'unfocused': 'newNodeLevel + unselectedCell + clickable + ' + orca.formatting.TUTORIAL + ' +
description ',
'basicWhereAmI': orca.formatting.TUTORIAL + ' + description + liveRegionDescription',
'detailedWhereAmI' : '[]'
},
diff --git a/src/orca/scripts/toolkits/Gecko/script.py b/src/orca/scripts/toolkits/Gecko/script.py
index b00407c..0a985b1 100644
--- a/src/orca/scripts/toolkits/Gecko/script.py
+++ b/src/orca/scripts/toolkits/Gecko/script.py
@@ -282,11 +282,11 @@ class Script(default.Script):
enabled in this script.
"""
- enabledTypes = [GeckoStructuralNavigation.ANCHOR,
- GeckoStructuralNavigation.BLOCKQUOTE,
+ enabledTypes = [GeckoStructuralNavigation.BLOCKQUOTE,
GeckoStructuralNavigation.BUTTON,
GeckoStructuralNavigation.CHECK_BOX,
GeckoStructuralNavigation.CHUNK,
+ GeckoStructuralNavigation.CLICKABLE,
GeckoStructuralNavigation.COMBO_BOX,
GeckoStructuralNavigation.ENTRY,
GeckoStructuralNavigation.FORM_FIELD,
@@ -1508,6 +1508,7 @@ class Script(default.Script):
if (not len(string) and role != pyatspi.ROLE_PARAGRAPH) \
or self.utilities.isEntry(obj) \
or self.utilities.isPasswordText(obj) \
+ or self.utilities.isClickableElement(obj) \
or role in [pyatspi.ROLE_LINK, pyatspi.ROLE_PUSH_BUTTON]:
[regions, fRegion] = \
self.brailleGenerator.generateBraille(obj)
@@ -3147,7 +3148,8 @@ class Script(default.Script):
if not len(string) \
or self.utilities.isEntry(obj) \
or self.utilities.isPasswordText(obj) \
- or role == pyatspi.ROLE_PUSH_BUTTON and obj.name:
+ or role == pyatspi.ROLE_PUSH_BUTTON and obj.name \
+ or self.utilities.isClickableElement(obj):
rv = self.speechGenerator.generateSpeech(obj)
# Crazy crap to make clump and friends happy until we can
# kill them. (They don't deal well with what the speech
diff --git a/src/orca/scripts/toolkits/Gecko/script_utilities.py
b/src/orca/scripts/toolkits/Gecko/script_utilities.py
index 81c4cac..56ebe71 100644
--- a/src/orca/scripts/toolkits/Gecko/script_utilities.py
+++ b/src/orca/scripts/toolkits/Gecko/script_utilities.py
@@ -599,3 +599,23 @@ class Utilities(script_utilities.Utilities):
objects.extend(toAdd)
return objects
+
+ def isClickableElement(self, obj):
+ # For Gecko, we want to identify things which are ONLY clickable.
+ # Things which are focusable, while technically "clickable", are
+ # easily discoverable (e.g. via role) and activatable (e.g. via
+ # pressing Space or Enter.
+ state = obj.getState()
+ if state.contains(pyatspi.STATE_FOCUSABLE):
+ return False
+
+ try:
+ action = obj.queryAction()
+ except NotImplementedError:
+ return False
+
+ for i in range(action.nActions):
+ if action.getName(i) in ["click"]:
+ return True
+
+ return False
diff --git a/src/orca/scripts/toolkits/WebKitGtk/script.py b/src/orca/scripts/toolkits/WebKitGtk/script.py
index 8e0b4a7..01ba504 100644
--- a/src/orca/scripts/toolkits/WebKitGtk/script.py
+++ b/src/orca/scripts/toolkits/WebKitGtk/script.py
@@ -151,8 +151,7 @@ class Script(default.Script):
"""Returns a list of the structural navigation object types
enabled in this script."""
- enabledTypes = [StructuralNavigation.ANCHOR,
- StructuralNavigation.BLOCKQUOTE,
+ enabledTypes = [StructuralNavigation.BLOCKQUOTE,
StructuralNavigation.BUTTON,
StructuralNavigation.CHECK_BOX,
StructuralNavigation.CHUNK,
diff --git a/src/orca/speech_generator.py b/src/orca/speech_generator.py
index 179590d..53340c6 100644
--- a/src/orca/speech_generator.py
+++ b/src/orca/speech_generator.py
@@ -222,6 +222,16 @@ class SpeechGenerator(generator.Generator):
result.extend(acss)
return result
+ def _generateClickable(self, obj, **args):
+ if _settingsManager.getSetting('onlySpeakDisplayedText'):
+ return []
+
+ acss = self.voice(SYSTEM)
+ result = generator.Generator._generateClickable(self, obj, **args)
+ if result:
+ result.extend(acss)
+ return result
+
def _generateTextRole(self, obj, **args):
"""A convenience method to prevent the pyatspi.ROLE_PARAGRAPH role
from being spoken. In the case of a pyatspi.ROLE_PARAGRAPH
diff --git a/src/orca/structural_navigation.py b/src/orca/structural_navigation.py
index e9853ee..f0a34d6 100644
--- a/src/orca/structural_navigation.py
+++ b/src/orca/structural_navigation.py
@@ -523,11 +523,11 @@ class StructuralNavigation:
# should be all that is needed to implement navigation by blockquote
# in OOo Writer documents.
#
- ANCHOR = "anchor"
BLOCKQUOTE = "blockquote"
BUTTON = "button"
CHECK_BOX = "checkBox"
CHUNK = "chunk"
+ CLICKABLE = "clickable"
COMBO_BOX = "comboBox"
ENTRY = "entry"
FORM_FIELD = "formField"
@@ -874,6 +874,7 @@ class StructuralNavigation:
criteria.interfaces,
criteria.matchInterfaces,
criteria.invert)
+
if criteria.applyPredicate:
predicate = structuralNavigationObject.predicate
else:
@@ -1780,87 +1781,6 @@ class StructuralNavigation:
########################
# #
- # Anchors #
- # #
- ########################
-
- def _anchorBindings(self):
- """Returns a dictionary of [keysymstring, modifiers, description]
- lists for navigating amongst anchors.
- """
-
- # NOTE: This doesn't handle the case where the anchor is not an
- # old-school <a name/id="foo"></a> anchor. For instance on the
- # GNOME wiki, an "anchor" is actually an id applied to some other
- # tag (e.g. <h2 id="foo">My Heading</h2>. We'll have to be a
- # bit more clever for those. With the old-school anchors, this
- # seems to work nicely and provides the user with a way to jump
- # among defined areas without having to find a Table of Contents
- # group of links (assuming such a thing is even present on the
- # page).
-
- bindings = {}
- prevDesc = cmdnames.ANCHOR_PREV
- bindings["previous"] = ["a", keybindings.SHIFT_MODIFIER_MASK, prevDesc]
-
- nextDesc = cmdnames.ANCHOR_NEXT
- bindings["next"] = ["a", keybindings.NO_MODIFIER_MASK, nextDesc]
- return bindings
-
- def _anchorCriteria(self, collection, arg=None):
- """Returns the MatchCriteria to be used for locating anchors
- by collection.
-
- Arguments:
- - collection: the collection interface for the document
- - arg: an optional argument which may need to be included in
- the criteria (e.g. the level of a heading).
- """
-
- role = [pyatspi.ROLE_LINK]
- state = [pyatspi.STATE_FOCUSABLE]
- stateMatch = collection.MATCH_NONE
- return MatchCriteria(collection,
- states=state,
- matchStates=stateMatch,
- roles=role)
-
- def _anchorPredicate(self, obj, arg=None):
- """The predicate to be used for verifying that the object
- obj is an anchor.
-
- Arguments:
- - obj: the accessible object under consideration.
- - arg: an optional argument which may need to be included in
- the criteria (e.g. the level of a heading).
- """
-
- isMatch = False
- if obj and obj.getRole() == pyatspi.ROLE_LINK:
- state = obj.getState()
- isMatch = not state.contains(pyatspi.STATE_FOCUSABLE)
- return isMatch
-
- def _anchorPresentation(self, obj, arg=None):
- """Presents the anchor or indicates that one was not found.
-
- Arguments:
- - obj: the accessible object under consideration.
- - arg: an optional argument which may need to be included in
- the criteria (e.g. the level of a heading).
- """
-
- if obj:
- [obj, characterOffset] = self._getCaretPosition(obj)
- self._setCaretPosition(obj, characterOffset)
- self._presentObject(obj, characterOffset)
- else:
- full = messages.NO_MORE_ANCHORS
- brief = messages.STRUCTURAL_NAVIGATION_NOT_FOUND
- self._script.presentMessage(full, brief)
-
- ########################
- # #
# Blockquotes #
# #
########################
@@ -3647,3 +3567,79 @@ class StructuralNavigation:
self._script.utilities.uri(obj)]
return guilabels.SN_TITLE_LINK, columnHeaders, rowData
+
+ ########################
+ # #
+ # Clickables #
+ # #
+ ########################
+
+ def _clickableBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst "clickable" objects."""
+
+ bindings = {}
+ prevDesc = cmdnames.CLICKABLE_PREV
+ bindings["previous"] = ["a", keybindings.SHIFT_MODIFIER_MASK, prevDesc]
+
+ nextDesc = cmdnames.CLICKABLE_NEXT
+ bindings["next"] = ["a", keybindings.NO_MODIFIER_MASK, nextDesc]
+
+ listDesc = cmdnames.CLICKABLE_LIST
+ bindings["list"] = ["a", keybindings.SHIFT_ALT_MODIFIER_MASK, listDesc]
+ return bindings
+
+ def _clickableCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating clickables
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ # TODO - JD: At the moment, matching via interface crashes Orca.
+ # Until that's addressed, we'll just use the predicate approach.
+ # See https://bugzilla.gnome.org/show_bug.cgi?id=734805.
+ return MatchCriteria(collection,
+ applyPredicate=True)
+
+ def _clickablePredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a clickable.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ return self._script.utilities.isClickableElement(obj)
+
+ def _clickablePresentation(self, obj, arg=None):
+ """Presents the clickable or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ [obj, characterOffset] = self._getCaretPosition(obj)
+ self._setCaretPosition(obj, characterOffset)
+ self._presentObject(obj, characterOffset)
+ elif not arg:
+ full = messages.NO_MORE_CLICKABLES
+ brief = messages.STRUCTURAL_NAVIGATION_NOT_FOUND
+ self._script.presentMessage(full, brief)
+
+ def _clickableDialogData(self):
+ columnHeaders = [guilabels.SN_HEADER_CLICKABLE]
+ columnHeaders.append(guilabels.SN_HEADER_ROLE)
+
+ def rowData(obj):
+ return [self._getText(obj), self._getRoleName(obj)]
+
+ return guilabels.SN_TITLE_CLICKABLE, columnHeaders, rowData
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]