From cc4d92f0f73a35893d4ea2b24e75e32d168bf363 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 24 Jan 2024 17:05:14 +0100 Subject: [PATCH] Improve novel selector widget and add to novel details dialog --- novelwriter/dialogs/preferences.py | 18 ++-- novelwriter/dialogs/projdetails.py | 2 +- novelwriter/extensions/novelselector.py | 28 +++++- novelwriter/gui/mainmenu.py | 2 +- novelwriter/gui/noveltree.py | 7 +- novelwriter/gui/outline.py | 5 +- novelwriter/guimain.py | 8 +- novelwriter/tools/noveldetails.py | 122 ++++++++++++++++++++++++ novelwriter/tools/novelinfo.py | 71 -------------- 9 files changed, 167 insertions(+), 96 deletions(-) create mode 100644 novelwriter/tools/noveldetails.py delete mode 100644 novelwriter/tools/novelinfo.py diff --git a/novelwriter/dialogs/preferences.py b/novelwriter/dialogs/preferences.py index 41663a42..5ece68b1 100644 --- a/novelwriter/dialogs/preferences.py +++ b/novelwriter/dialogs/preferences.py @@ -63,11 +63,6 @@ class GuiPreferences(QDialog): ) self.titleLabel.setIndent(CONFIG.pxInt(4)) - # SideBar - self.sidebar = NPagedSideBar(self) - self.sidebar.setLabelColor(SHARED.theme.helpText) - self.sidebar.buttonClicked.connect(self._sidebarClicked) - # Search Box self.searchText = QLineEdit(self) self.searchText.setPlaceholderText(self.tr("Search")) @@ -77,10 +72,10 @@ class GuiPreferences(QDialog): ) self.searchAction.triggered.connect(self._gotoSearch) - self.searchBox = QHBoxLayout() - self.searchBox.addWidget(self.titleLabel) - self.searchBox.addStretch(1) - self.searchBox.addWidget(self.searchText, 1) + # SideBar + self.sidebar = NPagedSideBar(self) + self.sidebar.setLabelColor(SHARED.theme.helpText) + self.sidebar.buttonClicked.connect(self._sidebarClicked) # Form self.mainForm = NScrollableForm(self) @@ -95,6 +90,11 @@ class GuiPreferences(QDialog): self.buttonBox.clicked.connect(self._dialogButtonClicked) # Assemble + self.searchBox = QHBoxLayout() + self.searchBox.addWidget(self.titleLabel) + self.searchBox.addStretch(1) + self.searchBox.addWidget(self.searchText, 1) + self.mainBox = QHBoxLayout() self.mainBox.addWidget(self.sidebar) self.mainBox.addWidget(self.mainForm) diff --git a/novelwriter/dialogs/projdetails.py b/novelwriter/dialogs/projdetails.py index a6e12163..31ffea62 100644 --- a/novelwriter/dialogs/projdetails.py +++ b/novelwriter/dialogs/projdetails.py @@ -419,7 +419,7 @@ class GuiProjectDetailsContents(QWidget): def updateValues(self) -> None: """Populate the tree.""" self._currentRoot = None - self.novelValue.updateList() + self.novelValue.refreshNovelList() self.novelValue.setHandle(self.novelValue.firstHandle) self._prepareData(self.novelValue.firstHandle) self._populateTree() diff --git a/novelwriter/extensions/novelselector.py b/novelwriter/extensions/novelselector.py index 035ab570..793a475d 100644 --- a/novelwriter/extensions/novelselector.py +++ b/novelwriter/extensions/novelselector.py @@ -3,7 +3,7 @@ novelWriter – Custom Widget: Novel Selector =========================================== File History: -Created: 2022-11-17 [2.0] +Created: 2022-11-17 [2.0] NovelSelector This file is a part of novelWriter Copyright 2018–2024, Veronica Berglyd Olsen @@ -43,6 +43,8 @@ class NovelSelector(QComboBox): super().__init__(parent=parent) self._blockSignal = False self._firstHandle = None + self._includeAll = False + self._listFormat = None self.currentIndexChanged.connect(self._indexChanged) return @@ -74,7 +76,23 @@ class NovelSelector(QComboBox): self._blockSignal = False return - def updateList(self, includeAll: bool = False, prefix: str | None = None) -> None: + def setIncludeAll(self, value: bool) -> None: + """Set flag to add an "All Novel Folders" option.""" + self._includeAll = value + return + + def setListFormat(self, value: str | None) -> None: + """Set a format string for the list entries.""" + if value is None or "{0}" in value: + self._listFormat = value + return + + ## + # Public Slots + ## + + @pyqtSlot() + def refreshNovelList(self) -> None: """Rebuild the list of novel items.""" self._blockSignal = True self._firstHandle = None @@ -83,8 +101,8 @@ class NovelSelector(QComboBox): icon = SHARED.theme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL]) handle = self.currentData() for tHandle, nwItem in SHARED.project.tree.iterRoots(nwItemClass.NOVEL): - if prefix: - name = prefix.format(nwItem.itemName) + if self._listFormat: + name = self._listFormat.format(nwItem.itemName) self.addItem(name, tHandle) else: name = nwItem.itemName @@ -92,7 +110,7 @@ class NovelSelector(QComboBox): if self._firstHandle is None: self._firstHandle = tHandle - if includeAll: + if self._includeAll: self.insertSeparator(self.count()) self.addItem(icon, self.tr("All Novel Folders"), "") diff --git a/novelwriter/gui/mainmenu.py b/novelwriter/gui/mainmenu.py index 94cd1d20..4ad1ab09 100644 --- a/novelwriter/gui/mainmenu.py +++ b/novelwriter/gui/mainmenu.py @@ -162,7 +162,7 @@ class GuiMainMenu(QMenuBar): # Project > Novel Info self.aNovelInfo = self.projMenu.addAction(self.tr("Novel Info")) self.aNovelInfo.setShortcut("Shift+F6") - self.aNovelInfo.triggered.connect(self.mainGui.showNovelInfoDialog) + self.aNovelInfo.triggered.connect(self.mainGui.showNovelDetailsDialog) # Project > Separator self.projMenu.addSeparator() diff --git a/novelwriter/gui/noveltree.py b/novelwriter/gui/noveltree.py index 1569262b..959ec7ae 100644 --- a/novelwriter/gui/noveltree.py +++ b/novelwriter/gui/noveltree.py @@ -207,9 +207,10 @@ class GuiNovelToolBar(QWidget): # Novel Selector selFont = self.font() selFont.setWeight(QFont.Weight.Bold) - self.novelPrefix = self.tr("Outline of {0}") + self.novelValue = NovelSelector(self) self.novelValue.setFont(selFont) + self.novelValue.setListFormat(self.tr("Outline of {0}")) self.novelValue.setMinimumWidth(CONFIG.pxInt(150)) self.novelValue.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) self.novelValue.novelSelectionChanged.connect(self.setCurrentRoot) @@ -294,7 +295,7 @@ class GuiNovelToolBar(QWidget): "QComboBox {border-style: none; padding-left: 0;} " "QComboBox::drop-down {border-style: none}" ) - self.novelValue.updateList(prefix=self.novelPrefix) + self.novelValue.refreshNovelList() self.tbNovel.setVisible(self.novelValue.count() > 1) return @@ -307,7 +308,7 @@ class GuiNovelToolBar(QWidget): def buildNovelRootMenu(self) -> None: """Build the novel root menu.""" - self.novelValue.updateList(prefix=self.novelPrefix) + self.novelValue.refreshNovelList() self.tbNovel.setVisible(self.novelValue.count() > 1) return diff --git a/novelwriter/gui/outline.py b/novelwriter/gui/outline.py index db066cee..cd441bae 100644 --- a/novelwriter/gui/outline.py +++ b/novelwriter/gui/outline.py @@ -220,6 +220,7 @@ class GuiOutlineToolBar(QToolBar): self.novelLabel.setContentsMargins(0, 0, mPx, 0) self.novelValue = NovelSelector(self) + self.novelValue.setIncludeAll(True) self.novelValue.setMinimumWidth(CONFIG.pxInt(200)) self.novelValue.novelSelectionChanged.connect(self._novelValueChanged) @@ -258,7 +259,7 @@ class GuiOutlineToolBar(QToolBar): def updateTheme(self) -> None: """Update theme elements.""" self.setStyleSheet("QToolBar {border: 0px;}") - self.novelValue.updateList(includeAll=True) + self.novelValue.refreshNovelList() self.aRefresh.setIcon(SHARED.theme.getIcon("refresh")) self.tbColumns.setIcon(SHARED.theme.getIcon("menu")) self.tbColumns.setStyleSheet("QToolButton::menu-indicator {image: none;}") @@ -266,7 +267,7 @@ class GuiOutlineToolBar(QToolBar): def populateNovelList(self) -> None: """Reload the content of the novel list.""" - self.novelValue.updateList(includeAll=True) + self.novelValue.refreshNovelList() return def setCurrentRoot(self, rootHandle: str | None) -> None: diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index fea65021..8f4c3e4d 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -56,9 +56,9 @@ from novelwriter.dialogs.preferences import GuiPreferences from novelwriter.dialogs.projdetails import GuiProjectDetails from novelwriter.dialogs.projsettings import GuiProjectSettings from novelwriter.tools.welcome import GuiWelcome -from novelwriter.tools.novelinfo import GuiNovelInfo from novelwriter.tools.manuscript import GuiManuscript from novelwriter.tools.dictionaries import GuiDictionaries +from novelwriter.tools.noveldetails import GuiNovelDetails from novelwriter.tools.writingstats import GuiWritingStats from novelwriter.enum import ( @@ -838,10 +838,10 @@ class GuiMain(QMainWindow): return @pyqtSlot() - def showNovelInfoDialog(self) -> None: - """Open the novel info dialog.""" + def showNovelDetailsDialog(self) -> None: + """Open the novel details dialog.""" if SHARED.hasProject: - dialog = GuiNovelInfo(self) + dialog = GuiNovelDetails(self) dialog.setModal(True) dialog.show() dialog.raise_() diff --git a/novelwriter/tools/noveldetails.py b/novelwriter/tools/noveldetails.py new file mode 100644 index 00000000..66eec029 --- /dev/null +++ b/novelwriter/tools/noveldetails.py @@ -0,0 +1,122 @@ +""" +novelWriter – GUI Novel Info +============================ + +File History: +Created: 2024-01-18 [2.3b1] GuiNovelDetails + +This file is a part of novelWriter +Copyright 2018–2024, Veronica Berglyd Olsen + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" +from __future__ import annotations + +import logging + +from PyQt5.QtGui import QCloseEvent +from PyQt5.QtWidgets import ( + QDialog, QDialogButtonBox, QHBoxLayout, QStackedWidget, QVBoxLayout, + QWidget +) + +from novelwriter import CONFIG, SHARED +from novelwriter.extensions.configlayout import NColourLabel +from novelwriter.extensions.novelselector import NovelSelector +from novelwriter.extensions.pagedsidebar import NPagedSideBar + +logger = logging.getLogger(__name__) + + +class GuiNovelDetails(QDialog): + + def __init__(self, parent: QWidget) -> None: + super().__init__(parent=parent) + + logger.debug("Create: GuiNovelDetails") + self.setObjectName("GuiNovelDetails") + self.setWindowTitle(self.tr("Novel Details")) + self.setMinimumSize(CONFIG.pxInt(600), CONFIG.pxInt(500)) + + # Title + self.titleLabel = NColourLabel( + self.tr("Novel Details"), SHARED.theme.helpText, parent=self, scale=1.25 + ) + self.titleLabel.setIndent(CONFIG.pxInt(4)) + + # Novel Selector + self.novelSelector = NovelSelector(self) + self.novelSelector.refreshNovelList() + + # SideBar + self.sidebar = NPagedSideBar(self) + self.sidebar.setLabelColor(SHARED.theme.helpText) + # self.sidebar.buttonClicked.connect(self._sidebarClicked) + + # Content + self.mainStack = QStackedWidget(self) + + # Buttons + self.buttonBox = QDialogButtonBox(QDialogButtonBox.StandardButton.Close) + self.buttonBox.rejected.connect(self.close) + + # Assemble + self.topBox = QHBoxLayout() + self.topBox.addWidget(self.titleLabel) + self.topBox.addStretch(1) + self.topBox.addWidget(self.novelSelector, 1) + + self.mainBox = QHBoxLayout() + self.mainBox.addWidget(self.sidebar) + self.mainBox.addWidget(self.mainStack) + self.mainBox.setContentsMargins(0, 0, 0, 0) + + self.outerBox = QVBoxLayout() + self.outerBox.addLayout(self.topBox) + self.outerBox.addLayout(self.mainBox) + self.outerBox.addWidget(self.buttonBox) + self.outerBox.setSpacing(CONFIG.pxInt(8)) + + self.setLayout(self.outerBox) + self.setSizeGripEnabled(True) + + logger.debug("Ready: GuiNovelDetails") + + return + + def __del__(self) -> None: # pragma: no cover + logger.debug("Delete: GuiNovelDetails") + return + + ## + # Events + ## + + def closeEvent(self, event: QCloseEvent) -> None: + """Capture the user closing the window and save settings.""" + self._saveSettings() + event.accept() + self.deleteLater() + return + + ## + # Internal Functions + ## + + def _saveSettings(self) -> None: + """Save the user GUI settings.""" + logger.debug("Saving State: GuiNovelDetails") + return + +# END Class GuiNovelDetails diff --git a/novelwriter/tools/novelinfo.py b/novelwriter/tools/novelinfo.py deleted file mode 100644 index 611c80b3..00000000 --- a/novelwriter/tools/novelinfo.py +++ /dev/null @@ -1,71 +0,0 @@ -""" -novelWriter – GUI Novel Info -============================ - -File History: -Created: 2024-01-18 [2.3b1] GuiNovelInfo - -This file is a part of novelWriter -Copyright 2018–2024, Veronica Berglyd Olsen - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" -from __future__ import annotations - -import logging - -from PyQt5.QtGui import QCloseEvent -from PyQt5.QtWidgets import QDialog, QWidget - -logger = logging.getLogger(__name__) - - -class GuiNovelInfo(QDialog): - - def __init__(self, parent: QWidget) -> None: - super().__init__(parent=parent) - - logger.debug("Create: GuiNovelInfo") - self.setObjectName("GuiNovelInfo") - - self.setWindowTitle(self.tr("Novel Info")) - logger.debug("Ready: GuiNovelInfo") - - return - - def __del__(self) -> None: # pragma: no cover - logger.debug("Delete: GuiNovelInfo") - return - - ## - # Events - ## - - def closeEvent(self, event: QCloseEvent) -> None: - """Capture the user closing the window and save settings.""" - self._saveSettings() - event.accept() - self.deleteLater() - return - - ## - # Internal Functions - ## - - def _saveSettings(self) -> None: - """Save the user GUI settings.""" - logger.debug("Saving State: GuiNovelInfo") - return - -# END Class GuiNovelInfo