A few more web tweaks. Updated version stuff, made a small script to update all version strings.
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
# Maintainer: Storm Dragon <storm_dragon@stormux.org>
|
# Maintainer: Storm Dragon <storm_dragon@stormux.org>
|
||||||
|
|
||||||
pkgname=cthulhu
|
pkgname=cthulhu
|
||||||
pkgver=2025.12.09
|
pkgver=2025.12.27
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Desktop-agnostic screen reader with plugin system, forked from Orca"
|
pkgdesc="Desktop-agnostic screen reader with plugin system, forked from Orca"
|
||||||
url="https://git.stormux.org/storm/cthulhu"
|
url="https://git.stormux.org/storm/cthulhu"
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
project('cthulhu',
|
project('cthulhu',
|
||||||
version: '2025.12.09',
|
version: '2025.12.27',
|
||||||
meson_version: '>= 1.0.0',
|
meson_version: '>= 1.0.0',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Executable
+53
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
newVersion="${1:-}"
|
||||||
|
|
||||||
|
if [[ -z "$newVersion" ]]; then
|
||||||
|
newVersion="$(date +%Y.%m.%d)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! "$newVersion" =~ ^[0-9]{4}\.[0-9]{2}\.[0-9]{2}$ ]]; then
|
||||||
|
echo "Usage: $(basename "$0") [YYYY.MM.DD]" >&2
|
||||||
|
echo "Error: version must match YYYY.MM.DD" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cthulhuVersionFile="${scriptDir}/src/cthulhu/cthulhuVersion.py"
|
||||||
|
mesonFile="${scriptDir}/meson.build"
|
||||||
|
pkgbuildFile="${scriptDir}/distro-packages/Arch-Linux/PKGBUILD"
|
||||||
|
|
||||||
|
for path in "$cthulhuVersionFile" "$mesonFile" "$pkgbuildFile"; do
|
||||||
|
if [[ ! -f "$path" ]]; then
|
||||||
|
echo "Error: Missing file: $path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
sed -i "s/^version = \".*\"/version = \"${newVersion}\"/" "$cthulhuVersionFile"
|
||||||
|
sed -i "s/^ version: '.*',/ version: '${newVersion}',/" "$mesonFile"
|
||||||
|
sed -i "s/^pkgver=.*/pkgver=${newVersion}/" "$pkgbuildFile"
|
||||||
|
sed -i "s/^pkgrel=.*/pkgrel=1/" "$pkgbuildFile"
|
||||||
|
|
||||||
|
if ! rg -q "^version = \"${newVersion}\"" "$cthulhuVersionFile"; then
|
||||||
|
echo "Error: Failed to update ${cthulhuVersionFile}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! rg -q "^ version: '${newVersion}'," "$mesonFile"; then
|
||||||
|
echo "Error: Failed to update ${mesonFile}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! rg -q "^pkgver=${newVersion}$" "$pkgbuildFile"; then
|
||||||
|
echo "Error: Failed to update ${pkgbuildFile}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! rg -q "^pkgrel=1$" "$pkgbuildFile"; then
|
||||||
|
echo "Error: Failed to reset pkgrel in ${pkgbuildFile}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Updated version to ${newVersion} in:" \
|
||||||
|
"${cthulhuVersionFile}" \
|
||||||
|
"${mesonFile}" \
|
||||||
|
"${pkgbuildFile}"
|
||||||
@@ -43,12 +43,13 @@ gi.require_version("Gtk", "3.0")
|
|||||||
from gi.repository import Gdk, GLib, Gtk
|
from gi.repository import Gdk, GLib, Gtk
|
||||||
|
|
||||||
from . import cmdnames
|
from . import cmdnames
|
||||||
|
from . import dbus_service
|
||||||
from . import debug
|
from . import debug
|
||||||
|
from . import focus_manager
|
||||||
from . import guilabels
|
from . import guilabels
|
||||||
from . import input_event
|
from . import input_event
|
||||||
from . import keybindings
|
from . import keybindings
|
||||||
from . import messages
|
from . import messages
|
||||||
from . import cthulhu
|
|
||||||
from . import cthulhu_state
|
from . import cthulhu_state
|
||||||
from . import script_manager
|
from . import script_manager
|
||||||
from .ax_object import AXObject
|
from .ax_object import AXObject
|
||||||
@@ -121,16 +122,21 @@ class ActionList(Gtk.Window):
|
|||||||
|
|
||||||
def populate_actions(self, actions: list[str]) -> None:
|
def populate_actions(self, actions: list[str]) -> None:
|
||||||
"""Populates the list with accessible actions."""
|
"""Populates the list with accessible actions."""
|
||||||
|
|
||||||
self._actions = actions
|
if isinstance(actions, dict):
|
||||||
|
items = list(actions.items())
|
||||||
|
else:
|
||||||
|
items = [(action, action) for action in actions]
|
||||||
|
|
||||||
|
self._actions = [name for name, _label in items]
|
||||||
|
|
||||||
# Clear existing items
|
# Clear existing items
|
||||||
for child in self._listbox.get_children():
|
for child in self._listbox.get_children():
|
||||||
self._listbox.remove(child)
|
self._listbox.remove(child)
|
||||||
|
|
||||||
# Add actions to list
|
# Add actions to list
|
||||||
for action in actions:
|
for _action, label_text in items:
|
||||||
label = Gtk.Label(label=action)
|
label = Gtk.Label(label=label_text)
|
||||||
label.set_xalign(0.0) # Left align
|
label.set_xalign(0.0) # Left align
|
||||||
label.set_margin_left(10)
|
label.set_margin_left(10)
|
||||||
label.set_margin_right(10)
|
label.set_margin_right(10)
|
||||||
@@ -163,6 +169,11 @@ class ActionPresenter:
|
|||||||
self._handlers = self.get_handlers(True)
|
self._handlers = self.get_handlers(True)
|
||||||
# _bindings will be initialized lazily in get_bindings()
|
# _bindings will be initialized lazily in get_bindings()
|
||||||
|
|
||||||
|
msg = "ACTION PRESENTER: Registering D-Bus commands."
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||||
|
controller = dbus_service.get_remote_controller()
|
||||||
|
controller.register_decorated_module("ActionPresenter", self)
|
||||||
|
|
||||||
def get_handlers(self, refresh: bool = False) -> dict:
|
def get_handlers(self, refresh: bool = False) -> dict:
|
||||||
"""Returns a dictionary of input event handlers."""
|
"""Returns a dictionary of input event handlers."""
|
||||||
|
|
||||||
@@ -226,9 +237,28 @@ class ActionPresenter:
|
|||||||
script = script_manager.get_manager().get_script(app, self._obj)
|
script = script_manager.get_manager().get_script(app, self._obj)
|
||||||
script_manager.get_manager().set_active_script(script, reason)
|
script_manager.get_manager().set_active_script(script, reason)
|
||||||
|
|
||||||
# Update Cthulhu state
|
manager = focus_manager.get_manager()
|
||||||
cthulhu_state.activeWindow = self._window
|
manager.clear_state(reason)
|
||||||
cthulhu_state.locusOfFocus = self._obj
|
manager.set_active_window(self._window)
|
||||||
|
manager.set_locus_of_focus(None, self._obj)
|
||||||
|
|
||||||
|
def _present_message(self, script, full_message, brief_message=None, notify_user=True) -> None:
|
||||||
|
"""Presents a message using the provided script or the active script."""
|
||||||
|
|
||||||
|
if not notify_user:
|
||||||
|
return
|
||||||
|
|
||||||
|
if script is not None:
|
||||||
|
script.presentMessage(full_message, brief_message)
|
||||||
|
return
|
||||||
|
|
||||||
|
active_script = cthulhu_state.activeScript
|
||||||
|
if active_script is not None:
|
||||||
|
active_script.presentMessage(full_message, brief_message)
|
||||||
|
return
|
||||||
|
|
||||||
|
msg = "ACTION PRESENTER: Unable to present message (no script)."
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||||
|
|
||||||
def _clear_gui_and_restore_focus(self) -> None:
|
def _clear_gui_and_restore_focus(self) -> None:
|
||||||
"""Clears the GUI reference and then restores focus."""
|
"""Clears the GUI reference and then restores focus."""
|
||||||
@@ -252,7 +282,7 @@ class ActionPresenter:
|
|||||||
# Use idle_add for asynchronous destruction to allow action to complete
|
# Use idle_add for asynchronous destruction to allow action to complete
|
||||||
GLib.idle_add(self._gui.destroy)
|
GLib.idle_add(self._gui.destroy)
|
||||||
|
|
||||||
def present_with_time(self, obj, start_time: float) -> bool:
|
def present_with_time(self, obj, start_time: float, script=None, notify_user=True) -> bool:
|
||||||
"""Presents accessible actions for the given object with timing."""
|
"""Presents accessible actions for the given object with timing."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -264,28 +294,36 @@ class ActionPresenter:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if obj is None:
|
if obj is None:
|
||||||
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: obj is None, using locusOfFocus", True)
|
msg = "ACTION PRESENTER: No object found, presenting LOCATION_NOT_FOUND"
|
||||||
obj = cthulhu_state.locusOfFocus
|
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||||
|
full_message = messages.LOCATION_NOT_FOUND_FULL
|
||||||
if obj is None:
|
brief_message = messages.LOCATION_NOT_FOUND_BRIEF
|
||||||
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: No object found, presenting NO_ACCESSIBLE_ACTIONS", True)
|
self._present_message(script, full_message, brief_message, notify_user)
|
||||||
full_message = messages.NO_ACCESSIBLE_ACTIONS
|
return True
|
||||||
cthulhu.presentMessage(full_message)
|
|
||||||
return False
|
|
||||||
|
|
||||||
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Getting actions for object: {obj}", True)
|
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Getting actions for object: {obj}", True)
|
||||||
actions = AXObject.get_action_names(obj)
|
actions = {}
|
||||||
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Found {len(actions) if actions else 0} actions: {actions}", True)
|
for i in range(AXObject.get_n_actions(obj)):
|
||||||
|
name = AXObject.get_action_name(obj, i)
|
||||||
|
if not name:
|
||||||
|
continue
|
||||||
|
localized_name = AXObject.get_action_localized_name(obj, i)
|
||||||
|
description = AXObject.get_action_description(obj, i)
|
||||||
|
actions[name] = localized_name or description or name
|
||||||
|
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Found {len(actions)} actions: {actions}", True)
|
||||||
|
|
||||||
if not actions:
|
if not actions:
|
||||||
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: No actions found, presenting NO_ACCESSIBLE_ACTIONS", True)
|
msg = "ACTION PRESENTER: No actions found, presenting NO_ACTIONS_FOUND_ON"
|
||||||
full_message = messages.NO_ACCESSIBLE_ACTIONS
|
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||||
cthulhu.presentMessage(full_message)
|
name = AXObject.get_name(obj) or AXUtilities.get_localized_role_name(obj)
|
||||||
return False
|
full_message = messages.NO_ACTIONS_FOUND_ON % name
|
||||||
|
self._present_message(script, full_message, notify_user=notify_user)
|
||||||
|
return True
|
||||||
|
|
||||||
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: Creating GUI", True)
|
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: Creating GUI", True)
|
||||||
self._obj = obj
|
self._obj = obj
|
||||||
self._window = cthulhu_state.activeWindow
|
self._window = focus_manager.get_manager().get_active_window()
|
||||||
|
|
||||||
self._gui = ActionList(self)
|
self._gui = ActionList(self)
|
||||||
self._gui.populate_actions(actions)
|
self._gui.populate_actions(actions)
|
||||||
@@ -298,7 +336,13 @@ class ActionPresenter:
|
|||||||
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Traceback: {traceback.format_exc()}", True)
|
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Traceback: {traceback.format_exc()}", True)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def show_actions_list(self, script: script.Script = None, input_event: input_event.InputEvent = None) -> bool:
|
@dbus_service.command
|
||||||
|
def show_actions_list(
|
||||||
|
self,
|
||||||
|
script: script.Script = None,
|
||||||
|
input_event: input_event.InputEvent = None,
|
||||||
|
notify_user: bool = True
|
||||||
|
) -> bool:
|
||||||
"""Shows the accessible actions list."""
|
"""Shows the accessible actions list."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -306,12 +350,19 @@ class ActionPresenter:
|
|||||||
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||||
|
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|
||||||
# Get object from input event if available, otherwise let present_with_time handle it
|
obj = None
|
||||||
obj = input_event.get_object() if input_event else None
|
if input_event is not None:
|
||||||
|
obj = input_event.get_object()
|
||||||
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Object from input_event: {obj}", True)
|
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Object from input_event: {obj}", True)
|
||||||
|
|
||||||
result = self.present_with_time(obj, start_time)
|
if obj is None:
|
||||||
|
manager = focus_manager.get_manager()
|
||||||
|
_mode, obj = manager.get_active_mode_and_object_of_interest()
|
||||||
|
if obj is None:
|
||||||
|
obj = manager.get_locus_of_focus()
|
||||||
|
|
||||||
|
result = self.present_with_time(obj, start_time, script, notify_user)
|
||||||
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: show_actions_list returning: {result}", True)
|
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: show_actions_list returning: {result}", True)
|
||||||
return result
|
return result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -325,4 +376,4 @@ _presenter = ActionPresenter()
|
|||||||
|
|
||||||
def getPresenter() -> ActionPresenter:
|
def getPresenter() -> ActionPresenter:
|
||||||
"""Returns the Action Presenter singleton."""
|
"""Returns the Action Presenter singleton."""
|
||||||
return _presenter
|
return _presenter
|
||||||
|
|||||||
@@ -23,5 +23,5 @@
|
|||||||
# Forked from Orca screen reader.
|
# Forked from Orca screen reader.
|
||||||
# Cthulhu project: https://git.stormux.org/storm/cthulhu
|
# Cthulhu project: https://git.stormux.org/storm/cthulhu
|
||||||
|
|
||||||
version = "2025.12.09"
|
version = "2025.12.27"
|
||||||
codeName = "testing"
|
codeName = "testing"
|
||||||
|
|||||||
@@ -31,11 +31,9 @@ __date__ = "$Date$"
|
|||||||
__copyright__ = "Copyright (C) 2011-2013 Igalia, S.L."
|
__copyright__ = "Copyright (C) 2011-2013 Igalia, S.L."
|
||||||
__license__ = "LGPL"
|
__license__ = "LGPL"
|
||||||
|
|
||||||
import gi
|
|
||||||
gi.require_version("Atspi", "2.0")
|
|
||||||
from gi.repository import Atspi
|
|
||||||
|
|
||||||
from . import debug
|
from . import debug
|
||||||
|
from .ax_component import AXComponent
|
||||||
|
from .ax_hypertext import AXHypertext
|
||||||
from .ax_object import AXObject
|
from .ax_object import AXObject
|
||||||
from .ax_table import AXTable
|
from .ax_table import AXTable
|
||||||
from .ax_text import AXText
|
from .ax_text import AXText
|
||||||
@@ -80,7 +78,9 @@ class LabelInference:
|
|||||||
result, objects = self.inferFromTextLeft(obj)
|
result, objects = self.inferFromTextLeft(obj)
|
||||||
debug.printMessage(debug.LEVEL_INFO, f"LABEL INFERENCE: Text Left: '{result}'", True)
|
debug.printMessage(debug.LEVEL_INFO, f"LABEL INFERENCE: Text Left: '{result}'", True)
|
||||||
if not result or self._preferRight(obj):
|
if not result or self._preferRight(obj):
|
||||||
result, objects = self.inferFromTextRight(obj) or result
|
rightResult = self.inferFromTextRight(obj)
|
||||||
|
if rightResult[0] is not None:
|
||||||
|
result, objects = rightResult
|
||||||
debug.printMessage(debug.LEVEL_INFO, f"LABEL INFERENCE: Text Right: '{result}'", True)
|
debug.printMessage(debug.LEVEL_INFO, f"LABEL INFERENCE: Text Right: '{result}'", True)
|
||||||
if not result:
|
if not result:
|
||||||
result, objects = self.inferFromTable(obj)
|
result, objects = self.inferFromTable(obj)
|
||||||
@@ -153,17 +153,12 @@ class LabelInference:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def isMatch(x):
|
def isMatch(x):
|
||||||
return x is not None \
|
return AXUtilities.is_web_element(x) and not AXUtilities.is_link(x)
|
||||||
and not self._script.utilities.isStaticTextLeaf(x) \
|
|
||||||
and not AXUtilities.is_link(x)
|
|
||||||
|
|
||||||
children = [child for child in AXObject.iter_children(obj, isMatch)]
|
children = list(AXObject.iter_children(obj, isMatch))
|
||||||
if len(children) > 1:
|
if len(children) > 1:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not AXObject.supports_text(obj):
|
|
||||||
return True
|
|
||||||
|
|
||||||
string = AXText.get_all_text(obj).strip()
|
string = AXText.get_all_text(obj).strip()
|
||||||
if string.count(self._script.EMBEDDED_OBJECT_CHARACTER) > 1:
|
if string.count(self._script.EMBEDDED_OBJECT_CHARACTER) > 1:
|
||||||
return False
|
return False
|
||||||
@@ -191,19 +186,7 @@ class LabelInference:
|
|||||||
if rv is not None:
|
if rv is not None:
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
widgetRoles = [Atspi.Role.CHECK_BOX,
|
isWidget = AXUtilities.is_widget(obj) or AXUtilities.is_menu_related(obj)
|
||||||
Atspi.Role.RADIO_BUTTON,
|
|
||||||
Atspi.Role.TOGGLE_BUTTON,
|
|
||||||
Atspi.Role.COMBO_BOX,
|
|
||||||
Atspi.Role.LIST,
|
|
||||||
Atspi.Role.LIST_BOX,
|
|
||||||
Atspi.Role.MENU,
|
|
||||||
Atspi.Role.MENU_ITEM,
|
|
||||||
Atspi.Role.ENTRY,
|
|
||||||
Atspi.Role.PASSWORD_TEXT,
|
|
||||||
Atspi.Role.PUSH_BUTTON]
|
|
||||||
|
|
||||||
isWidget = AXObject.get_role(obj) in widgetRoles
|
|
||||||
if not isWidget and AXUtilities.is_editable(obj):
|
if not isWidget and AXUtilities.is_editable(obj):
|
||||||
isWidget = True
|
isWidget = True
|
||||||
|
|
||||||
@@ -223,30 +206,15 @@ class LabelInference:
|
|||||||
return rv
|
return rv
|
||||||
|
|
||||||
extents = 0, 0, 0, 0
|
extents = 0, 0, 0, 0
|
||||||
text = self._script.utilities.queryNonEmptyText(obj)
|
if AXObject.supports_text(obj) and not AXUtilities.is_text_input(obj):
|
||||||
if text and not AXUtilities.is_text_input(obj):
|
|
||||||
if endOffset == -1:
|
if endOffset == -1:
|
||||||
endOffset = AXText.get_character_count(obj)
|
endOffset = AXText.get_character_count(obj)
|
||||||
|
rect = AXText.get_range_rect(obj, startOffset, endOffset)
|
||||||
try:
|
extents = rect.x, rect.y, rect.width, rect.height
|
||||||
extents = Atspi.Text.get_range_extents(
|
|
||||||
obj, startOffset, endOffset, Atspi.CoordType.SCREEN)
|
|
||||||
except Exception:
|
|
||||||
tokens = ["LABEL INFERENCE: Exception getting character count for", obj]
|
|
||||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
|
||||||
return extents
|
|
||||||
|
|
||||||
if not (extents[2] and extents[3]):
|
if not (extents[2] and extents[3]):
|
||||||
if not AXObject.supports_component(obj):
|
rect = AXComponent.get_rect(obj)
|
||||||
tokens = ["LABEL INFERENCE:", obj, "does not support the component interface"]
|
extents = rect.x, rect.y, rect.width, rect.height
|
||||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
ext = Atspi.Component.get_extents(obj, Atspi.CoordType.SCREEN)
|
|
||||||
extents = ext.x, ext.y, ext.width, ext.height
|
|
||||||
except Exception:
|
|
||||||
tokens = ["LABEL INFERENCE: Exception getting extents for", obj]
|
|
||||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
|
||||||
|
|
||||||
self._extentsCache[(hash(obj), startOffset, endOffset)] = extents
|
self._extentsCache[(hash(obj), startOffset, endOffset)] = extents
|
||||||
return extents
|
return extents
|
||||||
@@ -278,10 +246,12 @@ class LabelInference:
|
|||||||
|
|
||||||
key = hash(obj)
|
key = hash(obj)
|
||||||
if self._isWidget(obj):
|
if self._isWidget(obj):
|
||||||
start, end = self._script.utilities.getHyperlinkRange(obj)
|
start = AXHypertext.get_link_start_offset(obj)
|
||||||
obj = AXObject.get_parent(obj)
|
obj = AXObject.get_parent(obj)
|
||||||
|
|
||||||
rv = self._script.utilities.getLineContentsAtOffset(obj, start, True, False)
|
rv = self._script.utilities.getLineContentsAtOffset(obj, start, True, False)
|
||||||
|
if rv is None:
|
||||||
|
rv = []
|
||||||
self._lineCache[key] = rv
|
self._lineCache[key] = rv
|
||||||
|
|
||||||
return rv
|
return rv
|
||||||
@@ -408,6 +378,10 @@ class LabelInference:
|
|||||||
if self._cannotLabel(prevObj):
|
if self._cannotLabel(prevObj):
|
||||||
return None, []
|
return None, []
|
||||||
|
|
||||||
|
if string.endswith("\n"):
|
||||||
|
string = string[:-1]
|
||||||
|
end -= 1
|
||||||
|
|
||||||
if string.strip():
|
if string.strip():
|
||||||
x, y, width, height = self._getExtents(prevObj, start, end)
|
x, y, width, height = self._getExtents(prevObj, start, end)
|
||||||
objX, objY, objWidth, objHeight = self._getExtents(obj)
|
objX, objY, objWidth, objHeight = self._getExtents(obj)
|
||||||
@@ -528,7 +502,7 @@ class LabelInference:
|
|||||||
|
|
||||||
cellLeft = cellRight = cellAbove = cellBelow = None
|
cellLeft = cellRight = cellAbove = cellBelow = None
|
||||||
gridrow = AXObject.find_ancestor(cell, self._isRow)
|
gridrow = AXObject.find_ancestor(cell, self._isRow)
|
||||||
rowindex, colindex = self._script.utilities.coordinatesForCell(cell)
|
rowindex, colindex = AXTable.get_cell_coordinates(cell, prefer_attribute=False)
|
||||||
if colindex > -1:
|
if colindex > -1:
|
||||||
cellLeft = self._getCellFromTable(grid, rowindex, colindex - 1)
|
cellLeft = self._getCellFromTable(grid, rowindex, colindex - 1)
|
||||||
cellRight = self._getCellFromTable(grid, rowindex, colindex + 1)
|
cellRight = self._getCellFromTable(grid, rowindex, colindex + 1)
|
||||||
|
|||||||
@@ -3547,7 +3547,11 @@ class Utilities(script_utilities.Utilities):
|
|||||||
|
|
||||||
if rv and not AXObject.get_name(obj) and AXObject.supports_text(obj):
|
if rv and not AXObject.get_name(obj) and AXObject.supports_text(obj):
|
||||||
string = AXText.get_all_text(obj)
|
string = AXText.get_all_text(obj)
|
||||||
if not string.strip():
|
if not string.replace("\ufffc", ""):
|
||||||
|
tokens = ["WEB:", obj, "is not clickable: its text is just EOCs"]
|
||||||
|
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||||
|
rv = False
|
||||||
|
elif not string.strip():
|
||||||
rv = not (AXUtilities.is_static(obj) or AXUtilities.is_link(obj))
|
rv = not (AXUtilities.is_static(obj) or AXUtilities.is_link(obj))
|
||||||
|
|
||||||
self._isClickableElement[hash(obj)] = rv
|
self._isClickableElement[hash(obj)] = rv
|
||||||
|
|||||||
Reference in New Issue
Block a user