diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-19 21:13:51 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-19 21:13:51 +0200 |
| commit | 57df6adb767e1691599f894e1e3925870b1b3b3f (patch) | |
| tree | 98f127218440692ffacd40e8727b363f45845723 | |
| parent | 81139106f75bc06f45e9e579f9c6400d0eeb8940 (diff) | |
| download | sb-builder-57df6adb767e1691599f894e1e3925870b1b3b3f.tar.gz sb-builder-57df6adb767e1691599f894e1e3925870b1b3b3f.zip | |
Ribbon for the screens; centered, distinct tab bar
The Open menu becomes a toolbar ribbon (icon over label: Dashboard,
Packages, Jobs) using the desktop icon theme with Qt fallbacks. Tabs
are centered with a bordered, rounded shape, the selected one filled
with the highlight color and joined to a highlight line over the pane,
so the bar reads as its own control. Colors come from the palette.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| -rw-r--r-- | sb_builder/mainwindow.py | 34 | ||||
| -rw-r--r-- | tests/test_gui.py | 10 |
2 files changed, 38 insertions, 6 deletions
diff --git a/sb_builder/mainwindow.py b/sb_builder/mainwindow.py index 70eb121..bd2245d 100644 --- a/sb_builder/mainwindow.py +++ b/sb_builder/mainwindow.py @@ -18,10 +18,11 @@ import time -from PyQt6.QtCore import QMetaType, QObject, QTimer, pyqtSignal, pyqtSlot +from PyQt6.QtCore import QMetaType, QObject, QSize, Qt, QTimer, pyqtSignal, pyqtSlot from PyQt6.QtDBus import QDBusArgument, QDBusConnection, QDBusInterface, QDBusMessage -from PyQt6.QtGui import QKeySequence, QShortcut -from PyQt6.QtWidgets import QLabel, QMainWindow, QMessageBox, QPushButton, QTabBar, QTabWidget +from PyQt6.QtGui import QIcon, QKeySequence, QShortcut +from PyQt6.QtWidgets import (QLabel, QMainWindow, QMessageBox, QPushButton, QStyle, QTabBar, + QTabWidget, QToolBar) from . import VERSION, config from .dashboard import Dashboard @@ -32,6 +33,20 @@ from .remote import DOWN, MISSING NOTIFY = "org.freedesktop.Notifications" NOTIFY_PATH = "/org/freedesktop/Notifications" +# ribbon entries: label, freedesktop icon name, Qt fallback icon +RIBBON = (("Dashboard", "go-home", QStyle.StandardPixmap.SP_DirHomeIcon), + ("Packages", "package-x-generic", QStyle.StandardPixmap.SP_FileDialogDetailedView), + ("Jobs", "system-run", QStyle.StandardPixmap.SP_MediaPlay)) +# tabs as a distinct, centered row; colors from the palette so the theme applies +TAB_STYLE = """ +QTabWidget::tab-bar { alignment: center; } +QTabWidget::pane { border-top: 2px solid palette(highlight); top: -2px; } +QTabBar::tab { padding: 6px 20px; margin: 0 3px; border: 1px solid palette(mid); + border-bottom: none; border-top-left-radius: 8px; border-top-right-radius: 8px; + background: palette(button); color: palette(button-text); } +QTabBar::tab:selected { background: palette(highlight); color: palette(highlighted-text); } +QTabBar::tab:hover:!selected { background: palette(midlight); } +""" END_EVENT = {"ok": "finished", "failed": "failed", "killed": "killed", "lost": "lost"} MAX_RETRY_MS = 30000 @@ -78,6 +93,7 @@ class MainWindow(QMainWindow): self.setWindowTitle("sb-builder") self.tabs = QTabWidget(tabsClosable=True) + self.tabs.setStyleSheet(TAB_STYLE) self.tabs.tabCloseRequested.connect(self._close_tab) self.tabs.currentChanged.connect(lambda _i: self.poll_stats()) self.dashboard = Dashboard() @@ -94,9 +110,15 @@ class MainWindow(QMainWindow): # window-wide: QTabWidget's own Ctrl+PgUp/PgDn needs focus on the tabs self.tab_next = QShortcut(QKeySequence("Ctrl+PgDown"), self, lambda: self._cycle_tab(1)) self.tab_prev = QShortcut(QKeySequence("Ctrl+PgUp"), self, lambda: self._cycle_tab(-1)) - open_menu = self.menuBar().addMenu("&Open") - open_menu.addAction("&Packages", self.open_packages) - open_menu.addAction("&Jobs", self.open_jobs) + self.ribbon = QToolBar("Screens", movable=False, floatable=False) + self.ribbon.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextUnderIcon) + self.ribbon.setIconSize(QSize(32, 32)) + self.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.ribbon) + slots = {"Dashboard": lambda: self.tabs.setCurrentWidget(self.dashboard), + "Packages": self.open_packages, "Jobs": self.open_jobs} + for label, name, fallback in RIBBON: + icon = QIcon.fromTheme(name, self.style().standardIcon(fallback)) + self.ribbon.addAction(icon, label, slots[label]) self.conn = QLabel("● connecting") self.key = QLabel() diff --git a/tests/test_gui.py b/tests/test_gui.py index bf9177a..b99aebf 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -383,6 +383,16 @@ class SmokeTest(unittest.TestCase): w.dashboard.jobs.clicked.emit() self.assertIs(w.tabs.currentWidget(), w.jobs_tab) + def test_ribbon_opens_tabs(self): + w, _ = self.window() + w.poll_jobs() + self.assertEqual([a.text() for a in w.ribbon.actions()], ["Dashboard", "Packages", "Jobs"]) + self.assertEqual([m.text() for m in w.menuBar().actions()], ["&File"]) + w.ribbon.actions()[1].trigger() + self.assertIs(w.tabs.currentWidget(), w.packages_tab) + w.ribbon.actions()[0].trigger() + self.assertIs(w.tabs.currentWidget(), w.dashboard) + def test_tab_shortcuts_wrap(self): w, _ = self.window() w.poll_jobs() |
