diff options
Diffstat (limited to 'sb_builder/packages.py')
| -rw-r--r-- | sb_builder/packages.py | 108 |
1 files changed, 46 insertions, 62 deletions
diff --git a/sb_builder/packages.py b/sb_builder/packages.py index 64032a2..30215e0 100644 --- a/sb_builder/packages.py +++ b/sb_builder/packages.py @@ -20,13 +20,13 @@ import html from PyQt6.QtCore import QAbstractTableModel, QModelIndex, QSortFilterProxyModel, Qt, pyqtSignal from PyQt6.QtGui import QColor, QKeySequence, QShortcut -from PyQt6.QtWidgets import (QCheckBox, QDialog, QDialogButtonBox, QFormLayout, QHBoxLayout, - QLabel, QLineEdit, QMessageBox, QPushButton, QScrollArea, QSplitter, - QTableView, QVBoxLayout, QWidget) +from PyQt6.QtWidgets import (QHBoxLayout, QLabel, QLineEdit, QMessageBox, QPushButton, QScrollArea, + QSplitter, QTableView, QVBoxLayout, QWidget) from . import hints as hintlib from .dashboard import STATUS from .fmt import NA, fmt_time +from .ui import SP, action, enable OVERLAYS = ("personal", "pentesting") STATUSES = ("ok", "failed", "never", "outdated") @@ -230,9 +230,8 @@ class PackagesTab(QWidget): chips.addWidget(self.chips[name]) chips.addSpacing(16) chips.addStretch() - self.refresh_btn = QPushButton("Refresh") - self.refresh_btn.clicked.connect(self.reload) - chips.addWidget(self.refresh_btn) + self.count = QLabel() + chips.addWidget(self.count) self.table = QTableView(sortingEnabled=True) self.table.setModel(self.proxy) @@ -248,18 +247,14 @@ class PackagesTab(QWidget): alignment=Qt.AlignmentFlag.AlignTop) self.detail.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) self.detail.setContentsMargins(12, 8, 12, 8) - self.new_hint_btn = QPushButton("New hint") - self.new_hint_btn.clicked.connect(self.new_hint) - self.open_hint_btn = QPushButton("Open in Hints") - self.open_hint_btn.clicked.connect(lambda: self.hints_requested.emit(self.current()["name"])) - detail_bar = QHBoxLayout() - detail_bar.addWidget(self.new_hint_btn) - detail_bar.addWidget(self.open_hint_btn) - detail_bar.addStretch() + # navigation, not an action: a link in the pane (UI guidelines) + self.open_hint_btn = QLabel('<a href="#">Open in Hints</a>') + self.open_hint_btn.setContentsMargins(12, 0, 12, 8) + self.open_hint_btn.linkActivated.connect(lambda _l: self.hints_requested.emit(self.current()["name"])) pane = QWidget() pane_layout = QVBoxLayout(pane) pane_layout.addWidget(self.detail) - pane_layout.addLayout(detail_bar) + pane_layout.addWidget(self.open_hint_btn) pane_layout.addStretch() scroll = QScrollArea(widgetResizable=True) scroll.setWidget(pane) @@ -269,37 +264,38 @@ class PackagesTab(QWidget): split.addWidget(scroll) split.setSizes([700, 400]) - self.count = QLabel() - bar = QHBoxLayout() - bar.addWidget(self.count) - self.keys = [] - for label, slot, key in (("Select visible", self.select_visible, "Ctrl+A"), - ("Clear", self.clear, "Ctrl+Shift+A")): - b = QPushButton(label, toolTip=key) - b.clicked.connect(slot) - bar.addWidget(b) - # in the search field Ctrl+A still selects its text: QLineEdit overrides it - self.keys.append(QShortcut(QKeySequence(key), self, slot, - context=Qt.ShortcutContext.WidgetWithChildrenShortcut)) - bar.addStretch() - for action in ("build", "update", "rebuild", "remove"): - b = QPushButton(action.capitalize()) - b.clicked.connect(lambda _c, a=action: self.act(a)) - bar.addWidget(b) - for label, slot in (("Update all", self.update_all), ("Publish", self.publish)): - b = QPushButton(label) - b.clicked.connect(slot) - bar.addWidget(b) + self.act_build = { + a: action(self, a.capitalize(), icon, fb, lambda a=a: self.act(a)) + for a, icon, fb in (("build", "run-build", SP.SP_MediaPlay), + ("update", "system-software-update", SP.SP_BrowserReload), + ("rebuild", "view-refresh", SP.SP_BrowserReload), + ("remove", "edit-delete", SP.SP_TrashIcon))} + self.act_new_hint = action(self, "New hint", "document-new", SP.SP_FileIcon, self.new_hint) + self.actions = [*self.act_build.values(), + action(self, "Update all", "system-upgrade", SP.SP_ArrowUp, self.update_all), + self.act_new_hint, + action(self, "Publish", "document-send", SP.SP_DialogSaveButton, self.publish)] + self.view_actions = [ + action(self, "Select visible", "edit-select-all", SP.SP_DialogYesButton, + self.select_visible, shortcut="Ctrl+A"), + action(self, "Clear", "edit-clear", SP.SP_DialogResetButton, self.clear, + shortcut="Ctrl+Shift+A"), + action(self, "Refresh", "view-refresh", SP.SP_BrowserReload, self.reload)] + # in the search field Ctrl+A still selects its text: QLineEdit overrides it + self.keys = [QShortcut(QKeySequence(key), self, slot, + context=Qt.ShortcutContext.WidgetWithChildrenShortcut) + for key, slot in (("Ctrl+A", self.select_visible), ("Ctrl+Shift+A", self.clear))] + self.loaded = False layout = QVBoxLayout(self) layout.addWidget(self.query) layout.addLayout(chips) - layout.addWidget(split) - layout.addLayout(bar) + layout.addWidget(split, 1) self.model.dataChanged.connect(lambda *_: self.update_count()) self.update_count() def reload(self): + self.loaded = True self.count.setText("loading…") self.remote.call(self._loaded, "inventory") @@ -317,6 +313,15 @@ class PackagesTab(QWidget): def update_count(self): self.count.setText(f"{self.proxy.rowCount():,} shown · {len(self.model.checked)} selected") + for a in self.act_build.values(): + enable(a, None if self.model.checked else "tick packages first") + enable(self.act_new_hint, None if self.without_hint() else + "none of the ticked packages is without a hint") + + def without_hint(self): + """Ticked packages that have no hint, sorted.""" + return sorted(r["name"] for r in self.model.rows + if r["name"] in self.model.checked and not r["hint_path"]) def _toggle(self, index): """A click anywhere on a row ticks or unticks it.""" @@ -360,30 +365,9 @@ class PackagesTab(QWidget): def show_detail(self, current, _previous): row = current.data(Qt.ItemDataRole.UserRole) if current.isValid() else None self.detail.setText(detail_html(row) if row else "") - self.new_hint_btn.setVisible(bool(row) and not row["hint_path"]) self.open_hint_btn.setVisible(bool(row) and bool(row["hint_path"])) - def ask_new_hint(self, name): - """(version or "", no download) for a new hint, None on cancel.""" - d = QDialog(self, windowTitle=f"New hint: {name}") - version = QLineEdit(placeholderText="the .info version") - no_dl = QCheckBox('no download (DOWNLOAD="no")') - buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok - | QDialogButtonBox.StandardButton.Cancel) - buttons.accepted.connect(d.accept) - buttons.rejected.connect(d.reject) - form = QFormLayout(d) - form.addRow("version", version) - form.addRow(no_dl) - form.addRow(buttons) - if d.exec() != QDialog.DialogCode.Accepted: - return None - return version.text().strip(), no_dl.isChecked() - def new_hint(self): - row = self.current() - answer = row and self.ask_new_hint(row["name"]) - if answer: - version, no_dl = answer - self.start_requested.emit(["hint-new", row["name"], *([version] if version else []), - *(["--no-dl"] if no_dl else [])]) + names = self.without_hint() + if names: + self.start_requested.emit(["hint-new", *names]) |
