Almost read for new version.

This commit is contained in:
Storm Dragon
2025-12-28 16:58:29 -05:00
parent a9b4176672
commit 85b358d22b
5 changed files with 48 additions and 43 deletions

View File

@@ -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.27-testing 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"

View File

@@ -1,5 +1,5 @@
project('cthulhu', project('cthulhu',
version: '2025.12.27-testing', version: '2025.12.28-testing',
meson_version: '>= 1.0.0', meson_version: '>= 1.0.0',
) )

View File

@@ -72,7 +72,7 @@ if [[ -n "$codeNameValue" ]]; then
sed -i "s/^codeName = \".*\"/codeName = \"${codeNameValue}\"/" "$cthulhuVersionFile" sed -i "s/^codeName = \".*\"/codeName = \"${codeNameValue}\"/" "$cthulhuVersionFile"
fi fi
sed -i "s/^ version: '.*',/ version: '${fullVersion}',/" "$mesonFile" sed -i "s/^ version: '.*',/ version: '${fullVersion}',/" "$mesonFile"
sed -i "s/^pkgver=.*/pkgver=${fullVersion}/" "$pkgbuildFile" sed -i "s/^pkgver=.*/pkgver=${pythonVersion}/" "$pkgbuildFile"
sed -i "s/^pkgrel=.*/pkgrel=1/" "$pkgbuildFile" sed -i "s/^pkgrel=.*/pkgrel=1/" "$pkgbuildFile"
if ! rg -q "^version = \"${pythonVersion}\"" "$cthulhuVersionFile"; then if ! rg -q "^version = \"${pythonVersion}\"" "$cthulhuVersionFile"; then

View File

@@ -63,41 +63,26 @@ class ActionList(Gtk.Window):
"""Window containing a list of accessible actions.""" """Window containing a list of accessible actions."""
def __init__(self, presenter: ActionPresenter): def __init__(self, presenter: ActionPresenter):
super().__init__() super().__init__(window_position=Gtk.WindowPosition.MOUSE, transient_for=None)
self._presenter = presenter self._presenter = presenter
self._actions = []
self._setup_gui() self._setup_gui()
def _setup_gui(self) -> None: def _setup_gui(self) -> None:
"""Sets up the GUI for the actions list.""" """Sets up the GUI for the actions list."""
self.set_title(guilabels.KB_GROUP_ACTIONS) self.set_title(guilabels.KB_GROUP_ACTIONS)
self.set_modal(True)
self.set_decorated(False) self.set_decorated(False)
self.set_skip_taskbar_hint(True)
self.set_skip_pager_hint(True)
self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
# Note: set_window_position is deprecated, using move() instead
self.move(100, 100) # Position window at reasonable location
self.set_default_size(400, 300)
# Create scrolled window
scrolled = Gtk.ScrolledWindow()
scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
self.add(scrolled)
# Create list box
self._listbox = Gtk.ListBox() self._listbox = Gtk.ListBox()
self._listbox.set_selection_mode(Gtk.SelectionMode.SINGLE) self._listbox.set_selection_mode(Gtk.SelectionMode.SINGLE)
self._listbox.connect("row-activated", self._on_row_activated) self._listbox.connect("row-activated", self._on_row_activated)
scrolled.add(self._listbox) self._listbox.set_margin_top(5)
self._listbox.set_margin_bottom(5)
self.add(self._listbox)
# Connect key events
self.connect("key-press-event", self._on_key_press) self.connect("key-press-event", self._on_key_press)
self.connect("destroy", self._on_destroy) self.connect("destroy", self._on_destroy)
self.show_all()
def _on_key_press(self, widget, event) -> bool: def _on_key_press(self, widget, event) -> bool:
"""Handles key press events.""" """Handles key press events."""
@@ -108,19 +93,17 @@ class ActionList(Gtk.Window):
def _on_row_activated(self, listbox, row) -> None: def _on_row_activated(self, listbox, row) -> None:
"""Handles row activation (Enter or double-click).""" """Handles row activation (Enter or double-click)."""
if row is not None: action_name = getattr(row, "_action_name", None)
action_index = row.get_index() if action_name:
if 0 <= action_index < len(self._actions): self._presenter._perform_action(action_name)
action_name = self._actions[action_index]
self._presenter._perform_action(action_name)
def _on_destroy(self, widget) -> None: def _on_destroy(self, widget) -> None:
"""Handles window destruction.""" """Handles window destruction."""
GLib.idle_add(self._presenter._clear_gui_and_restore_focus) GLib.idle_add(self._presenter._clear_gui_and_restore_focus)
def populate_actions(self, actions: list[str]) -> None: def populate_actions(self, actions: dict[str, str] | list[str]) -> None:
"""Populates the list with accessible actions.""" """Populates the list with accessible actions."""
if isinstance(actions, dict): if isinstance(actions, dict):
@@ -128,22 +111,19 @@ class ActionList(Gtk.Window):
else: else:
items = [(action, action) for action in actions] 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, label_text in items: for action_name, label_text in items:
label = Gtk.Label(label=label_text) row = Gtk.ListBoxRow()
label.set_xalign(0.0) # Left align label = Gtk.Label(label=label_text, xalign=0)
label.set_margin_left(10) label.set_margin_start(10)
label.set_margin_right(10) label.set_margin_end(10)
label.set_margin_top(5) row.add(label)
label.set_margin_bottom(5) setattr(row, "_action_name", action_name)
self._listbox.add(row)
self._listbox.add(label)
# Select first item # Select first item
if actions: if actions:
@@ -152,7 +132,12 @@ class ActionList(Gtk.Window):
self._listbox.select_row(first_row) self._listbox.select_row(first_row)
first_row.grab_focus() first_row.grab_focus()
def show_gui(self) -> None:
"""Shows the window."""
self.show_all() self.show_all()
self.present_with_time(time.time())
self._listbox.grab_focus()
class ActionPresenter: class ActionPresenter:
@@ -264,7 +249,26 @@ class ActionPresenter:
"""Clears the GUI reference and then restores focus.""" """Clears the GUI reference and then restores focus."""
self._gui = None self._gui = None
GLib.timeout_add(150, self._maybe_restore_focus)
def _maybe_restore_focus(self) -> bool:
"""Restores focus unless it already moved to another object in the target app."""
if not AXObject.is_valid(self._obj):
return False
manager = focus_manager.get_manager()
current_focus = manager.get_locus_of_focus()
if current_focus and AXObject.is_valid(current_focus):
target_app = AXObject.get_application(self._obj)
focus_app = AXObject.get_application(current_focus)
if target_app and focus_app == target_app and current_focus != self._obj:
tokens = ["ACTION PRESENTER: Skipping focus restore; focus now on", current_focus]
debug.printTokens(debug.LEVEL_INFO, tokens, True)
return False
self._restore_focus() self._restore_focus()
return False
def _perform_action(self, action: str) -> None: def _perform_action(self, action: str) -> None:
"""Attempts to perform the named action.""" """Attempts to perform the named action."""
@@ -327,6 +331,7 @@ class ActionPresenter:
self._gui = ActionList(self) self._gui = ActionList(self)
self._gui.populate_actions(actions) self._gui.populate_actions(actions)
self._gui.show_gui()
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: GUI created successfully", True) debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: GUI created successfully", True)
return True return True

View File

@@ -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.27" version = "2025.12.28"
codeName = "testing" codeName = "testing"