Started porting over some bug fixes and modernizations from Orca.

This commit is contained in:
Storm Dragon
2026-01-06 12:09:40 -05:00
parent 21cfec55ee
commit 3c4cbe3c3f
5 changed files with 18 additions and 39 deletions
+1 -1
View File
@@ -540,7 +540,7 @@ class Component(Region):
been scrolled off the display.""" been scrolled off the display."""
if cthulhu_state.activeScript and cthulhu_state.activeScript.utilities.\ if cthulhu_state.activeScript and cthulhu_state.activeScript.utilities.\
grabFocusBeforeRouting(self.accessible, offset): grabFocusBeforeRouting(self.accessible):
if AXObject.supports_component(self.accessible): if AXObject.supports_component(self.accessible):
try: try:
Atspi.Component.grab_focus(self.accessible) Atspi.Component.grab_focus(self.accessible)
+1 -2
View File
@@ -560,13 +560,12 @@ class Utilities:
return False return False
def grabFocusBeforeRouting(self, obj, offset): def grabFocusBeforeRouting(self, obj):
"""Whether or not we should perform a grabFocus before routing """Whether or not we should perform a grabFocus before routing
the cursor via the braille cursor routing keys. the cursor via the braille cursor routing keys.
Arguments: Arguments:
- obj: the accessible object where the cursor should be routed - obj: the accessible object where the cursor should be routed
- offset: the offset to which it should be routed
Returns True if we should do an explicit grabFocus on obj prior Returns True if we should do an explicit grabFocus on obj prior
to routing the cursor. to routing the cursor.
@@ -81,9 +81,6 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
if not self._script.utilities.inDocumentContent(obj): if not self._script.utilities.inDocumentContent(obj):
return super()._generateAncestors(obj, **args) return super()._generateAncestors(obj, **args)
if self._script.inSayAll() and obj == cthulhu_state.locusOfFocus:
return []
result = [] result = []
priorObj = args.get('priorObj') priorObj = args.get('priorObj')
if priorObj and self._script.utilities.inDocumentContent(priorObj): if priorObj and self._script.utilities.inDocumentContent(priorObj):
+4
View File
@@ -651,6 +651,10 @@ class SpeechGenerator(generator.Generator):
Note that a 'role' attribute in args will override the Note that a 'role' attribute in args will override the
accessible role of the obj. accessible role of the obj.
""" """
# Prevent role repetition when object is same as prior object
if obj == args.get("priorObj"):
return []
if _settingsManager.getSetting('onlySpeakDisplayedText'): if _settingsManager.getSetting('onlySpeakDisplayedText'):
return [] return []
+12 -33
View File
@@ -596,15 +596,11 @@ class StructuralNavigation:
# #
self.lastTableCell = [-1, -1] self.lastTableCell = [-1, -1]
self._objectCache = {}
self._inModalDialog = False self._inModalDialog = False
def clearCache(self, document=None): def clearCache(self, document=None):
if document: """No-op for backwards compatibility. Caching has been removed."""
self._objectCache[hash(document)] = {} pass
else:
self._objectCache = {}
def structuralNavigationObjectCreator(self, name): def structuralNavigationObjectCreator(self, name):
"""This convenience method creates a StructuralNavigationObject """This convenience method creates a StructuralNavigationObject
@@ -794,7 +790,11 @@ class StructuralNavigation:
structuralNavigationObject.present(cell, arg) structuralNavigationObject.present(cell, arg)
def _getAll(self, structuralNavigationObject, arg=None): def _getAll(self, structuralNavigationObject, arg=None):
"""Returns all the instances of structuralNavigationObject.""" """Returns all the instances of structuralNavigationObject.
Queries fresh each time without caching to ensure accurate results
when returning to a page after switching applications.
"""
modalDialog = self._script.utilities.getModalDialog(cthulhu_state.locusOfFocus) modalDialog = self._script.utilities.getModalDialog(cthulhu_state.locusOfFocus)
inModalDialog = bool(modalDialog) inModalDialog = bool(modalDialog)
@@ -804,29 +804,11 @@ class StructuralNavigation:
f"{self._inModalDialog} to {inModalDialog}" f"{self._inModalDialog} to {inModalDialog}"
) )
debug.printMessage(debug.LEVEL_INFO, msg, True) debug.printMessage(debug.LEVEL_INFO, msg, True)
self.clearCache()
self._inModalDialog = inModalDialog self._inModalDialog = inModalDialog
def filterZombies(matchList):
if not matchList:
return []
return [match for match in matchList if not self._script.utilities.isZombie(match)]
document = self._script.utilities.documentFrame() document = self._script.utilities.documentFrame()
cache = self._objectCache.get(hash(document), {}) if not document:
key = f"{structuralNavigationObject.objType}:{arg}" return []
matches = cache.get(key, [])
if matches:
matches = filterZombies(matches)
if matches:
tokens = ["STRUCTURAL NAVIGATION: Returning", len(matches), "matches from cache"]
debug.printTokens(debug.LEVEL_INFO, tokens, True)
return matches.copy()
tokens = ["STRUCTURAL NAVIGATION: Cached matches are zombies; refreshing"]
debug.printTokens(debug.LEVEL_INFO, tokens, True)
cache.pop(key, None)
self._objectCache[hash(document)] = cache
if structuralNavigationObject.getter: if structuralNavigationObject.getter:
matches = structuralNavigationObject.getter(document, arg) matches = structuralNavigationObject.getter(document, arg)
@@ -843,15 +825,12 @@ class StructuralNavigation:
if inModalDialog: if inModalDialog:
originalSize = len(matches) originalSize = len(matches)
matches = [m for m in matches if AXObject.find_ancestor(m, lambda x: x == modalDialog)] matches = [m for m in matches if AXObject.find_ancestor(m, lambda x: x == modalDialog)]
tokens = ["STRUCTURAL NAVIGATION: Removed", {originalSize - len(matches)}, tokens = ["STRUCTURAL NAVIGATION: Removed", originalSize - len(matches),
"objects outside of modal dialog", modalDialog] "objects outside of modal dialog", modalDialog]
debug.printTokens(debug.LEVEL_INFO, tokens, True) debug.printTokens(debug.LEVEL_INFO, tokens, True)
matches = filterZombies(matches) # Filter out zombie objects and return
rv = matches.copy() return [m for m in matches if not self._script.utilities.isZombie(m)]
cache[key] = matches
self._objectCache[hash(document)] = cache
return rv
def goEdge(self, structuralNavigationObject, isStart, container=None, arg=None): def goEdge(self, structuralNavigationObject, isStart, container=None, arg=None):
if container is None: if container is None: