[orca] Chromium: Improve presentation of browser pop-up menus
- From: Joanmarie Diggs <joanied src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [orca] Chromium: Improve presentation of browser pop-up menus
- Date: Mon, 3 Dec 2018 23:38:53 +0000 (UTC)
commit 09ee2739aacd44d84bd3b6c8e757db3fb803f947
Author: Joanmarie Diggs <jdiggs igalia com>
Date: Mon Dec 3 18:38:22 2018 -0500
Chromium: Improve presentation of browser pop-up menus
src/orca/scripts/toolkits/Chromium/script.py | 21 ++++++-
.../scripts/toolkits/Chromium/script_utilities.py | 66 +++++++++++++++++++++-
.../scripts/toolkits/Chromium/speech_generator.py | 11 ++++
3 files changed, 95 insertions(+), 3 deletions(-)
---
diff --git a/src/orca/scripts/toolkits/Chromium/script.py b/src/orca/scripts/toolkits/Chromium/script.py
index 202c00a71..370aaa027 100644
--- a/src/orca/scripts/toolkits/Chromium/script.py
+++ b/src/orca/scripts/toolkits/Chromium/script.py
@@ -310,7 +310,8 @@ class Script(web.Script):
topLevel = self.utilities.topLevelObject(event.source)
if self.utilities.canBeActiveWindow(topLevel):
orca_state.activeWindow = topLevel
- orca.setLocusOfFocus(event, event.source)
+ notify = not self.utilities.isPopupMenuForCurrentItem(event.source)
+ orca.setLocusOfFocus(event, event.source, notify)
return
if super().onShowingChanged(event):
@@ -356,6 +357,24 @@ class Script(web.Script):
if not self.utilities.canBeActiveWindow(event.source):
return
+ if not event.source.name:
+ orca_state.activeWindow = event.source
+
+ # If this is a frame for a popup menu, we don't want to treat
+ # it like a proper window:activate event because it's not as
+ # far as the end-user experience is concerned.
+ activeItem = self.utilities.popupMenuForFrame(event.source)
+ if activeItem:
+ selected = self.utilities.selectedChildren(activeItem)
+ if len(selected) == 1:
+ activeItem = selected[0]
+
+ msg = "CHROMIUM: Setting locusOfFocus to %s" % activeItem
+ orca_state.activeWindow = event.source
+ orca.setLocusOfFocus(event, activeItem)
+ debug.println(debug.LEVEL_INFO, msg, True)
+ return
+
if super().onWindowActivated(event):
return
diff --git a/src/orca/scripts/toolkits/Chromium/script_utilities.py
b/src/orca/scripts/toolkits/Chromium/script_utilities.py
index fdc4c5552..7c63f4a2a 100644
--- a/src/orca/scripts/toolkits/Chromium/script_utilities.py
+++ b/src/orca/scripts/toolkits/Chromium/script_utilities.py
@@ -109,8 +109,7 @@ class Utilities(web.Utilities):
return count
# HACK: Ideally, we'd use the selection interface to get the selected
- # children and then only present this menu if there isn't a selected
- # child. But that interface is not implemented yet. This hackaround
+ # child count. But that interface is not implemented yet. This hackaround
# is extremely non-performant.
for child in obj:
if child.getState().contains(pyatspi.STATE_SELECTED):
@@ -120,6 +119,20 @@ class Utilities(web.Utilities):
debug.println(debug.LEVEL_INFO, msg, True)
return count
+ def selectedChildren(self, obj):
+ result = super().selectedChildren(obj)
+ if result or "Selection" in pyatspi.listInterfaces(obj):
+ return result
+
+ # HACK: Ideally, we'd use the selection interface to get the selected
+ # children. But that interface is not implemented yet. This hackaround
+ # is extremely non-performant.
+ for child in obj:
+ if child.getState().contains(pyatspi.STATE_SELECTED):
+ result.append(child)
+
+ return result
+
def isMenuWithNoSelectedChild(self, obj):
if not obj:
return False
@@ -194,6 +207,55 @@ class Utilities(web.Utilities):
return False
+ def isPopupMenuForCurrentItem(self, obj):
+ # When a submenu is closed, it has role menu item. But when that submenu
+ # is opened/expanded, a menu with that same name appears. It would be
+ # nice if there were a connection (parent/child or an accessible relation)
+ # between the two....
+ if not self.treatAsMenu(orca_state.locusOfFocus):
+ return False
+
+ if obj.name and obj.name == orca_state.locusOfFocus.name:
+ return obj.getRole() == pyatspi.ROLE_MENU
+
+ return False
+
+ def isFrameForPopupMenu(self, obj):
+ try:
+ name = obj.name
+ role = obj.getRole()
+ childCount = obj.childCount
+ except:
+ msg = "CHROMIUM: Exception getting properties of %s" % obj
+ debug.println(debug.LEVEL_INFO, msg, True)
+ return False
+
+ # The ancestry of a popup menu appears to be a menu bar (even though
+ # one is not actually showing) contained in a nameless frame. It would
+ # be nice if these things were pruned from the accessibility tree....
+ if name or role != pyatspi.ROLE_FRAME or childCount != 1:
+ return False
+
+ if obj[0].getRole() == pyatspi.ROLE_MENU_BAR:
+ return True
+
+ return False
+
+ def popupMenuForFrame(self, obj):
+ if not self.isFrameForPopupMenu(obj):
+ return None
+
+ try:
+ menu = pyatspi.findDescendant(obj, lambda x: x and x.getRole() == pyatspi.ROLE_MENU)
+ except:
+ msg = "CHROMIUM: Exception finding descendant of %s" % obj
+ debug.println(debug.LEVEL_INFO, msg, True)
+ return None
+
+ msg = "CHROMIUM: HACK: Popup menu for %s: %s" % (obj, menu)
+ debug.println(debug.LEVEL_INFO, msg, True)
+ return menu
+
def grabFocusWhenSettingCaret(self, obj):
# HACK: Remove this when setting the caret updates focus.
msg = "CHROMIUM: HACK: Doing focus grab when setting caret on %s" % obj
diff --git a/src/orca/scripts/toolkits/Chromium/speech_generator.py
b/src/orca/scripts/toolkits/Chromium/speech_generator.py
index c7b2f9daf..c69284e36 100644
--- a/src/orca/scripts/toolkits/Chromium/speech_generator.py
+++ b/src/orca/scripts/toolkits/Chromium/speech_generator.py
@@ -43,6 +43,17 @@ class SpeechGenerator(web.SpeechGenerator):
def __init__(self, script):
super().__init__(script)
+ def _generateNewAncestors(self, obj, **args):
+ # Likely a refocused submenu whose functional child was just collapsed.
+ # The new ancestors might technically be new, but they are not as far
+ # as the user is concerned.
+ if self._script.utilities.treatAsMenu(obj):
+ msg = "CHROMIUM: Not generating new ancestors for %s" % obj
+ debug.println(debug.LEVEL_INFO, msg, True)
+ return []
+
+ return super()._generateAncestors(obj, **args)
+
def generateSpeech(self, obj, **args):
if self._script.utilities.inDocumentContent(obj):
return super().generateSpeech(obj, **args)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]