From 671d7130e1750ca19d233b4942f66c9b76473cbd Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Mon, 8 Jan 2024 21:08:33 +0100 Subject: [PATCH] Add search bar to preferences --- novelwriter/dialogs/preferences.py | 70 +++++++++++++++++++++++--- novelwriter/extensions/configlayout.py | 39 +++++++++++--- 2 files changed, 96 insertions(+), 13 deletions(-) diff --git a/novelwriter/dialogs/preferences.py b/novelwriter/dialogs/preferences.py index 5e532ffa..6207a468 100644 --- a/novelwriter/dialogs/preferences.py +++ b/novelwriter/dialogs/preferences.py @@ -26,12 +26,12 @@ from __future__ import annotations import logging -from PyQt5.QtGui import QCloseEvent, QFont +from PyQt5.QtGui import QCloseEvent, QColor, QFont, QKeyEvent, QKeySequence, QPalette from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot from PyQt5.QtWidgets import ( - QAbstractButton, QDialog, QHBoxLayout, QVBoxLayout, QWidget, QComboBox, - QSpinBox, QPushButton, QDialogButtonBox, QLineEdit, QFileDialog, - QFontDialog, QDoubleSpinBox, qApp + QAbstractButton, QComboBox, QCompleter, QDialog, QDialogButtonBox, + QDoubleSpinBox, QFileDialog, QFontDialog, QHBoxLayout, QLabel, QLineEdit, + QPushButton, QSpinBox, QVBoxLayout, QWidget, qApp ) from novelwriter import CONFIG, SHARED @@ -58,37 +58,80 @@ class GuiPreferences(QDialog): self.setMinimumSize(CONFIG.pxInt(600), CONFIG.pxInt(500)) self.resize(*CONFIG.preferencesWinSize) + # Title + font = self.font() + font.setPointSizeF(1.5*SHARED.theme.fontPointSize) + + palette = self.palette() + palette.setColor(QPalette.ColorRole.WindowText, QColor(*SHARED.theme.helpText)) + + self.titleLabel = QLabel(self.tr("Preferences"), self) + self.titleLabel.setFont(font) + self.titleLabel.setPalette(palette) + self.titleLabel.setIndent(CONFIG.pxInt(4)) + # SideBar self.sidebar = NPagedSideBar(self) self.sidebar.setLabelColor(SHARED.theme.helpText) - self.sidebar.addLabel(self.tr("Preferences")) self.sidebar.buttonClicked.connect(self._sidebarClicked) + # Search Box + self.searchText = QLineEdit(self) + self.searchText.setPlaceholderText(self.tr("Search")) + self.searchText.setMinimumWidth(CONFIG.pxInt(200)) + self.searchAction = self.searchText.addAction( + SHARED.theme.getIcon("search"), QLineEdit.ActionPosition.TrailingPosition + ) + self.searchAction.triggered.connect(self._gotoSearch) + + self.searchBox = QHBoxLayout() + self.searchBox.addWidget(self.titleLabel) + self.searchBox.addStretch(1) + self.searchBox.addWidget(self.searchText, 1) + # Form self.mainForm = NScrollableForm(self) self.mainForm.setHelpTextStyle(SHARED.theme.helpText) # Buttons self.buttonBox = QDialogButtonBox( - QDialogButtonBox.Apply | QDialogButtonBox.Save | QDialogButtonBox.Close + QDialogButtonBox.StandardButton.Apply + | QDialogButtonBox.StandardButton.Save + | QDialogButtonBox.StandardButton.Close ) self.buttonBox.clicked.connect(self._dialogButtonClicked) # Assemble + self.mainSearch = QVBoxLayout() + self.mainSearch.addItem(self.searchBox) + self.mainSearch.addWidget(self.mainForm) + self.mainBox = QHBoxLayout() self.mainBox.addWidget(self.sidebar) + # self.mainBox.addLayout(self.mainSearch) self.mainBox.addWidget(self.mainForm) self.mainBox.setContentsMargins(0, 0, 0, 0) self.outerBox = QVBoxLayout() + self.outerBox.addLayout(self.searchBox) self.outerBox.addLayout(self.mainBox) self.outerBox.addWidget(self.buttonBox) self.outerBox.setSpacing(CONFIG.pxInt(8)) self.setLayout(self.outerBox) self.setSizeGripEnabled(True) + + # Build Form self.buildForm() + # Populate Search + self.searchCompleter = QCompleter(self.mainForm.labels, self) + self.searchCompleter.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive) + self.searchCompleter.setFilterMode(Qt.MatchFlag.MatchContains) + self.searchCompleter.activated.connect(self._gotoSearch) + + self.searchText.setCompleter(self.searchCompleter) + logger.debug("Ready: GuiPreferences") return @@ -768,9 +811,17 @@ class GuiPreferences(QDialog): logger.debug("Close: GuiPreferences") self._saveWindowSize() event.accept() + qApp.processEvents() self.deleteLater() return + def keyPressEvent(self, event: QKeyEvent) -> None: + """Overload keyPressEvent to block enter key to save.""" + if event.matches(QKeySequence.Cancel): + self.reject() + event.ignore() + return + ## # Private Slots ## @@ -794,6 +845,12 @@ class GuiPreferences(QDialog): self.mainForm.scrollToSection(section) return + @pyqtSlot() + def _gotoSearch(self) -> None: + """Go to the setting indicated by the search text.""" + self.mainForm.scrollToLabel(self.searchText.text().strip()) + return + @pyqtSlot() def _selectGuiFont(self) -> None: """Open the QFontDialog and set a font for the font style.""" @@ -972,7 +1029,6 @@ class GuiPreferences(QDialog): # Finalise CONFIG.saveConfig() self.newPreferencesReady.emit(needsRestart, refreshTree, updateTheme, updateSyntax) - qApp.processEvents() return diff --git a/novelwriter/extensions/configlayout.py b/novelwriter/extensions/configlayout.py index 83a2715c..676d055b 100644 --- a/novelwriter/extensions/configlayout.py +++ b/novelwriter/extensions/configlayout.py @@ -3,7 +3,9 @@ novelWriter – Custom Widget: Config Layout ========================================== File History: -Created: 2020-05-03 [0.4.5] +Created: 2020-05-03 [0.4.5] NConfigLayout, NHelpLabel +Created: 2023-05-23 [2.1b1] NSimpleLayout +Created: 2024-01-08 [2.3b1] NScrollableForm This file is a part of novelWriter Copyright 2018–2024, Veronica Berglyd Olsen @@ -45,6 +47,7 @@ class NScrollableForm(QScrollArea): self._fontScale = FONT_SCALE self._sections: dict[int, QLabel] = {} self._editable: dict[str, NHelpLabel] = {} + self._index: dict[str, QWidget] = {} self._layout = QVBoxLayout() self._layout.setSpacing(CONFIG.pxInt(12)) @@ -59,6 +62,18 @@ class NScrollableForm(QScrollArea): return + ## + # Properties + ## + + @property + def labels(self) -> list[str]: + return list(self._index.keys()) + + ## + # Setters + ## + def setHelpTextStyle(self, color: QColor | list | tuple, scale: float = FONT_SCALE) -> None: """Set the text color for the help text.""" self._helpCol = color if isinstance(color, QColor) else QColor(*color) @@ -71,18 +86,24 @@ class NScrollableForm(QScrollArea): qHelp.setText(text) return - def finalise(self) -> None: - """Finalise the layout when the form is built.""" - self._layout.addStretch(1) - return + ## + # Methods + ## - def scrollToSection(self, identifier: int, offset: int = 50) -> None: + def scrollToSection(self, identifier: int) -> None: """Scroll to the requested section identifier.""" if identifier in self._sections: yPos = self._sections[identifier].pos().y() - CONFIG.pxInt(8) self.verticalScrollBar().setValue(yPos) return + def scrollToLabel(self, label: str) -> None: + """Scroll to the requested label.""" + if label in self._index: + yPos = self._index[label].pos().y() - CONFIG.pxInt(8) + self.verticalScrollBar().setValue(yPos) + return + def addGroupLabel(self, label: str, identifier: int) -> None: """Add a text label to separate groups of settings.""" hM = CONFIG.pxInt(4) @@ -124,9 +145,15 @@ class NScrollableForm(QScrollArea): row.addWidget(button) self._layout.addLayout(row) + self._index[label.strip()] = widget return + def finalise(self) -> None: + """Finalise the layout when the form is built.""" + self._layout.addStretch(1) + return + # END Class NScrollableForm