Refine Wine dialog event presentation
This commit is contained in:
1 parent
1fd9acd94b
commit
f6b7f2ad71
5 files changed
+351
-13
No files matched your search
@@ -304,6 +304,13 @@ class Backend:
|
||||
|
||||
def _migrateSettings(self, settingsDict):
|
||||
"""Migrate old setting names to new ones."""
|
||||
migrationKey = 'wineAccessibilityPluginMigrated'
|
||||
activePlugins = settingsDict.get('activePlugins')
|
||||
if isinstance(activePlugins, list) and migrationKey not in settingsDict:
|
||||
if 'WineAccessibility' not in activePlugins:
|
||||
activePlugins.append('WineAccessibility')
|
||||
settingsDict[migrationKey] = True
|
||||
|
||||
# Migration: enableSpeechIndentation -> enableIndentation
|
||||
if 'enableSpeechIndentation' in settingsDict and 'enableIndentation' not in settingsDict:
|
||||
settingsDict['enableIndentation'] = settingsDict['enableSpeechIndentation']
|
||||
|
||||
@@ -25,6 +25,7 @@ import contextlib
|
||||
import enum
|
||||
import inspect
|
||||
import os
|
||||
import time
|
||||
import types
|
||||
import typing
|
||||
import xml.etree.ElementTree as ET
|
||||
@@ -641,6 +642,8 @@ class _InterfaceBuilder:
|
||||
class CthulhuDBusServiceInterface(Publishable):
|
||||
"""Internal D-Bus service object that handles D-Bus specifics."""
|
||||
|
||||
_WINE_FOCUS_DEBOUNCE_MS = 50
|
||||
|
||||
def for_publication(self):
|
||||
"""Returns the D-Bus interface XML for publication."""
|
||||
|
||||
@@ -744,15 +747,92 @@ class CthulhuDBusServiceInterface(Publishable):
|
||||
) -> bool:
|
||||
"""Presents a normalized accessibility event received from Wine."""
|
||||
|
||||
del sourceId, eventType, states
|
||||
del states
|
||||
eventType = eventType.casefold()
|
||||
debug.print_message(
|
||||
debug.LEVEL_INFO,
|
||||
(
|
||||
"WINE ACCESS EVENT: "
|
||||
f"type={eventType} source={sourceId} name={name!r} "
|
||||
f"role={role!r} value={value!r} window={windowTitle!r}"
|
||||
),
|
||||
True,
|
||||
)
|
||||
if eventType == "focus":
|
||||
self._wineAccessibleFocusSource = sourceId
|
||||
else:
|
||||
if eventType not in {"value", "selection"}:
|
||||
return False
|
||||
focusedSource = getattr(self, "_wineAccessibleFocusSource", None)
|
||||
focusedWindow = getattr(self, "_wineAccessibleWindowTitle", None)
|
||||
pendingFocus = getattr(self, "_wineAccessiblePendingFocus", None)
|
||||
if focusedWindow is None and pendingFocus is not None:
|
||||
focusedWindow = pendingFocus[2]
|
||||
if sourceId != focusedSource:
|
||||
if eventType != "selection":
|
||||
return False
|
||||
if not windowTitle or windowTitle != focusedWindow:
|
||||
return False
|
||||
|
||||
if role.casefold() == "client":
|
||||
role = ""
|
||||
parts = [part for part in (name, role, value, description) if part]
|
||||
if position > 0 and count > 0:
|
||||
parts.append(f"{position} of {count}")
|
||||
if windowTitle and windowTitle not in parts:
|
||||
parts.append(windowTitle)
|
||||
if not parts:
|
||||
if not parts and not (eventType == "focus" and windowTitle):
|
||||
return False
|
||||
return self.PresentMessage(", ".join(parts))
|
||||
|
||||
message = ", ".join(parts)
|
||||
now = time.monotonic()
|
||||
signature = (sourceId, message or windowTitle)
|
||||
lastSignature = getattr(self, "_wineAccessibleLastSignature", None)
|
||||
lastTime = getattr(self, "_wineAccessibleLastTime", 0.0)
|
||||
if signature == lastSignature and now - lastTime < 0.1:
|
||||
if eventType == "focus":
|
||||
self._wineAccessiblePendingFocus = None
|
||||
return False
|
||||
|
||||
if eventType == "focus":
|
||||
self._wineAccessiblePendingFocus = (signature, parts, windowTitle)
|
||||
if not getattr(self, "_wineAccessibleFocusTimeoutId", 0):
|
||||
self._wineAccessibleFocusTimeoutId = GLib.timeout_add(
|
||||
self._WINE_FOCUS_DEBOUNCE_MS,
|
||||
self._present_pending_accessible_focus,
|
||||
)
|
||||
return True
|
||||
|
||||
pending = getattr(self, "_wineAccessiblePendingFocus", None)
|
||||
if pending is not None and (
|
||||
pending[0][0] == sourceId or (windowTitle and pending[2] == windowTitle)
|
||||
):
|
||||
self._wineAccessiblePendingFocus = (signature, parts, pending[2])
|
||||
return True
|
||||
|
||||
self._wineAccessibleLastSignature = signature
|
||||
self._wineAccessibleLastTime = now
|
||||
return self.PresentMessage(message)
|
||||
|
||||
def _present_pending_accessible_focus(self) -> bool:
|
||||
"""Presents the final focus event after Wine's transient focus burst."""
|
||||
|
||||
pending = getattr(self, "_wineAccessiblePendingFocus", None)
|
||||
self._wineAccessiblePendingFocus = None
|
||||
self._wineAccessibleFocusTimeoutId = 0
|
||||
if pending is None:
|
||||
return False
|
||||
|
||||
signature, parts, windowTitle = pending
|
||||
self._wineAccessibleLastSignature = signature
|
||||
self._wineAccessibleLastTime = time.monotonic()
|
||||
parts = list(parts)
|
||||
if windowTitle:
|
||||
previousTitle = getattr(self, "_wineAccessibleWindowTitle", None)
|
||||
self._wineAccessibleWindowTitle = windowTitle
|
||||
if windowTitle != previousTitle and windowTitle not in parts:
|
||||
parts.append(windowTitle)
|
||||
if parts:
|
||||
self.PresentMessage(", ".join(parts))
|
||||
return False
|
||||
|
||||
def GetVersion(self) -> str: # pylint: disable=invalid-name
|
||||
"""Returns Cthulhu's version and revision if available."""
|
||||
|
||||
Reference in new issue
Block a user