Press enter on clickables to activate them like any other web control. Very experimental.

This commit is contained in:
Storm Dragon
2025-08-10 21:28:56 -04:00
parent eae9a5896e
commit b715e9071b
2 changed files with 85 additions and 2 deletions
+1 -1
View File
@@ -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"
+84 -1
View File
@@ -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."""