Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions src/axidev_osk/windows/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

from PySide6.QtCore import QSize
from PySide6.QtGui import QCloseEvent, QShowEvent
from PySide6.QtWidgets import QMainWindow, QVBoxLayout, QWidget
from PySide6.QtWidgets import QGraphicsOpacityEffect, QMainWindow, QVBoxLayout, QWidget

from ..config.models import WindowConfig
from ..platform.overlay import OverlayBackend
from ..runtime.context import Context
from ..runtime.events import WindowCloseRequested
from .chrome import install_overlay_chrome
Expand Down Expand Up @@ -42,10 +43,10 @@ def __init__(self, config: WindowConfig, context: Context, parent: QWidget | Non
self._config = config
self._context = context
self._quit_controller_managed = False
self._layer_shell_opacity_effect: QGraphicsOpacityEffect | None = None
self.setProperty("componentType", "window")
self.setProperty("componentId", config.id)
self.setWindowTitle(config.title)
self.setWindowOpacity(config.opacity)
if config.overlay.always_on_top:
self._overlay = configure_always_on_top_window(self, config=config.overlay.config)
else:
Expand All @@ -63,8 +64,21 @@ def __init__(self, config: WindowConfig, context: Context, parent: QWidget | Non
on_resize=self._overlay.resize_by,
)
self.setCentralWidget(central)
if self._overlay.backend == OverlayBackend.WAYLAND_LAYER_SHELL:
self._layer_shell_opacity_effect = QGraphicsOpacityEffect(central)
central.setGraphicsEffect(self._layer_shell_opacity_effect)
self.setWindowOpacity(config.opacity)
self.apply_startup_size(minimum_size=config.surface.minimum_size)

def setWindowOpacity(self, opacity: float) -> None: # noqa: N802
"""Apply opacity through rendered pixels when layer-shell cannot do it."""

if self._layer_shell_opacity_effect is None:
super().setWindowOpacity(opacity)
return
super().setWindowOpacity(1.0)
self._layer_shell_opacity_effect.setOpacity(opacity)

@property
def window_id(self) -> str:
"""Return this window's deterministic runtime ID."""
Expand Down
34 changes: 31 additions & 3 deletions tests/test_window_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@
from unittest.mock import patch

from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QLabel, QPushButton
from PySide6.QtWidgets import QApplication, QGraphicsOpacityEffect, QLabel, QPushButton

from axidev_osk.components import register_components
from axidev_osk.components.grid.keyboard import KeyboardWidget
from axidev_osk.config.defaults import build_default_app_config
from axidev_osk.platform.overlay import OverlayBackend
from axidev_osk.runtime.registries import ComponentRegistry, SurfaceRegistry
from axidev_osk.runtime.testing import make_test_context
from axidev_osk.windows.builder import build_window
from axidev_osk.windows.chrome import OverlayResizeHandle, OverlayTitleBar
from axidev_osk.windows.surface import register_surfaces
from axidev_osk.windows.overlay.always_on_top import OverlayPlacement
from axidev_osk.windows.surface import register_surfaces


class FakeKeyboardBackend:
Expand Down Expand Up @@ -49,7 +50,13 @@ def sync_latched_key(self, spec, latched: bool, press_handle=None):


class FakeOverlayController:
def __init__(self, *, uses_custom_chrome: bool = True) -> None:
def __init__(
self,
*,
backend: OverlayBackend = OverlayBackend.WINDOWS_NATIVE,
uses_custom_chrome: bool = True,
) -> None:
self.backend = backend
self.uses_custom_chrome = uses_custom_chrome

def prepare_show(self) -> bool:
Expand Down Expand Up @@ -207,6 +214,27 @@ def test_keyboard_window_uses_configured_normal_opacity(self) -> None:
self.addCleanup(window.close)
self.assertAlmostEqual(window.windowOpacity(), 0.85, delta=0.005)

def test_layer_shell_window_renders_configured_opacity_in_content(self) -> None:
_app()
overlay = FakeOverlayController(backend=OverlayBackend.WAYLAND_LAYER_SHELL)

with patch(
"axidev_osk.windows.builder.configure_always_on_top_window",
return_value=overlay,
):
window = _build_keyboard_window(FakeKeyboardBackend(ready=True))

self.addCleanup(window.close)
effect = window.centralWidget().graphicsEffect()
self.assertIsInstance(effect, QGraphicsOpacityEffect)
self.assertAlmostEqual(window.windowOpacity(), 1.0, delta=0.005)
self.assertAlmostEqual(effect.opacity(), 0.85, delta=0.005)

window.setWindowOpacity(0.01)

self.assertAlmostEqual(window.windowOpacity(), 1.0, delta=0.005)
self.assertAlmostEqual(effect.opacity(), 0.01, delta=0.005)

def test_runtime_window_and_components_expose_dynamic_identity_properties(self) -> None:
_app()
overlay = FakeOverlayController()
Expand Down