%PDF- %PDF-
Direktori : /lib/python3/dist-packages/orca/ |
Current File : //lib/python3/dist-packages/orca/action_presenter.py |
# Orca # # Copyright 2023 Igalia, S.L. # Author: Joanmarie Diggs <jdiggs@igalia.com> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., Franklin Street, Fifth Floor, # Boston MA 02110-1301 USA. """Module for performing accessible actions via a menu""" __id__ = "$Id$" __version__ = "$Revision$" __date__ = "$Date$" __copyright__ = "Copyright (c) 2023 Igalia, S.L." __license__ = "LGPL" import gi gi.require_version("Gdk", "3.0") gi.require_version("Gtk", "3.0") from gi.repository import Gdk, GLib, Gtk from . import cmdnames from . import debug from . import focus_manager from . import input_event from . import keybindings from . import messages from . import script_manager from .ax_object import AXObject class ActionPresenter: """Provides menu for performing accessible actions on an object.""" def __init__(self): self._handlers = self.get_handlers(True) self._bindings = keybindings.KeyBindings() self._gui = None self._obj = None self._window = None def get_bindings(self, refresh=False, is_desktop=True): """Returns the action-presenter keybindings.""" if refresh: msg = "ACTION PRESENTER: Refreshing bindings." debug.printMessage(debug.LEVEL_INFO, msg, True) self._setup_bindings() elif self._bindings.isEmpty(): self._setup_bindings() return self._bindings def get_handlers(self, refresh=False): """Returns the action-presenter handlers.""" if refresh: msg = "ACTION PRESENTER: Refreshing handlers." debug.printMessage(debug.LEVEL_INFO, msg, True) self._setup_handlers() return self._handlers def _setup_handlers(self): """Sets up the action-presenter input event handlers.""" self._handlers = {} self._handlers["show_actions_menu"] = \ input_event.InputEventHandler( self.show_actions_menu, cmdnames.SHOW_ACTIONS_MENU) msg = "ACTION PRESENTER: Handlers set up." debug.printMessage(debug.LEVEL_INFO, msg, True) def _setup_bindings(self): """Sets up the action-presenter key bindings.""" self._bindings = keybindings.KeyBindings() self._bindings.add( keybindings.KeyBinding( "a", keybindings.defaultModifierMask, keybindings.ORCA_SHIFT_MODIFIER_MASK, self._handlers.get("show_actions_menu"))) msg = "ACTION PRESENTER: Bindings set up." debug.printMessage(debug.LEVEL_INFO, msg, True) def _restore_focus(self): """Restores focus to the object associated with the actions menu.""" tokens = ["ACTION PRESENTER: Restoring focus to", self._obj, "in", self._window] debug.printTokens(debug.LEVEL_INFO, tokens, True) # TODO - JD: Consider having set_locus_of_focus always update the active script. reason = "Action Presenter menu is being destroyed" app = AXObject.get_application(self._obj) script = script_manager.getManager().getScript(app, self._obj) script_manager.getManager().setActiveScript(script, reason) manager = focus_manager.getManager() manager.clear_state(reason) manager.set_active_window(self._window) manager.set_locus_of_focus(None, self._obj) def _perform_action(self, action): """Attempts to perform the named action.""" result = AXObject.do_named_action(self._obj, action) tokens = ["ACTION PRESENTER: Performing", action, "on", self._obj, "succeeded:", result] debug.printTokens(debug.LEVEL_INFO, tokens, True) self._gui = None def show_actions_menu(self, script, event=None): """Shows a menu with all the available accessible actions.""" manager = focus_manager.getManager() obj = manager.get_active_mode_and_object_of_interest()[1] or manager.get_locus_of_focus() if obj is None: full = messages.LOCATION_NOT_FOUND_FULL brief = messages.LOCATION_NOT_FOUND_BRIEF script.presentMessage(full, brief) return True actions = {} for i in range(AXObject.get_n_actions(obj)): name = AXObject.get_action_name(obj, i) description = AXObject.get_action_description(obj, i) tokens = [f"ACTION PRESENTER: Action {i} on", obj, f": '{name}' localized description: '{description}'"] debug.printTokens(debug.LEVEL_INFO, tokens, True) actions[name] = description or name if not actions.items(): name = AXObject.get_name(obj) or script.speechGenerator.getLocalizedRoleName(obj) script.presentMessage(messages.NO_ACTIONS_FOUND_ON % name) return True self._obj = obj self._window = manager.get_active_window() self._gui = ActionMenu(actions, self._perform_action, self._restore_focus) timeout = 500 msg = f"ACTION PRESENTER: Delaying popup {timeout}ms due to GtkMenu grab conflict." debug.printMessage(debug.LEVEL_INFO, msg, True) GLib.timeout_add(timeout, self._gui.show_gui) return True class ActionMenu(Gtk.Menu): """A simple Gtk.Menu containing a list of accessible actions.""" def __init__(self, actions, action_handler, cleanup_handler): super().__init__() self.connect("popped-up", self._on_popped_up) self.connect("hide", self._on_hidden) self.on_option_selected = action_handler self.on_menu_hidden = cleanup_handler for name, description in actions.items(): menu_item = Gtk.MenuItem(label=description) menu_item.connect("activate", self._on_activate, name) self.append(menu_item) def _on_activate(self, widget, option): """Handler for the 'activate' menuitem signal""" self.on_option_selected(option) def _on_popped_up(self, *args): """Handler for the 'popped-up' menu signal""" msg = "ACTION PRESENTER: ActionMenu popped up" debug.printMessage(debug.LEVEL_INFO, msg, True) def _on_hidden(self, *args): """Handler for the 'hide' menu signal""" msg = "ACTION PRESENTER: ActionMenu hidden" debug.printMessage(debug.LEVEL_INFO, msg, True) self.on_menu_hidden() def show_gui(self): """Shows the menu""" self.show_all() display = Gdk.Display.get_default() seat = display.get_default_seat() device = seat.get_pointer() screen, x, y = device.get_position() event = Gdk.Event.new(Gdk.EventType.BUTTON_PRESS) event.set_screen(screen) event.set_device(device) event.time = Gtk.get_current_event_time() event.x = x event.y = y rect = Gdk.Rectangle() rect.x = x rect.y = y rect.width = 1 rect.height = 1 window = Gdk.get_default_root_window() self.popup_at_rect(window, rect, Gdk.Gravity.NORTH_WEST, Gdk.Gravity.NORTH_WEST, event) _presenter = ActionPresenter() def getPresenter(): return _presenter