[orca/gnome-42] Web: Improve presentation of description lists



commit 8863130f9216db794d4edebb5f2f06e1faa7a5eb
Author: Joanmarie Diggs <jdiggs igalia com>
Date:   Fri Jun 17 15:28:02 2022 +0200

    Web: Improve presentation of description lists
    
    * Announce entering and leaving list
    * Announce item count (based on the number of description list terms)
    * Append the role name at the end of each description list term in
      order to make it possible to distinguish terms from their descriptions
      in confusing lists, while still making it possible for users to cut
      off that speech in cases where the distinction is obvious.
    * Similarly append the role name at the end of each description list
      value.
    
    See issue #248

 src/orca/formatting.py                   |  8 ++++++--
 src/orca/generator.py                    |  6 ++++++
 src/orca/script_utilities.py             | 11 ++++++++++-
 src/orca/scripts/web/script_utilities.py | 27 +++++++++++++++++++++++++++
 src/orca/scripts/web/speech_generator.py |  7 +++++--
 src/orca/speech_generator.py             |  7 ++++++-
 6 files changed, 60 insertions(+), 6 deletions(-)
---
diff --git a/src/orca/formatting.py b/src/orca/formatting.py
index cbfe5499c..c25224c5d 100644
--- a/src/orca/formatting.py
+++ b/src/orca/formatting.py
@@ -200,11 +200,15 @@ formatting = {
         'ROLE_CONTENT_SUGGESTION': {
             'focused': 'leaving or roleName',
             },
+        pyatspi.ROLE_DESCRIPTION_LIST: {
+            'focused' : 'leaving or (labelOrName + pause + (numberOfChildren or roleName) + pause + 
nestingLevel)',
+            'unfocused': 'labelOrName + pause + focusedItem + pause + multiselectableState + 
(numberOfChildren or roleName) + pause'
+            },
         pyatspi.ROLE_DESCRIPTION_TERM: {
-            'unfocused': '(labelOrName or (displayedText + allTextSelection))',
+            'unfocused': '(labelOrName or (displayedText + allTextSelection) + roleName)',
             },
         pyatspi.ROLE_DESCRIPTION_VALUE: {
-            'unfocused': '(labelOrName or (displayedText + allTextSelection))',
+            'unfocused': '(labelOrName or (displayedText + allTextSelection) + roleName)',
             },
         pyatspi.ROLE_DIAL: {
             'focused': 'value',
diff --git a/src/orca/generator.py b/src/orca/generator.py
index ebcdaaa22..63f5e12a8 100644
--- a/src/orca/generator.py
+++ b/src/orca/generator.py
@@ -1297,6 +1297,12 @@ class Generator:
             return 'ROLE_CONTENT_MARK'
         if self._script.utilities.isContentSuggestion(obj):
             return 'ROLE_CONTENT_SUGGESTION'
+        if self._script.utilities.isDescriptionList(obj):
+            return pyatspi.ROLE_DESCRIPTION_LIST
+        if self._script.utilities.isDescriptionListTerm(obj):
+            return pyatspi.ROLE_DESCRIPTION_TERM
+        if self._script.utilities.isDescriptionListDescription(obj):
+            return pyatspi.ROLE_DESCRIPTION_VALUE
         if self._script.utilities.isLandmark(obj):
             if self._script.utilities.isLandmarkRegion(obj):
                 return 'ROLE_REGION'
diff --git a/src/orca/script_utilities.py b/src/orca/script_utilities.py
index 1c72eb1e9..40b834282 100644
--- a/src/orca/script_utilities.py
+++ b/src/orca/script_utilities.py
@@ -1201,8 +1201,17 @@ class Utilities:
     def isBlockquote(self, obj):
         return obj and obj.getRole() == pyatspi.ROLE_BLOCK_QUOTE
 
+    def isDescriptionList(self, obj):
+        return obj.getRole() == pyatspi.ROLE_DESCRIPTION_LIST
+
+    def isDescriptionListTerm(self, obj):
+        return obj.getRole() == pyatspi.ROLE_DESCRIPTION_TERM
+
+    def isDescriptionListDescription(self, obj):
+        return obj.getRole() == pyatspi.ROLE_DESCRIPTION_VALUE
+
     def isDocumentList(self, obj):
-        if not (obj and obj.getRole() == pyatspi.ROLE_LIST):
+        if not (obj and obj.getRole() in [pyatspi.ROLE_LIST, pyatspi.ROLE_DESCRIPTION_LIST]):
             return False
 
         try:
diff --git a/src/orca/scripts/web/script_utilities.py b/src/orca/scripts/web/script_utilities.py
index a972d6f4f..92575c10f 100644
--- a/src/orca/scripts/web/script_utilities.py
+++ b/src/orca/scripts/web/script_utilities.py
@@ -2395,6 +2395,9 @@ class Utilities(script_utilities.Utilities):
         if not (obj and self.inDocumentContent(obj)):
             return False
 
+        if self.isDescriptionList(obj):
+            return False
+
         try:
             role = obj.getRole()
             childCount = obj.childCount
@@ -3127,6 +3130,12 @@ class Utilities(script_utilities.Utilities):
 
         if role == pyatspi.ROLE_LIST:
             rv = self.treatAsDiv(obj)
+        elif self.isDescriptionList(obj):
+            rv = False
+        elif self.isDescriptionListTerm(obj):
+            rv = False
+        elif self.isDescriptionListDescription(obj):
+            rv = False
         elif self.isMath(obj):
             rv = False
         elif self.isLandmark(obj):
@@ -3611,6 +3620,24 @@ class Utilities(script_utilities.Utilities):
 
         return self._getTag(obj) == "code" or "code" in self._getXMLRoles(obj)
 
+    def isDescriptionList(self, obj):
+        if super().isDescriptionList(obj):
+            return True
+
+        return self._getTag(obj) == "dl"
+
+    def isDescriptionListTerm(self, obj):
+        if super().isDescriptionListTerm(obj):
+            return True
+
+        return self._getTag(obj) == "dt"
+
+    def isDescriptionListDescription(self, obj):
+        if super().isDescriptionListDescription(obj):
+            return True
+
+        return self._getTag(obj) == "dd"
+
     def getComboBoxValue(self, obj):
         attrs = self.objectAttributes(obj, False)
         return attrs.get("valuetext", super().getComboBoxValue(obj))
diff --git a/src/orca/scripts/web/speech_generator.py b/src/orca/scripts/web/speech_generator.py
index baea77854..c37e4808a 100644
--- a/src/orca/scripts/web/speech_generator.py
+++ b/src/orca/scripts/web/speech_generator.py
@@ -451,12 +451,15 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         # other toolkits (e.g. exposing list items to us that are not
         # exposed to sighted users)
         role = args.get('role', obj.getRole())
-        if role not in [pyatspi.ROLE_LIST, pyatspi.ROLE_LIST_BOX]:
+        if role not in [pyatspi.ROLE_LIST, pyatspi.ROLE_LIST_BOX, pyatspi.ROLE_DESCRIPTION_LIST]:
             return super()._generateNumberOfChildren(obj, **args)
 
         setsize = self._script.utilities.getSetSize(obj[0])
         if setsize is None:
-            children = [x for x in obj if x.getRole() == pyatspi.ROLE_LIST_ITEM]
+            if self._script.utilities.isDescriptionList(obj):
+                children = [x for x in obj if self._script.utilities.isDescriptionListTerm(x)]
+            else:
+                children = [x for x in obj if x.getRole() == pyatspi.ROLE_LIST_ITEM]
             setsize = len(children)
 
         if not setsize:
diff --git a/src/orca/speech_generator.py b/src/orca/speech_generator.py
index d71ea03c8..583fba40c 100644
--- a/src/orca/speech_generator.py
+++ b/src/orca/speech_generator.py
@@ -1814,6 +1814,7 @@ class SpeechGenerator(generator.Generator):
                     'ROLE_CONTENT_SUGGESTION',
                     'ROLE_DPUB_LANDMARK',
                     'ROLE_DPUB_SECTION',
+                    pyatspi.ROLE_DESCRIPTION_LIST,
                     pyatspi.ROLE_FORM,
                     pyatspi.ROLE_LANDMARK,
                     pyatspi.ROLE_LIST,
@@ -1830,6 +1831,7 @@ class SpeechGenerator(generator.Generator):
                 enabled.extend([pyatspi.ROLE_LANDMARK, 'ROLE_DPUB_LANDMARK'])
             if _settingsManager.getSetting('sayAllContextList'):
                 enabled.append(pyatspi.ROLE_LIST)
+                enabled.append(pyatspi.ROLE_DESCRIPTION_LIST)
             if _settingsManager.getSetting('sayAllContextPanel'):
                 enabled.extend([pyatspi.ROLE_PANEL,
                                 pyatspi.ROLE_TOOL_TIP,
@@ -1849,6 +1851,7 @@ class SpeechGenerator(generator.Generator):
                 enabled.extend([pyatspi.ROLE_LANDMARK, 'ROLE_DPUB_LANDMARK', 'ROLE_REGION'])
             if _settingsManager.getSetting('speakContextList'):
                 enabled.append(pyatspi.ROLE_LIST)
+                enabled.append(pyatspi.ROLE_DESCRIPTION_LIST)
             if _settingsManager.getSetting('speakContextPanel'):
                 enabled.extend([pyatspi.ROLE_PANEL,
                                 pyatspi.ROLE_TOOL_TIP,
@@ -1884,7 +1887,8 @@ class SpeechGenerator(generator.Generator):
                 result.append(messages.leavingNBlockquotes(count))
             else:
                 result.append(messages.LEAVING_BLOCKQUOTE)
-        elif role == pyatspi.ROLE_LIST and self._script.utilities.isDocumentList(obj):
+        elif role in [pyatspi.ROLE_LIST, pyatspi.ROLE_DESCRIPTION_LIST] \
+            and self._script.utilities.isDocumentList(obj):
             if count > 1:
                 result.append(messages.leavingNLists(count))
             else:
@@ -2125,6 +2129,7 @@ class SpeechGenerator(generator.Generator):
 
         args['leaving'] = True
         args['includeOnly'] = [pyatspi.ROLE_BLOCK_QUOTE,
+                               pyatspi.ROLE_DESCRIPTION_LIST,
                                pyatspi.ROLE_FORM,
                                pyatspi.ROLE_LANDMARK,
                                'ROLE_CONTENT_DELETION',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]