[orca] Web: Improve presentation of description lists



commit 3d833ed10f1a11e99a8746f562da0950047b543f
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 184a17081..7f0f40cbb 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 fa3089f8c..805a32d35 100644
--- a/src/orca/generator.py
+++ b/src/orca/generator.py
@@ -1332,6 +1332,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 50ad34b95..f5fea3f57 100644
--- a/src/orca/script_utilities.py
+++ b/src/orca/script_utilities.py
@@ -1214,8 +1214,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 9585ab56a..108dd933a 100644
--- a/src/orca/scripts/web/script_utilities.py
+++ b/src/orca/scripts/web/script_utilities.py
@@ -2396,6 +2396,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
@@ -3128,6 +3131,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):
@@ -3614,6 +3623,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 b990917b1..77ad3b6d0 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 483c7c1a6..e4e6a2ccf 100644
--- a/src/orca/speech_generator.py
+++ b/src/orca/speech_generator.py
@@ -1819,6 +1819,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,
@@ -1835,6 +1836,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,
@@ -1854,6 +1856,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,
@@ -1889,7 +1892,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:
@@ -2130,6 +2134,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]