Fix GTK 4 foreground matching and menu labels
This commit is contained in:
1 parent
d0c977281d
commit
49eeae89f4
8 files changed
+254
-9
No files matched your search
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2026 Storm Dragon
|
||||
# Authors: Storm Dragon, Angus Kola
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
"""A GTK 4 popover menu, whose items expose their label only through a relation."""
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "4.0")
|
||||
from gi.repository import Gio, GLib, Gtk
|
||||
|
||||
|
||||
def activate(app):
|
||||
window = Gtk.ApplicationWindow(application=app, title="GTK 4 menu label probe")
|
||||
for name in ("compose", "add-account", "grouping"):
|
||||
action = (Gio.SimpleAction.new_stateful(name, None, GLib.Variant.new_boolean(True))
|
||||
if name == "grouping" else Gio.SimpleAction.new(name, None))
|
||||
window.add_action(action)
|
||||
app.set_accels_for_action("win.compose", ["<Control>n"])
|
||||
window.add_action(Gio.SimpleAction.new_stateful("filter", GLib.VariantType.new("s"),
|
||||
GLib.Variant.new_string("all")))
|
||||
menu = Gio.Menu()
|
||||
menu.append("_New Message", "win.compose")
|
||||
menu.append("_Add Account…", "win.add-account")
|
||||
menu.append("Group into _Conversations", "win.grouping")
|
||||
menu.append("_All Messages", "win.filter::all")
|
||||
button = Gtk.MenuButton(label="File", menu_model=menu)
|
||||
window.set_child(button)
|
||||
window.present()
|
||||
GLib.timeout_add(300, lambda: (button.popup(), False)[1])
|
||||
|
||||
|
||||
app = Gtk.Application(application_id="org.stormux.CthulhuGtk4MenuProbe")
|
||||
app.connect("activate", activate)
|
||||
app.run(None)
|
||||
@@ -31,5 +31,29 @@ field("multiple", ["Account", "identifier"])
|
||||
field("named", ["Other label"], name="Explicit name")
|
||||
field("unlabelled", [])
|
||||
field("protected", ["Password"], password=True)
|
||||
|
||||
# Exercise the role-invalid Text fallback used by GTK 4, and ensure that a
|
||||
# password relation target cannot contribute either its name or its contents.
|
||||
invalid_label = Gtk.Label(label="Fallback label")
|
||||
invalid_label.get_accessible().set_role(Atk.Role.INVALID)
|
||||
invalid_label.get_accessible().set_name("")
|
||||
box.pack_start(invalid_label, False, False, 0)
|
||||
invalid_entry = Gtk.Entry()
|
||||
invalid_entry.get_accessible().set_name("")
|
||||
invalid_entry.get_accessible().set_description("invalid-label")
|
||||
invalid_entry.get_accessible().add_relationship(Atk.RelationType.LABELLED_BY,
|
||||
invalid_label.get_accessible())
|
||||
box.pack_start(invalid_entry, False, False, 0)
|
||||
password_label = Gtk.Entry()
|
||||
password_label.set_visibility(False)
|
||||
password_label.set_text("secret relation value")
|
||||
password_label.get_accessible().set_name("secret relation name")
|
||||
box.pack_start(password_label, False, False, 0)
|
||||
safe_entry = Gtk.Entry()
|
||||
safe_entry.get_accessible().set_name("")
|
||||
safe_entry.get_accessible().set_description("password-target")
|
||||
safe_entry.get_accessible().add_relationship(Atk.RelationType.LABELLED_BY,
|
||||
password_label.get_accessible())
|
||||
box.pack_start(safe_entry, False, False, 0)
|
||||
window.show_all()
|
||||
Gtk.main()
|
||||
+58
-3
@@ -26,6 +26,9 @@ def run_inside():
|
||||
if os.environ.get("CTHULHU_NATIVE_EDIT_LABELS") == "1":
|
||||
run_edit_label_probe()
|
||||
return
|
||||
if os.environ.get("CTHULHU_NATIVE_GTK4_MENU_LABELS") == "1":
|
||||
run_gtk4_menu_label_probe()
|
||||
return
|
||||
if os.environ.get("CTHULHU_FOREGROUND_PID") == "1":
|
||||
if os.environ.get("CTHULHU_FOREGROUND_PROXY") == "1":
|
||||
run_proxy_probe()
|
||||
@@ -58,11 +61,20 @@ def run_inside():
|
||||
def run_edit_label_probe():
|
||||
fixture = subprocess.Popen([sys.executable, str(Path(__file__).with_name("gtk_edit_labels.py"))])
|
||||
try:
|
||||
import pyatspi
|
||||
import gi
|
||||
|
||||
gi.require_version("Atspi", "2.0")
|
||||
from gi.repository import Atspi
|
||||
|
||||
def find_window():
|
||||
return any(window.name == "Edit label probe"
|
||||
for app in pyatspi.Registry.getDesktop(0) for window in app)
|
||||
desktop = Atspi.get_desktop(0)
|
||||
for i in range(desktop.get_child_count()):
|
||||
app = desktop.get_child_at_index(i)
|
||||
for j in range(app.get_child_count() if app else 0):
|
||||
window = app.get_child_at_index(j)
|
||||
if window is not None and window.get_name() == "Edit label probe":
|
||||
return True
|
||||
return False
|
||||
|
||||
wait_for(find_window, "edit label probe missing")
|
||||
environment = dict(os.environ, GOCACHE="/tmp/cthulhu-go-cache", GOPROXY="off")
|
||||
@@ -76,6 +88,49 @@ def run_edit_label_probe():
|
||||
fixture.wait(timeout=3)
|
||||
|
||||
|
||||
def run_gtk4_menu_label_probe():
|
||||
fixture = subprocess.Popen([sys.executable, str(Path(__file__).with_name("gtk4_menu_labels.py"))],
|
||||
env=dict(os.environ, GTK_A11Y="atspi"))
|
||||
try:
|
||||
import gi
|
||||
|
||||
gi.require_version("Atspi", "2.0")
|
||||
from gi.repository import Atspi
|
||||
|
||||
def showing_menu_item(node, depth=0):
|
||||
if node is None or depth > 40:
|
||||
return False
|
||||
if node.get_role_name() in ("menu item", "check menu item", "radio menu item") and \
|
||||
node.get_state_set().contains(Atspi.StateType.SHOWING):
|
||||
return True
|
||||
return any(showing_menu_item(node.get_child_at_index(i), depth + 1)
|
||||
for i in range(node.get_child_count()))
|
||||
|
||||
def menu_open():
|
||||
desktop = Atspi.get_desktop(0)
|
||||
for i in range(desktop.get_child_count()):
|
||||
app = desktop.get_child_at_index(i)
|
||||
for j in range(app.get_child_count() if app else 0):
|
||||
window = app.get_child_at_index(j)
|
||||
if window is not None and window.get_name() == "GTK 4 menu label probe":
|
||||
try:
|
||||
return showing_menu_item(window)
|
||||
except Exception: # noqa: BLE001 - tree still settling
|
||||
return False
|
||||
return False
|
||||
|
||||
wait_for(menu_open, "GTK 4 menu probe missing")
|
||||
environment = dict(os.environ, GOCACHE="/tmp/cthulhu-go-cache", GOPROXY="off")
|
||||
subprocess.run(
|
||||
["go", "test", "./internal/atspi", "-run", "^TestNativeGTK4MenuItemLabels$",
|
||||
"-count=1", "-timeout=25s", "-v"],
|
||||
cwd=Path(__file__).resolve().parents[1], env=environment, check=True,
|
||||
)
|
||||
finally:
|
||||
fixture.terminate()
|
||||
fixture.wait(timeout=3)
|
||||
|
||||
|
||||
def run_thunar_probe():
|
||||
with tempfile.TemporaryDirectory(prefix="cthulhu-thunar-") as temporary:
|
||||
home = Path(temporary)
|
||||
|
||||
Reference in new issue
Block a user