Modernize log page action menus

Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
Loren Eteval
2026-08-12 12:24:04 +08:00
parent 27fc47b9c4
commit 20d34a14fd
4 changed files with 79 additions and 16 deletions
+34
View File
@@ -51,6 +51,7 @@ __all__ = [
'AppQMenuBar',
'AppQMessageBox',
'AppQIconTextPushButton',
'AppQMenuPushButton',
'AppQPushButton',
'AppQSpinBox',
'AppQTableView',
@@ -832,6 +833,39 @@ class AppQPushButton(Mixins.QTranslatable, Mixins.ThemeAware, QPushButton):
self.setText(_(self.text()))
class AppQMenuPushButton(AppQPushButton):
"""Open a popup menu from a regular Fluent-style push button."""
def __init__(self, *args, popupMenu=None, **kwargs):
"""Initialize a button without Qt's native menu indicator."""
super().__init__(*args, **kwargs)
self._popupMenu = None
self.setPopupMenu(popupMenu)
self.clicked.connect(self.showPopupMenu)
def popupMenu(self):
"""Return the menu presented by this button."""
return self._popupMenu
def setPopupMenu(self, menu):
"""Set the menu presented below this button."""
if menu is not None and not isinstance(menu, QMenu):
raise TypeError('popupMenu must be a QMenu or None')
self._popupMenu = menu
@QtCore.Slot()
def showPopupMenu(self):
"""Open the configured menu immediately below the button."""
if self._popupMenu is None:
return
position = self.mapToGlobal(QtCore.QPoint(0, self.height() + 2))
self._popupMenu.popup(position)
class AppQIconTextPushButton(
Mixins.QTranslatable,
Mixins.ThemeAware,
+2
View File
@@ -65,6 +65,7 @@ from .QtWidgets import (
AppQMainWindow,
AppQMenu,
AppQMenuBar,
AppQMenuPushButton,
AppQMessageBox,
AppQPushButton,
AppQSpinBox,
@@ -116,6 +117,7 @@ __all__ = [
'AppQMainWindow',
'AppQMenu',
'AppQMenuBar',
'AppQMenuPushButton',
'AppQMessageBox',
'AppQNetworkAccessManager',
'AppQPlainTextEdit',
+2 -13
View File
@@ -477,13 +477,11 @@ class HomePage(Mixins.QTranslatable, QMainWindow):
)
self.serverMenu = AppQMenu(*serverActions, parent=self)
self.serverButton = AppQPushButton(
self.serverButton = AppQMenuPushButton(
_('Server'),
icon=bootstrapIcon('server.svg'),
popupMenu=self.serverMenu,
)
# Keep the protocol/profile creation menu without presenting this as a
# split or drop-down button. The regular button opens the menu itself.
self.serverButton.clicked.connect(self.showServerMenu)
self.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.NoContextMenu)
@@ -588,15 +586,6 @@ class HomePage(Mixins.QTranslatable, QMainWindow):
self.setCentralWidget(self._widget)
@QtCore.Slot()
def showServerMenu(self):
"""Open server creation actions below the regular Server button."""
position = self.serverButton.mapToGlobal(
QtCore.QPoint(0, self.serverButton.height() + 2)
)
self.serverMenu.popup(position)
@QtCore.Slot()
def handleServerSelectionChanged(self, *_args):
"""Apply Home's selection policy to the shared connection control."""
+41 -3
View File
@@ -217,9 +217,34 @@ class LogPage(Mixins.QTranslatable, QMainWindow):
parent=self,
)
self.menuBar().addMenu(self._fileMenu)
self.menuBar().addMenu(self._editMenu)
self.menuBar().addMenu(self._viewMenu)
self.fileButton = AppQMenuPushButton(
_('File'),
icon=bootstrapIcon('file-earmark.svg'),
popupMenu=self._fileMenu,
)
self.editButton = AppQMenuPushButton(
_('Edit'),
icon=bootstrapIcon('pencil-square.svg'),
popupMenu=self._editMenu,
)
self.viewButton = AppQMenuPushButton(
_('View'),
icon=bootstrapIcon('eye.svg'),
popupMenu=self._viewMenu,
)
actionLayout = QHBoxLayout()
actionLayout.setContentsMargins(0, 0, 0, 0)
actionLayout.setSpacing(8)
actionLayout.addWidget(self.fileButton)
actionLayout.addWidget(self.editButton)
actionLayout.addWidget(self.viewButton)
actionLayout.addStretch(1)
centralLayout.insertLayout(1, actionLayout)
for menu in (self._fileMenu, self._editMenu, self._viewMenu):
self._registerMenuShortcuts(menu)
self._populateFilters(self._preferredFilter)
self._refreshEntries()
@@ -231,6 +256,19 @@ class LogPage(Mixins.QTranslatable, QMainWindow):
self.retranslate()
def _registerMenuShortcuts(self, menu):
"""Associate popup actions with the page so shortcuts stay active."""
for action in menu.actions():
if action.isSeparator():
continue
self.addAction(action)
submenu = action.menu() if hasattr(action, 'menu') else None
if submenu is not None:
self._registerMenuShortcuts(submenu)
def _categoryText(self, category) -> str:
"""Return a category's translated or literal display label."""
if category.id == APPLICATION_LOG_CATEGORY: