Clickables now present a message when loading. Focus is preserved. Woogoo
This commit is contained in:
@@ -1359,9 +1359,12 @@ class Script(default.Script):
|
||||
# First try the standard clickable detection
|
||||
if self.utilities.isClickableElement(obj):
|
||||
from cthulhu import ax_event_synthesizer
|
||||
# Give immediate feedback that activation is starting
|
||||
self.presentMessage("Activating...")
|
||||
result = ax_event_synthesizer.AXEventSynthesizer.click_object(obj)
|
||||
if result:
|
||||
self.presentMessage("Element activated")
|
||||
# Schedule success message after a brief delay
|
||||
self._presentDelayedMessage("Element activated", 50)
|
||||
# Try to restore focus to the clicked element after a brief delay
|
||||
self._restoreFocusAfterClick(original_focus)
|
||||
return True
|
||||
@@ -1370,9 +1373,12 @@ class Script(default.Script):
|
||||
from cthulhu import ax_object
|
||||
if ax_object.AXObject.has_action(obj, "click"):
|
||||
from cthulhu import ax_event_synthesizer
|
||||
# Give immediate feedback that activation is starting
|
||||
self.presentMessage("Activating...")
|
||||
result = ax_event_synthesizer.AXEventSynthesizer.click_object(obj)
|
||||
if result:
|
||||
self.presentMessage("Element activated")
|
||||
# Schedule success message after a brief delay
|
||||
self._presentDelayedMessage("Element activated", 50)
|
||||
# Try to restore focus to the clicked element after a brief delay
|
||||
self._restoreFocusAfterClick(original_focus)
|
||||
return True
|
||||
@@ -1382,24 +1388,80 @@ class Script(default.Script):
|
||||
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
|
||||
from cthulhu import ax_object
|
||||
|
||||
# Store the document and caret position to restore navigation context
|
||||
document = None
|
||||
caret_offset = -1
|
||||
try:
|
||||
# Find the document root
|
||||
obj = original_focus
|
||||
while obj and not ax_object.AXObject.is_dead(obj):
|
||||
if self.utilities.isDocument(obj):
|
||||
document = obj
|
||||
break
|
||||
parent = ax_object.AXObject.get_parent(obj)
|
||||
if parent == obj: # Avoid infinite loops
|
||||
break
|
||||
obj = parent
|
||||
|
||||
# Get current caret position if available
|
||||
if document:
|
||||
try:
|
||||
caret_offset = ax_object.AXObject.get_caret_offset(document)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def restore_focus():
|
||||
try:
|
||||
from cthulhu import ax_object
|
||||
# First try direct focus restoration if object still exists
|
||||
if not ax_object.AXObject.is_dead(original_focus):
|
||||
original_focus.grabFocus()
|
||||
return False
|
||||
|
||||
# If we have document and caret info, try to restore position
|
||||
if document and not ax_object.AXObject.is_dead(document) and caret_offset >= 0:
|
||||
try:
|
||||
# Set caret back to where it was
|
||||
ax_object.AXObject.set_caret_offset(document, caret_offset)
|
||||
# Focus the document to make sure screen reader tracks properly
|
||||
document.grabFocus()
|
||||
return False
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Last resort: try to focus the document if we have it
|
||||
if document and not ax_object.AXObject.is_dead(document):
|
||||
document.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
|
||||
GObject.timeout_add(150, restore_focus) # 150ms delay for DOM changes
|
||||
|
||||
except Exception:
|
||||
pass # Focus restoration is best effort
|
||||
|
||||
def _presentDelayedMessage(self, message, delay_ms):
|
||||
"""Present a message after a specified delay in milliseconds."""
|
||||
try:
|
||||
from gi.repository import GObject
|
||||
|
||||
def present_message():
|
||||
self.presentMessage(message)
|
||||
return False # Don't repeat
|
||||
|
||||
GObject.timeout_add(delay_ms, present_message)
|
||||
except Exception:
|
||||
# If delay fails, present immediately
|
||||
self.presentMessage(message)
|
||||
|
||||
def activateClickableElement(self, inputEvent):
|
||||
"""Activates clickable element at current focus via Return key."""
|
||||
return self._tryClickableActivation(inputEvent)
|
||||
|
Reference in New Issue
Block a user