[orca] Web: Support presentation of child position for description list terms



commit 40d15d91b57398dc95d955dd137131a43fafa9b6
Author: Joanmarie Diggs <jdiggs igalia com>
Date:   Wed Jun 22 15:42:49 2022 +0200

    Web: Support presentation of child position for description list terms
    
    NOTE: This functionality requires "speak child position" to be enabled
    on the "Speech" page in Orca's preferences dialog (it is not enabled by
    default).
    
    * Try to get the posinset and setsize values via object attribute
      (works for Chrome)
    * Fall back on calculating it by iterating through all children
      (needed for Firefox)
    * Cache results so we don't have to do this iteration every time
    
    See issue #248

 src/orca/formatting.py                   |  2 +-
 src/orca/script_utilities.py             | 32 +++++++++++++++++++++---------
 src/orca/scripts/web/script_utilities.py | 34 ++++++++++++++++++++++++++++++++
 src/orca/scripts/web/speech_generator.py |  2 +-
 4 files changed, 59 insertions(+), 11 deletions(-)
---
diff --git a/src/orca/formatting.py b/src/orca/formatting.py
index 7e0ff7cff..ac43e7a29 100644
--- a/src/orca/formatting.py
+++ b/src/orca/formatting.py
@@ -209,7 +209,7 @@ formatting = {
             'unfocused': 'labelOrName + pause + focusedItem + pause + multiselectableState + 
(numberOfChildren or roleName) + pause'
             },
         pyatspi.ROLE_DESCRIPTION_TERM: {
-            'unfocused': '(labelOrName or (displayedText + allTextSelection) + roleName + pause + 
termValueCount)',
+            'unfocused': '(labelOrName or (displayedText + allTextSelection)) + roleName + pause + 
termValueCount + pause + positionInList',
             },
         pyatspi.ROLE_DESCRIPTION_VALUE: {
             'unfocused': '(labelOrName or (displayedText + allTextSelection) + roleName)',
diff --git a/src/orca/script_utilities.py b/src/orca/script_utilities.py
index 496d633bb..4190f741f 100644
--- a/src/orca/script_utilities.py
+++ b/src/orca/script_utilities.py
@@ -1229,6 +1229,12 @@ class Utilities:
     def isDescriptionListDescription(self, obj):
         return obj and obj.getRole() == pyatspi.ROLE_DESCRIPTION_VALUE
 
+    def descriptionListTerms(self, obj):
+        if not self.isDescriptionList(obj):
+            return []
+
+        return [x for x in obj if self.isDescriptionListTerm(x)]
+
     def isDocumentList(self, obj):
         if not (obj and obj.getRole() in [pyatspi.ROLE_LIST, pyatspi.ROLE_DESCRIPTION_LIST]):
             return False
@@ -5130,7 +5136,7 @@ class Utilities:
 
         return obj.childCount
 
-    def getFunctionalChildren(self, obj):
+    def getFunctionalChildren(self, obj, sibling=None):
         if not obj:
             return None
 
@@ -5141,6 +5147,10 @@ class Utilities:
             r = relations[0]
             result = [r.getTarget(i) for i in range(r.getNTargets())]
 
+        if not result:
+            if self.isDescriptionListTerm(sibling):
+                return self.descriptionListTerms(obj)
+
         return result or [child for child in obj]
 
     def getFunctionalParent(self, obj):
@@ -5182,7 +5192,7 @@ class Utilities:
         if childCount > 100 and parent == obj.parent:
             return obj.getIndexInParent(), childCount
 
-        siblings = self.getFunctionalChildren(parent)
+        siblings = self.getFunctionalChildren(parent, obj)
         if len(siblings) < 100 and not pyatspi.utils.findAncestor(obj, isComboBox):
             layoutRoles = [pyatspi.ROLE_SEPARATOR, pyatspi.ROLE_TEAROFF_MENU_ITEM]
             isNotLayoutOnly = lambda x: not (self.isZombie(x) or x.getRole() in layoutRoles)
@@ -5200,9 +5210,9 @@ class Utilities:
         setSize = len(siblings)
         return position, setSize
 
-    def getValueCountForTerm(self, obj):
+    def valuesForTerm(self, obj):
         if not self.isDescriptionListTerm(obj):
-            return -1
+            return []
 
         try:
             index = obj.getIndexInParent()
@@ -5210,15 +5220,19 @@ class Utilities:
         except:
             msg = "ERROR: Exception getting index and sibling count for %s" % obj
             debug.println(debug.LEVEL_INFO, msg, True)
-            return -1
+            return []
 
-        count = 0
+        values = []
         for i in range(index + 1, total):
-            if not self.isDescriptionListDescription(obj.parent[i]):
+            child = obj.parent[i]
+            if not self.isDescriptionListDescription(child):
                 break
-            count += 1
+            values.append(child)
 
-        return count
+        return values
+
+    def getValueCountForTerm(self, obj):
+        return len(self.valuesForTerm(obj))
 
     def getRoleDescription(self, obj, isBraille=False):
         return ""
diff --git a/src/orca/scripts/web/script_utilities.py b/src/orca/scripts/web/script_utilities.py
index 10fb35e41..45c0bc81a 100644
--- a/src/orca/scripts/web/script_utilities.py
+++ b/src/orca/scripts/web/script_utilities.py
@@ -106,6 +106,8 @@ class Utilities(script_utilities.Utilities):
         self._inferredLabels = {}
         self._labelsForObject = {}
         self._labelTargets = {}
+        self._descriptionListTerms = {}
+        self._valuesForTerm = {}
         self._displayedLabelText = {}
         self._mimeType = {}
         self._preferDescriptionOverName = {}
@@ -201,6 +203,8 @@ class Utilities(script_utilities.Utilities):
         self._inferredLabels = {}
         self._labelsForObject = {}
         self._labelTargets = {}
+        self._descriptionListTerms = {}
+        self._valuesForTerm = {}
         self._displayedLabelText = {}
         self._mimeType = {}
         self._preferDescriptionOverName = {}
@@ -3666,6 +3670,36 @@ class Utilities(script_utilities.Utilities):
 
         return self._getTag(obj) == "dd"
 
+    def descriptionListTerms(self, obj):
+        if not obj:
+            return []
+
+        rv = self._descriptionListTerms.get(hash(obj))
+        if rv is not None:
+            return rv
+
+        rv = super().descriptionListTerms(obj)
+        if not self.inDocumentContent(obj):
+            return rv
+
+        self._descriptionListTerms[hash(obj)] = rv
+        return rv
+
+    def valuesForTerm(self, obj):
+        if not obj:
+            return []
+
+        rv = self._valuesForTerm.get(hash(obj))
+        if rv is not None:
+            return rv
+
+        rv = super().valuesForTerm(obj)
+        if not self.inDocumentContent(obj):
+            return rv
+
+        self._valuesForTerm[hash(obj)] = rv
+        return rv
+
     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 84521589e..ac3d45f0c 100644
--- a/src/orca/scripts/web/speech_generator.py
+++ b/src/orca/scripts/web/speech_generator.py
@@ -471,7 +471,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         setsize = self._script.utilities.getSetSize(obj[0])
         if setsize is None:
             if self._script.utilities.isDescriptionList(obj):
-                children = [x for x in obj if self._script.utilities.isDescriptionListTerm(x)]
+                children = self._script.utilities.descriptionListTerms(obj)
             elif role in [pyatspi.ROLE_LIST, pyatspi.ROLE_LIST_BOX]:
                 children = [x for x in obj if x.getRole() == pyatspi.ROLE_LIST_ITEM]
             setsize = len(children)


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