From 74f139b52cc410e3f79ea6b0858d62b7d4e5afb8 Mon Sep 17 00:00:00 2001 From: Loren Eteval Date: Wed, 12 Aug 2026 12:12:51 +0800 Subject: [PATCH] Refine Fluent tray menu styling Signed-off-by: Loren Eteval --- Furious/Application/TrayIcon.py | 1 + Furious/Qt/AppStyleSheet.py | 55 +++++++++++++++++++++++++++------ Furious/Qt/QtWidgets.py | 24 ++++++++++++++ 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/Furious/Application/TrayIcon.py b/Furious/Application/TrayIcon.py index 84b1b91..da279ea 100644 --- a/Furious/Application/TrayIcon.py +++ b/Furious/Application/TrayIcon.py @@ -107,6 +107,7 @@ class TrayIcon( self.setContextMenu(self._menu) + self._menu.setMenuRole('tray', recursive=True) self._menu.aboutToShow.connect(self.rebuildDynamicMenus) self.setDisconnectedIcon() diff --git a/Furious/Qt/AppStyleSheet.py b/Furious/Qt/AppStyleSheet.py index 6d8f44c..1af14ce 100644 --- a/Furious/Qt/AppStyleSheet.py +++ b/Furious/Qt/AppStyleSheet.py @@ -487,26 +487,31 @@ class AppStyleSheet: QMenu {{ padding: 6px; border: 1px solid {palette['border_strong']}; - border-radius: 8px; + border-radius: 9px; background-color: {palette['overlay']}; }} QMenu::separator {{ height: 1px; - margin: 5px 8px; - background-color: {palette['border']}; + margin: 6px 9px; + background-color: {palette['border_strong']}; }} QMenu::item {{ - min-width: 156px; - min-height: 20px; - padding: 7px 30px 7px 28px; - border-radius: 5px; + min-width: 160px; + min-height: 22px; + padding: 7px 32px; + border-radius: 6px; background-color: transparent; }} QMenu::item:selected {{ - background-color: {palette['accent_soft']}; + background-color: {palette['accent_soft_hover']}; + color: {palette['text_strong']}; + }} + + QMenu::item:pressed {{ + background-color: {palette['pressed']}; color: {palette['text_strong']}; }} @@ -515,16 +520,20 @@ class AppStyleSheet: color: {palette['disabled']}; }} + QMenu::item:disabled:selected {{ + background-color: transparent; + }} + QMenu::icon {{ position: relative; - left: 7px; + left: 8px; }} QMenu::indicator {{ width: 14px; height: 14px; position: relative; - left: 7px; + left: 8px; }} QMenu::indicator:checked {{ @@ -536,9 +545,35 @@ class AppStyleSheet: QMenu::right-arrow {{ width: 9px; height: 9px; + position: relative; + right: 8px; image: url("{caretRightIcon}"); }} + QMenu[menuRole="tray"] {{ + padding: 7px; + border-radius: 10px; + }} + + QMenu[menuRole="tray"]::item {{ + min-width: 176px; + min-height: 24px; + padding: 8px 34px; + }} + + QMenu[menuRole="tray"]::separator {{ + margin: 6px 10px; + }} + + QMenu[menuRole="tray"]::icon, + QMenu[menuRole="tray"]::indicator {{ + left: 9px; + }} + + QMenu[menuRole="tray"]::right-arrow {{ + right: 9px; + }} + QToolBar {{ padding: 4px 6px; border: none; diff --git a/Furious/Qt/QtWidgets.py b/Furious/Qt/QtWidgets.py index 0252757..d55e645 100644 --- a/Furious/Qt/QtWidgets.py +++ b/Furious/Qt/QtWidgets.py @@ -534,6 +534,8 @@ class AppQMenu(Mixins.QTranslatable, QMenu): def __init__(self, *actions, **kwargs): """Initialize the AppQMenu.""" + menuRole = kwargs.pop('menuRole', '') + super().__init__(**kwargs) # In some old version PySide6, the self.actions() method @@ -552,6 +554,28 @@ class AppQMenu(Mixins.QTranslatable, QMenu): # Do nothing pass + if menuRole: + self.setMenuRole(menuRole) + + def setMenuRole(self, role: str, recursive=False): + """Apply a reusable visual role to this menu and its submenus.""" + self.setProperty('menuRole', str(role)) + + if recursive: + for action in self.actions(): + submenu = action.menu() if hasattr(action, 'menu') else None + + if isinstance(submenu, AppQMenu): + submenu.setMenuRole(role, recursive=True) + + # Dynamic properties participate in QSS selector matching only after + # the widget is repolished when an application stylesheet is active. + style = self.style() + style.unpolish(self) + style.polish(self) + + self.update() + def retranslate(self): """Refresh translated text for the app q menu.""" self.setTitle(_(self.title()))