[orca] Improve presentation of editable comboboxes



commit 41707bc0c8f931f45856a34cbc57bc335a5bbdf1
Author: Joanmarie Diggs <jdiggs igalia com>
Date:   Mon Oct 17 05:19:38 2016 -0400

    Improve presentation of editable comboboxes

 src/orca/formatting.py                            |    4 +-
 src/orca/script_utilities.py                      |   38 ++++++++++++-
 src/orca/scripts/apps/soffice/script.py           |    7 ++-
 src/orca/scripts/apps/soffice/speech_generator.py |   60 +++++++++++++++------
 src/orca/speech_generator.py                      |    3 +
 5 files changed, 89 insertions(+), 23 deletions(-)
---
diff --git a/src/orca/formatting.py b/src/orca/formatting.py
index e671795..30e6770 100644
--- a/src/orca/formatting.py
+++ b/src/orca/formatting.py
@@ -154,8 +154,8 @@ formatting = {
             },
         pyatspi.ROLE_COMBO_BOX: {
             'focused': 'expandableState',
-            'unfocused': 'labelAndName + roleName + pause + positionInList + ' + MNEMONIC + ' + accelerator',
-            'basicWhereAmI': 'label + roleName + pause + name + positionInList + ' + MNEMONIC + ' + 
accelerator'
+            'unfocused': 'labelAndName + roleName + pause + (currentLineText + anyTextSelection or 
positionInList) + ' + MNEMONIC + ' + accelerator',
+            'basicWhereAmI': 'label + roleName + pause + name + (currentLineText + anyTextSelection or 
positionInList) + ' + MNEMONIC + ' + accelerator'
             },
         pyatspi.ROLE_DIAL: {
             'focused': 'value',
diff --git a/src/orca/script_utilities.py b/src/orca/script_utilities.py
index 789f9fe..529df0b 100644
--- a/src/orca/script_utilities.py
+++ b/src/orca/script_utilities.py
@@ -3329,11 +3329,45 @@ class Utilities:
     def isEntryCompletionPopupItem(self, obj):
         return False
 
+    def getEntryForEditableComboBox(self, obj):
+        if not obj:
+            return None
+
+        try:
+            role = obj.getRole()
+        except:
+            msg = "ERROR: Exception getting role for %s" % obj
+            debug.println(debug.LEVEL_INFO, msg, True)
+            return None
+
+        if role != pyatspi.ROLE_COMBO_BOX:
+            return None
+
+        children = [x for x in obj if self.isEditableTextArea(x)]
+        if len(children) == 1:
+            return children[0]
+
+        return None
+
     def isEditableComboBox(self, obj):
-        return False
+        return self.getEntryForEditableComboBox(obj) is not None
 
     def isEditableDescendantOfComboBox(self, obj):
-        return False
+        if not obj:
+            return False
+
+        try:
+            state = obj.getState()
+        except:
+            msg = "ERROR: Exception getting state for %s" % obj
+            debug.println(debug.LEVEL_INFO, msg, True)
+            return False
+
+        if not state.contains(pyatspi.STATE_EDITABLE):
+            return False
+
+        isComboBox = lambda x: x and x.getRole() == pyatspi.ROLE_COMBO_BOX
+        return pyatspi.findAncestor(obj, isComboBox) is not None
 
     def isPopOver(self, obj):
         return False
diff --git a/src/orca/scripts/apps/soffice/script.py b/src/orca/scripts/apps/soffice/script.py
index fda59c0..e2f3acd 100644
--- a/src/orca/scripts/apps/soffice/script.py
+++ b/src/orca/scripts/apps/soffice/script.py
@@ -698,14 +698,15 @@ class Script(default.Script):
             orca.setLocusOfFocus(event, event.source)
             return
 
-        if self.utilities.isZombie(event.source):
+        role = event.source.getRole()
+
+        if self.utilities.isZombie(event.source) \
+           or role in [pyatspi.ROLE_TEXT, pyatspi.ROLE_LIST]:
             comboBox = self.utilities.containingComboBox(event.source)
             if comboBox:
                 orca.setLocusOfFocus(event, comboBox, True)
                 return
 
-        role = event.source.getRole()
-
         # This seems to be something we inherit from Gtk+
         if role in [pyatspi.ROLE_TEXT, pyatspi.ROLE_PASSWORD_TEXT]:
             orca.setLocusOfFocus(event, event.source)
diff --git a/src/orca/scripts/apps/soffice/speech_generator.py 
b/src/orca/scripts/apps/soffice/speech_generator.py
index 88e6231..4667dff 100644
--- a/src/orca/scripts/apps/soffice/speech_generator.py
+++ b/src/orca/scripts/apps/soffice/speech_generator.py
@@ -111,29 +111,44 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         object will be used.  This method will return an empty array
         if nothing can be found.
         """
-        role = args.get('role', obj.getRole())
-        if role == pyatspi.ROLE_TEXT and obj.parent.getRole() == pyatspi.ROLE_COMBO_BOX:
-            return []
 
-        if role in [pyatspi.ROLE_PUSH_BUTTON, pyatspi.ROLE_TOGGLE_BUTTON] \
-           and self._script.utilities.ancestorWithRole(
-                obj, [pyatspi.ROLE_TOOL_BAR], [pyatspi.ROLE_FRAME]):
-            acss = self.voice(speech_generator.SYSTEM)
+        # TODO - JD: This should be the behavior by default. But the default
+        # generators call displayedText(). Once that is corrected, this method
+        # can be removed.
+        if obj.name:
             result = [obj.name]
-            if result:
-                result.extend(acss)
-                return result
+            result.extend(self.voice(speech_generator.DEFAULT))
+            return result
 
-        return speech_generator.SpeechGenerator._generateName(
-            self, obj, **args)
+        return super()._generateName(obj, **args)
+
+    def _generateLabelAndName(self, obj, **args):
+        if obj.getRole() != pyatspi.ROLE_COMBO_BOX:
+            return super()._generateLabelAndName(obj, **args)
+
+        # TODO - JD: This should be the behavior by default because many
+        # toolkits use the label for the name.
+        result = []
+        label = self._script.utilities.displayedLabel(obj)
+        if label:
+            result.append(label)
+            result.extend(self.voice(speech_generator.DEFAULT))           
+
+        name = obj.name
+        if label == name or not name:
+            selected = self._script.utilities.selectedChildren(obj)
+            if selected:
+                name = selected[0].name
+
+        if name:
+            result.append(name)
+            result.extend(self.voice(speech_generator.DEFAULT))
+
+        return result
 
     def _generateLabelOrName(self, obj, **args):
         """Gets the label or the name if the label is not preset."""
 
-        role = args.get('role', obj.getRole())
-        if role == pyatspi.ROLE_TEXT and obj.parent.getRole() == pyatspi.ROLE_COMBO_BOX:
-            return []
-
         result = []
         acss = self.voice(speech_generator.DEFAULT)
         override = self.__overrideParagraph(obj, **args)
@@ -162,6 +177,13 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                 self, obj, **args))
         return result
 
+    def _generateAnyTextSelection(self, obj, **args):
+        comboBoxEntry = self._script.utilities.getEntryForEditableComboBox(obj)
+        if comboBoxEntry:
+            return super()._generateAnyTextSelection(comboBoxEntry)
+
+        return super()._generateAnyTextSelection(obj, **args)
+
     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
@@ -214,6 +236,12 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             if priorObj and priorObj.parent != obj.parent:
                 return []
 
+        if obj.getRole() == pyatspi.ROLE_COMBO_BOX:
+            entry = self._script.utilities.getEntryForEditableComboBox(obj)
+            if entry:
+                return super()._generateCurrentLineText(entry)
+            return []
+
         # TODO - JD: The SayLine, etc. code should be generated and not put
         # together in the scripts. In addition, the voice crap needs to go
         # here. Then it needs to be removed from the scripts.
diff --git a/src/orca/speech_generator.py b/src/orca/speech_generator.py
index 3f01895..a019261 100644
--- a/src/orca/speech_generator.py
+++ b/src/orca/speech_generator.py
@@ -1654,6 +1654,9 @@ class SpeechGenerator(generator.Generator):
            and obj.parent == self._script.utilities.topLevelObject(obj):
             return []
 
+        if self._script.utilities.isEditableComboBox(obj):
+            return []
+
         result = []
         acss = self.voice(SYSTEM)
         position, total = self._script.utilities.getPositionAndSetSize(obj)


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