[orca] Add support for new 'suggestion' role
- From: Joanmarie Diggs <joanied src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [orca] Add support for new 'suggestion' role
- Date: Thu, 23 Jan 2020 19:56:46 +0000 (UTC)
commit 5f91ae95b279ac45a236d7bb806966f51137fd04
Author: Joanmarie Diggs <joanmarie diggs gmail com>
Date: Thu Jan 23 14:56:33 2020 -0500
Add support for new 'suggestion' role
src/orca/formatting.py | 4 ++++
src/orca/generator.py | 5 +++++
src/orca/messages.py | 14 ++++++++++++++
src/orca/object_properties.py | 7 +++++++
src/orca/script_utilities.py | 6 ++++++
src/orca/scripts/web/script_utilities.py | 26 ++++++++++++++++++++++++++
src/orca/scripts/web/speech_generator.py | 3 ++-
src/orca/speech_generator.py | 24 ++++++++++++++++++++++--
8 files changed, 86 insertions(+), 3 deletions(-)
---
diff --git a/src/orca/formatting.py b/src/orca/formatting.py
index 1aa151ac1..a4aeb0530 100644
--- a/src/orca/formatting.py
+++ b/src/orca/formatting.py
@@ -190,6 +190,10 @@ formatting = {
'ROLE_CONTENT_MARK': {
'unfocused': 'markStart + pause + displayedText + pause + markEnd',
},
+ # TODO - JD: When we bump dependencies to 2.36, remove this fake role and use the real one.
+ 'ROLE_CONTENT_SUGGESTION': {
+ 'focused': 'leaving or (labelOrName + roleName)',
+ },
pyatspi.ROLE_DESCRIPTION_TERM: {
'unfocused': '(labelOrName or (displayedText + allTextSelection))',
},
diff --git a/src/orca/generator.py b/src/orca/generator.py
index 34cf3fedd..ca2cfa257 100644
--- a/src/orca/generator.py
+++ b/src/orca/generator.py
@@ -1226,6 +1226,8 @@ class Generator:
return 'ROLE_CONTENT_INSERTION'
if self._script.utilities.isContentMarked(obj):
return 'ROLE_CONTENT_MARK'
+ if self._script.utilities.isContentSuggestion(obj):
+ return 'ROLE_CONTENT_SUGGESTION'
if self._script.utilities.isLandmark(obj):
return pyatspi.ROLE_LANDMARK
if self._script.utilities.isFocusableLabel(obj):
@@ -1265,6 +1267,9 @@ class Generator:
if isVertical:
return object_properties.ROLE_SPLITTER_HORIZONTAL
+ if self._script.utilities.isContentSuggestion(obj):
+ return object_properties.ROLE_CONTENT_SUGGESTION
+
if self._script.utilities.isFeed(obj):
return object_properties.ROLE_FEED
diff --git a/src/orca/messages.py b/src/orca/messages.py
index 42e12a221..58199759a 100644
--- a/src/orca/messages.py
+++ b/src/orca/messages.py
@@ -442,6 +442,13 @@ CONTENT_MARK_START = C_("content", "highlight start")
# is inside an HTML 'mark' element.
CONTENT_MARK_END = C_("content", "highlight end")
+# Translators: This phrase is spoken to inform the user that the content being
+# presented is the end of an inline suggestion a document. A "suggestion" is a
+# proposed change. This change can include the insertion and/or deletion
+# of content, and would typically be seen in a collaborative editor, such as
+# in Google Docs.
+CONTENT_SUGGESTION_END = C_("content", "suggestion end")
+
# Translators: This is for navigating document content by moving to the start
# or end of a container. Examples of containers include tables, lists, and
# blockquotes. When moving to the end of a container, Orca attempts to place
@@ -1380,6 +1387,13 @@ LEAVING_PULLQUOTE = C_("role", "leaving pullquote.")
# for the corresponding term with context "role" found in object_properties.py
LEAVING_QNA = C_("role", "leaving QNA.")
+# Translators: This message is presented when a user is navigating within a
+# suggestion and then navigates out of it. A "suggestion" is a container with
+# a proposed change. This change can include the insertion and/or deletion
+# of content, and would typically be seen in a collaborative editor, such as
+# in Google Docs.
+LEAVING_SUGGESTION = C_("role", "leaving suggestion.")
+
# Translators: This message is presented when a user is navigating within
# a document container and then navigates out of it. The word or phrase
# that follows "leaving" should be consistent with the translation provided
diff --git a/src/orca/object_properties.py b/src/orca/object_properties.py
index bef7cf6a4..5202f4eba 100644
--- a/src/orca/object_properties.py
+++ b/src/orca/object_properties.py
@@ -94,6 +94,13 @@ RELATION_DETAILS_FOR = _("details for %s")
# See https://w3c.github.io/aria/#aria-details
RELATION_HAS_DETAILS = _("has details in %s")
+# Translators: This string should be treated as a role describing an object.
+# Examples of roles include "checkbox", "radio button", "paragraph", and "link."
+# This role refers to a container with a proposed change. This change can
+# include the insertion and/or deletion of content, and would typically be seen
+# in a collaborative editor, such as in Google Docs.
+ROLE_CONTENT_SUGGESTION = C_("role", "suggestion")
+
# Translators: This string should be treated as a role describing an object.
# Examples of roles include "checkbox", "radio button", "paragraph", and "link."
# The reason for including the editable state as part of the role is to make it
diff --git a/src/orca/script_utilities.py b/src/orca/script_utilities.py
index cd513c677..9df886b3c 100644
--- a/src/orca/script_utilities.py
+++ b/src/orca/script_utilities.py
@@ -779,6 +779,12 @@ class Utilities:
def isContentMarked(self, obj):
return False
+ def isContentSuggestion(self, obj):
+ return False
+
+ def isLastItemInInlineContentSuggestion(self, obj):
+ return False
+
def isEmpty(self, obj):
return False
diff --git a/src/orca/scripts/web/script_utilities.py b/src/orca/scripts/web/script_utilities.py
index 2b4957188..57db2ed2b 100644
--- a/src/orca/scripts/web/script_utilities.py
+++ b/src/orca/scripts/web/script_utilities.py
@@ -2152,6 +2152,30 @@ class Utilities(script_utilities.Utilities):
return 'mark' in self._getXMLRoles(obj) or 'mark' in self._getTag(obj)
+ def isContentSuggestion(self, obj):
+ if not (obj and self.inDocumentContent(obj)):
+ return super().isContentSuggestion(obj)
+
+ # Remove this check when we bump dependencies to 2.36
+ try:
+ if obj.getRole() == pyatspi.ROLE_SUGGESTION:
+ return True
+ except:
+ pass
+
+ return 'suggestion' in self._getXMLRoles(obj)
+
+ def isLastItemInInlineContentSuggestion(self, obj):
+ suggestion = pyatspi.findAncestor(obj, self.isContentSuggestion)
+ if not (suggestion and suggestion.childCount):
+ return False
+
+ displayStyle = self._getDisplayStyle(suggestion)
+ if "inline" not in displayStyle:
+ return False
+
+ return suggestion[-1] == obj
+
def speakMathSymbolNames(self, obj=None):
obj = obj or orca_state.locusOfFocus
return self.isMath(obj)
@@ -2719,6 +2743,8 @@ class Utilities(script_utilities.Utilities):
rv = False
elif self.isLandmark(obj):
rv = False
+ elif self.isContentSuggestion(obj):
+ rv = False
elif self.isDPub(obj):
rv = False
elif self.isFeed(obj):
diff --git a/src/orca/scripts/web/speech_generator.py b/src/orca/scripts/web/speech_generator.py
index 8b6e8eb84..d6ec95c3e 100644
--- a/src/orca/scripts/web/speech_generator.py
+++ b/src/orca/scripts/web/speech_generator.py
@@ -245,7 +245,8 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
if self._script.utilities.isTextBlockElement(obj) \
and not self._script.utilities.isLandmark(obj) \
- and not self._script.utilities.isDPub(obj):
+ and not self._script.utilities.isDPub(obj) \
+ and not self._script.utilities.isContentSuggestion(obj):
return []
if obj.name:
diff --git a/src/orca/speech_generator.py b/src/orca/speech_generator.py
index 6e41be127..467fd8514 100644
--- a/src/orca/speech_generator.py
+++ b/src/orca/speech_generator.py
@@ -330,6 +330,12 @@ class SpeechGenerator(generator.Generator):
result = [messages.CONTENT_DELETION_END]
result.extend(self.voice(SYSTEM))
+
+ if self._script.utilities.isLastItemInInlineContentSuggestion(obj):
+ result.extend(self._generatePause(obj, **args))
+ result.extend([messages.CONTENT_SUGGESTION_END])
+ result.extend(self.voice(SYSTEM))
+
return result
def _generateInsertionStart(self, obj, **args):
@@ -356,6 +362,12 @@ class SpeechGenerator(generator.Generator):
result = [messages.CONTENT_INSERTION_END]
result.extend(self.voice(SYSTEM))
+
+ if self._script.utilities.isLastItemInInlineContentSuggestion(obj):
+ result.extend(self._generatePause(obj, **args))
+ result.extend([messages.CONTENT_SUGGESTION_END])
+ result.extend(self.voice(SYSTEM))
+
return result
def _generateMarkStart(self, obj, **args):
@@ -1667,6 +1679,7 @@ class SpeechGenerator(generator.Generator):
def _getEnabledAndDisabledContextRoles(self):
allRoles = [pyatspi.ROLE_BLOCK_QUOTE,
+ 'ROLE_CONTENT_SUGGESTION',
pyatspi.ROLE_FORM,
pyatspi.ROLE_LANDMARK,
'ROLE_DPUB_LANDMARK',
@@ -1684,7 +1697,9 @@ class SpeechGenerator(generator.Generator):
if _settingsManager.getSetting('sayAllContextList'):
enabled.append(pyatspi.ROLE_LIST)
if _settingsManager.getSetting('sayAllContextPanel'):
- enabled.extend([pyatspi.ROLE_PANEL, 'ROLE_DPUB_SECTION'])
+ enabled.extend([pyatspi.ROLE_PANEL,
+ 'ROLE_CONTENT_SUGGESTION',
+ 'ROLE_DPUB_SECTION'])
if _settingsManager.getSetting('sayAllContextNonLandmarkForm'):
enabled.append(pyatspi.ROLE_FORM)
if _settingsManager.getSetting('sayAllContextTable'):
@@ -1697,7 +1712,9 @@ class SpeechGenerator(generator.Generator):
if _settingsManager.getSetting('speakContextList'):
enabled.append(pyatspi.ROLE_LIST)
if _settingsManager.getSetting('speakContextPanel'):
- enabled.extend([pyatspi.ROLE_PANEL, 'ROLE_DPUB_SECTION'])
+ enabled.extend([pyatspi.ROLE_PANEL,
+ 'ROLE_CONTENT_SUGGESTION',
+ 'ROLE_DPUB_SECTION'])
if _settingsManager.getSetting('speakContextNonLandmarkForm'):
enabled.append(pyatspi.ROLE_FORM)
if _settingsManager.getSetting('speakContextTable'):
@@ -1818,6 +1835,8 @@ class SpeechGenerator(generator.Generator):
result = ['']
elif role == pyatspi.ROLE_FORM:
result.append(messages.LEAVING_FORM)
+ elif role == 'ROLE_CONTENT_SUGGESTION':
+ result.append(messages.LEAVING_SUGGESTION)
else:
result = ['']
if result:
@@ -1953,6 +1972,7 @@ class SpeechGenerator(generator.Generator):
args['includeOnly'] = [pyatspi.ROLE_BLOCK_QUOTE,
pyatspi.ROLE_FORM,
pyatspi.ROLE_LANDMARK,
+ 'ROLE_CONTENT_SUGGESTION',
'ROLE_DPUB_LANDMARK',
'ROLE_DPUB_SECTION',
pyatspi.ROLE_LIST,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]