zapret-kvn/xray_fluent/ui/base_page.py
loop-uh a7cf61a05e
All checks were successful
Windows project source guards / test (push) Successful in 36s
Prepare v0.4.79: unified theming, responsive layout, real flags
- Single source of truth for theme: theme.py tokens, FluentDialog base
  for all dialogs, setCustomStyleSheet migration, theme applied before
  window creation, SystemThemeListener for auto mode
- Responsive layout: ScrollablePage base with as-needed horizontal
  scrolling, word wrap on dashboard labels, adaptive dashboard grid,
  realistic 860x560 window minimum
- Real country flags (flagcdn PNG set) with themed border and fallback
- Active server row marked by accent stripe only (no synthesized bold)
- Subscription polish from parallel work (QR utils, http, diagnostics)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 23:21:28 +03:00

91 lines
3.6 KiB
Python

"""Base scrollable page (AC7).
``ScrollablePage`` is the common base for content pages: a vertical
``SmoothScrollArea`` whose horizontal scrollbar policy is ``ScrollBarAsNeeded``
(content wider than the viewport scrolls instead of being clipped), fully
transparent surfaces so the Windows 11 Mica effect shows through, and a
``body`` container with the standard page paddings.
Subclasses add their content to ``self.body_layout`` (children parented to
``self.body``).
"""
from __future__ import annotations
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QVBoxLayout, QWidget
from qfluentwidgets import SmoothScrollArea
#: Standard content paddings (left, top, right, bottom) for pages.
BODY_MARGINS = (24, 20, 24, 20)
_TRANSPARENT_SCROLL_QSS = "QScrollArea { background: transparent; border: none; }"
_TRANSPARENT_BODY_QSS = "QWidget { background: transparent; }"
class PageScrollArea(SmoothScrollArea):
"""SmoothScrollArea that reports the *logical* scrollbar policies.
qfluentwidgets' SmoothScrollDelegate replaces the native scrollbars with
floating ones: it monkey-patches the policy setters on the instance, always
forces the native policy to AlwaysOff and expresses the requested policy
via ``setForceHidden`` on the floating bars. That makes the stock
``horizontalScrollBarPolicy()`` getter lie about the effective behaviour.
This subclass records the requested policies and returns them from the
getters so the effective policy is inspectable (AC7).
"""
def __init__(self, parent: QWidget | None = None):
super().__init__(parent)
self._logical_h_policy = Qt.ScrollBarPolicy.ScrollBarAsNeeded
self._logical_v_policy = Qt.ScrollBarPolicy.ScrollBarAsNeeded
# The delegate installed bound methods as *instance* attributes, which
# shadow any subclass override — wrap them instead.
delegate_set_h = self.setHorizontalScrollBarPolicy
delegate_set_v = self.setVerticalScrollBarPolicy
def _set_h(policy: Qt.ScrollBarPolicy) -> None:
self._logical_h_policy = policy
delegate_set_h(policy)
def _set_v(policy: Qt.ScrollBarPolicy) -> None:
self._logical_v_policy = policy
delegate_set_v(policy)
self.setHorizontalScrollBarPolicy = _set_h
self.setVerticalScrollBarPolicy = _set_v
def horizontalScrollBarPolicy(self) -> Qt.ScrollBarPolicy:
return self._logical_h_policy
def verticalScrollBarPolicy(self) -> Qt.ScrollBarPolicy:
return self._logical_v_policy
class ScrollablePage(QWidget):
"""Vertically scrollable, Mica-transparent page with a padded body."""
def __init__(self, parent: QWidget | None = None):
super().__init__(parent)
outer = QVBoxLayout(self)
outer.setContentsMargins(0, 0, 0, 0)
outer.setSpacing(0)
self.scroll_area = PageScrollArea(self)
self.scroll_area.setWidgetResizable(True)
# ScrollBarAsNeeded (NOT AlwaysOff): overly wide content must remain
# reachable instead of being cut off at the viewport edge.
self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
self.scroll_area.setStyleSheet(_TRANSPARENT_SCROLL_QSS)
outer.addWidget(self.scroll_area)
self.body = QWidget()
self.body.setStyleSheet(_TRANSPARENT_BODY_QSS)
self.scroll_area.setWidget(self.body)
self.body_layout = QVBoxLayout(self.body)
self.body_layout.setContentsMargins(*BODY_MARGINS)
self.body_layout.setSpacing(12)