Press enter on clickables to activate them like any other web control. Very experimental.
This commit is contained in:
@@ -23,5 +23,5 @@
|
||||
# Fork of Orca Screen Reader (GNOME)
|
||||
# Original source: https://gitlab.gnome.org/GNOME/orca
|
||||
|
||||
version = "2025.08.06"
|
||||
version = "2025.08.10"
|
||||
codeName = "testing"
|
||||
|
||||
@@ -250,6 +250,12 @@ class Script(default.Script):
|
||||
Script.toggleLayoutMode,
|
||||
cmdnames.TOGGLE_LAYOUT_MODE)
|
||||
|
||||
self.inputEventHandlers["activateClickableHandler"] = \
|
||||
input_event.InputEventHandler(
|
||||
Script.activateClickableElement,
|
||||
"Activate clickable element")
|
||||
|
||||
|
||||
def getBookmarks(self):
|
||||
"""Returns the "bookmarks" class for this script."""
|
||||
|
||||
@@ -566,7 +572,14 @@ class Script(default.Script):
|
||||
self._lastCommandWasStructNav = False
|
||||
self._lastCommandWasMouseButton = False
|
||||
|
||||
return super().consumesKeyboardEvent(keyboardEvent)
|
||||
# Check parent first
|
||||
consumes = super().consumesKeyboardEvent(keyboardEvent)
|
||||
|
||||
# If parent doesn't consume Return key, try our clickable fallback
|
||||
if not consumes and keyboardEvent.event_string == "Return":
|
||||
return self._tryClickableActivation(keyboardEvent)
|
||||
|
||||
return consumes
|
||||
|
||||
def getEnabledKeyBindings(self):
|
||||
all = super().getEnabledKeyBindings()
|
||||
@@ -1321,6 +1334,76 @@ class Script(default.Script):
|
||||
self._browseModeIsSticky = False
|
||||
self.refreshKeyGrabs()
|
||||
|
||||
def _tryClickableActivation(self, keyboardEvent):
|
||||
"""Try to activate clickable element - returns True if we should consume the event."""
|
||||
|
||||
obj = cthulhu_state.locusOfFocus
|
||||
if not obj or not self.utilities.inDocumentContent(obj):
|
||||
return False
|
||||
|
||||
# Skip form controls where Return should have normal behavior
|
||||
from cthulhu import ax_utilities
|
||||
if (ax_utilities.AXUtilities.is_entry(obj) or
|
||||
ax_utilities.AXUtilities.is_text(obj) or
|
||||
ax_utilities.AXUtilities.is_password_text(obj) or
|
||||
ax_utilities.AXUtilities.is_combo_box(obj) or
|
||||
ax_utilities.AXUtilities.is_button(obj) or
|
||||
ax_utilities.AXUtilities.is_push_button(obj) or
|
||||
ax_utilities.AXUtilities.is_link(obj)):
|
||||
return False
|
||||
|
||||
# Try clickable activation for non-standard clickable elements
|
||||
# Store focus information before clicking for potential restoration
|
||||
original_focus = obj
|
||||
|
||||
# First try the standard clickable detection
|
||||
if self.utilities.isClickableElement(obj):
|
||||
from cthulhu import ax_event_synthesizer
|
||||
result = ax_event_synthesizer.AXEventSynthesizer.click_object(obj)
|
||||
if result:
|
||||
self.presentMessage("Element activated")
|
||||
# Try to restore focus to the clicked element after a brief delay
|
||||
self._restoreFocusAfterClick(original_focus)
|
||||
return True
|
||||
|
||||
# If that didn't work, try a more permissive approach for any element with click action
|
||||
from cthulhu import ax_object
|
||||
if ax_object.AXObject.has_action(obj, "click"):
|
||||
from cthulhu import ax_event_synthesizer
|
||||
result = ax_event_synthesizer.AXEventSynthesizer.click_object(obj)
|
||||
if result:
|
||||
self.presentMessage("Element activated")
|
||||
# Try to restore focus to the clicked element after a brief delay
|
||||
self._restoreFocusAfterClick(original_focus)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _restoreFocusAfterClick(self, original_focus):
|
||||
"""Try to restore focus after a click action that may have caused DOM changes."""
|
||||
try:
|
||||
# Schedule focus restoration after a brief delay to allow DOM changes to settle
|
||||
from gi.repository import GObject
|
||||
|
||||
def restore_focus():
|
||||
try:
|
||||
from cthulhu import ax_object
|
||||
if not ax_object.AXObject.is_dead(original_focus):
|
||||
original_focus.grabFocus()
|
||||
except Exception:
|
||||
pass # Focus restoration is best effort
|
||||
return False # Don't repeat the timeout
|
||||
|
||||
# Delay restoration to allow JavaScript and DOM changes to complete
|
||||
GObject.timeout_add(100, restore_focus) # 100ms delay
|
||||
|
||||
except Exception:
|
||||
pass # Focus restoration is best effort
|
||||
|
||||
def activateClickableElement(self, inputEvent):
|
||||
"""Activates clickable element at current focus via Return key."""
|
||||
return self._tryClickableActivation(inputEvent)
|
||||
|
||||
def locusOfFocusChanged(self, event, oldFocus, newFocus):
|
||||
"""Handles changes of focus of interest to the script."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user