refactor: extract PrivacyController from main_window.py
Move _toggle_privacy and its lazy state (_privacy_on, _privacy_overlay, _popout_was_visible) into gui/privacy.py. Rewire menu action, popout signal, resizeEvent, and keyPressEvent to use the controller. No behavior change. main_window.py: 3111 -> 3068 lines.
This commit is contained in:
parent
321ba8edfa
commit
cb2445a90a
@ -59,6 +59,7 @@ from .log_handler import LogHandler
|
|||||||
from .async_signals import AsyncSignals
|
from .async_signals import AsyncSignals
|
||||||
from .info_panel import InfoPanel
|
from .info_panel import InfoPanel
|
||||||
from .window_state import WindowStateController
|
from .window_state import WindowStateController
|
||||||
|
from .privacy import PrivacyController
|
||||||
|
|
||||||
log = logging.getLogger("booru")
|
log = logging.getLogger("booru")
|
||||||
|
|
||||||
@ -130,6 +131,7 @@ class BooruApp(QMainWindow):
|
|||||||
# (and from the splitter timer's flush on close). Uses the same
|
# (and from the splitter timer's flush on close). Uses the same
|
||||||
# 300ms debounce pattern as the splitter saver.
|
# 300ms debounce pattern as the splitter saver.
|
||||||
self._window_state = WindowStateController(self)
|
self._window_state = WindowStateController(self)
|
||||||
|
self._privacy = PrivacyController(self)
|
||||||
self._main_window_save_timer = QTimer(self)
|
self._main_window_save_timer = QTimer(self)
|
||||||
self._main_window_save_timer.setSingleShot(True)
|
self._main_window_save_timer.setSingleShot(True)
|
||||||
self._main_window_save_timer.setInterval(300)
|
self._main_window_save_timer.setInterval(300)
|
||||||
@ -556,7 +558,7 @@ class BooruApp(QMainWindow):
|
|||||||
|
|
||||||
privacy_action = QAction("&Privacy Screen", self)
|
privacy_action = QAction("&Privacy Screen", self)
|
||||||
privacy_action.setShortcut(QKeySequence("Ctrl+P"))
|
privacy_action.setShortcut(QKeySequence("Ctrl+P"))
|
||||||
privacy_action.triggered.connect(self._toggle_privacy)
|
privacy_action.triggered.connect(self._privacy.toggle)
|
||||||
view_menu.addAction(privacy_action)
|
view_menu.addAction(privacy_action)
|
||||||
|
|
||||||
def _load_sites(self) -> None:
|
def _load_sites(self) -> None:
|
||||||
@ -2053,7 +2055,7 @@ class BooruApp(QMainWindow):
|
|||||||
self._fullscreen_window.open_in_default.connect(self._open_preview_in_default)
|
self._fullscreen_window.open_in_default.connect(self._open_preview_in_default)
|
||||||
self._fullscreen_window.open_in_browser.connect(self._open_preview_in_browser)
|
self._fullscreen_window.open_in_browser.connect(self._open_preview_in_browser)
|
||||||
self._fullscreen_window.closed.connect(self._on_fullscreen_closed)
|
self._fullscreen_window.closed.connect(self._on_fullscreen_closed)
|
||||||
self._fullscreen_window.privacy_requested.connect(self._toggle_privacy)
|
self._fullscreen_window.privacy_requested.connect(self._privacy.toggle)
|
||||||
# Set post tags for BL Tag menu
|
# Set post tags for BL Tag menu
|
||||||
post = self._preview._current_post
|
post = self._preview._current_post
|
||||||
if post:
|
if post:
|
||||||
@ -2847,54 +2849,9 @@ class BooruApp(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
self.showFullScreen()
|
self.showFullScreen()
|
||||||
|
|
||||||
# -- Privacy screen --
|
|
||||||
|
|
||||||
def _toggle_privacy(self) -> None:
|
|
||||||
if not hasattr(self, '_privacy_on'):
|
|
||||||
self._privacy_on = False
|
|
||||||
self._privacy_overlay = QWidget(self)
|
|
||||||
self._privacy_overlay.setStyleSheet("background: black;")
|
|
||||||
self._privacy_overlay.hide()
|
|
||||||
# Tracks whether the popout was visible at privacy-on time
|
|
||||||
# so privacy-off only restores it if it was actually up
|
|
||||||
# before. Without the gate, privacy-off would re-show a
|
|
||||||
# popout that the user closed before triggering privacy.
|
|
||||||
self._popout_was_visible = False
|
|
||||||
|
|
||||||
self._privacy_on = not self._privacy_on
|
|
||||||
if self._privacy_on:
|
|
||||||
self._privacy_overlay.setGeometry(self.rect())
|
|
||||||
self._privacy_overlay.raise_()
|
|
||||||
self._privacy_overlay.show()
|
|
||||||
self.setWindowTitle("booru-viewer")
|
|
||||||
# Pause preview video
|
|
||||||
if self._preview._stack.currentIndex() == 1:
|
|
||||||
self._preview._video_player.pause()
|
|
||||||
# Delegate popout hide-and-pause to FullscreenPreview so it
|
|
||||||
# can capture its own geometry for restore.
|
|
||||||
self._popout_was_visible = bool(
|
|
||||||
self._fullscreen_window and self._fullscreen_window.isVisible()
|
|
||||||
)
|
|
||||||
if self._popout_was_visible:
|
|
||||||
self._fullscreen_window.privacy_hide()
|
|
||||||
else:
|
|
||||||
self._privacy_overlay.hide()
|
|
||||||
# Resume embedded preview video — unconditional resume, the
|
|
||||||
# common case (privacy hides → user comes back → video should
|
|
||||||
# be playing again) wins over the manually-paused edge case.
|
|
||||||
if self._preview._stack.currentIndex() == 1:
|
|
||||||
self._preview._video_player.resume()
|
|
||||||
# Restore the popout via its own privacy_show method, which
|
|
||||||
# also re-dispatches the captured geometry to Hyprland (Qt
|
|
||||||
# show() alone doesn't preserve position on Wayland) and
|
|
||||||
# resumes its video.
|
|
||||||
if self._popout_was_visible and self._fullscreen_window:
|
|
||||||
self._fullscreen_window.privacy_show()
|
|
||||||
|
|
||||||
def resizeEvent(self, event) -> None:
|
def resizeEvent(self, event) -> None:
|
||||||
super().resizeEvent(event)
|
super().resizeEvent(event)
|
||||||
if hasattr(self, '_privacy_overlay') and self._privacy_on:
|
self._privacy.resize_overlay()
|
||||||
self._privacy_overlay.setGeometry(self.rect())
|
|
||||||
# Capture window state proactively so the saved value is always
|
# Capture window state proactively so the saved value is always
|
||||||
# fresh — closeEvent's hyprctl query can fail if the compositor has
|
# fresh — closeEvent's hyprctl query can fail if the compositor has
|
||||||
# already started unmapping. Debounced via the 300ms timer.
|
# already started unmapping. Debounced via the 300ms timer.
|
||||||
@ -2915,10 +2872,10 @@ class BooruApp(QMainWindow):
|
|||||||
key = event.key()
|
key = event.key()
|
||||||
# Privacy screen always works
|
# Privacy screen always works
|
||||||
if key == Qt.Key.Key_P and event.modifiers() == Qt.KeyboardModifier.ControlModifier:
|
if key == Qt.Key.Key_P and event.modifiers() == Qt.KeyboardModifier.ControlModifier:
|
||||||
self._toggle_privacy()
|
self._privacy.toggle()
|
||||||
return
|
return
|
||||||
# If privacy is on, only allow toggling it off
|
# If privacy is on, only allow toggling it off
|
||||||
if hasattr(self, '_privacy_on') and self._privacy_on:
|
if self._privacy.is_active:
|
||||||
return
|
return
|
||||||
if key == Qt.Key.Key_F and self._posts:
|
if key == Qt.Key.Key_F and self._posts:
|
||||||
idx = self._grid.selected_index
|
idx = self._grid.selected_index
|
||||||
|
|||||||
66
booru_viewer/gui/privacy.py
Normal file
66
booru_viewer/gui/privacy.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
"""Privacy-screen overlay for the main window."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from PySide6.QtWidgets import QWidget
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .main_window import BooruApp
|
||||||
|
|
||||||
|
|
||||||
|
class PrivacyController:
|
||||||
|
"""Owns the privacy overlay toggle and popout coordination."""
|
||||||
|
|
||||||
|
def __init__(self, app: BooruApp) -> None:
|
||||||
|
self._app = app
|
||||||
|
self._on = False
|
||||||
|
self._overlay: QWidget | None = None
|
||||||
|
self._popout_was_visible = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_active(self) -> bool:
|
||||||
|
return self._on
|
||||||
|
|
||||||
|
def resize_overlay(self) -> None:
|
||||||
|
"""Re-fit the overlay to the main window's current rect."""
|
||||||
|
if self._overlay is not None and self._on:
|
||||||
|
self._overlay.setGeometry(self._app.rect())
|
||||||
|
|
||||||
|
def toggle(self) -> None:
|
||||||
|
if self._overlay is None:
|
||||||
|
self._overlay = QWidget(self._app)
|
||||||
|
self._overlay.setStyleSheet("background: black;")
|
||||||
|
self._overlay.hide()
|
||||||
|
|
||||||
|
self._on = not self._on
|
||||||
|
if self._on:
|
||||||
|
self._overlay.setGeometry(self._app.rect())
|
||||||
|
self._overlay.raise_()
|
||||||
|
self._overlay.show()
|
||||||
|
self._app.setWindowTitle("booru-viewer")
|
||||||
|
# Pause preview video
|
||||||
|
if self._app._preview._stack.currentIndex() == 1:
|
||||||
|
self._app._preview._video_player.pause()
|
||||||
|
# Delegate popout hide-and-pause to FullscreenPreview so it
|
||||||
|
# can capture its own geometry for restore.
|
||||||
|
self._popout_was_visible = bool(
|
||||||
|
self._app._fullscreen_window
|
||||||
|
and self._app._fullscreen_window.isVisible()
|
||||||
|
)
|
||||||
|
if self._popout_was_visible:
|
||||||
|
self._app._fullscreen_window.privacy_hide()
|
||||||
|
else:
|
||||||
|
self._overlay.hide()
|
||||||
|
# Resume embedded preview video — unconditional resume, the
|
||||||
|
# common case (privacy hides -> user comes back -> video should
|
||||||
|
# be playing again) wins over the manually-paused edge case.
|
||||||
|
if self._app._preview._stack.currentIndex() == 1:
|
||||||
|
self._app._preview._video_player.resume()
|
||||||
|
# Restore the popout via its own privacy_show method, which
|
||||||
|
# also re-dispatches the captured geometry to Hyprland (Qt
|
||||||
|
# show() alone doesn't preserve position on Wayland) and
|
||||||
|
# resumes its video.
|
||||||
|
if self._popout_was_visible and self._app._fullscreen_window:
|
||||||
|
self._app._fullscreen_window.privacy_show()
|
||||||
Loading…
x
Reference in New Issue
Block a user