From 3761b1729f2f4e466beddb65a69a82c95725dc70 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 26 Jan 2024 21:31:14 +0100 Subject: [PATCH] Make the style of config dialogs consistent --- novelwriter/dialogs/preferences.py | 4 +- novelwriter/dialogs/projectsettings.py | 82 ++++++++++++----------- novelwriter/extensions/configlayout.py | 91 ++++++++++++++++++++------ novelwriter/tools/noveldetails.py | 52 ++++++++------- 4 files changed, 144 insertions(+), 85 deletions(-) diff --git a/novelwriter/dialogs/preferences.py b/novelwriter/dialogs/preferences.py index 5ece68b1..7653734e 100644 --- a/novelwriter/dialogs/preferences.py +++ b/novelwriter/dialogs/preferences.py @@ -59,9 +59,9 @@ class GuiPreferences(QDialog): # Title self.titleLabel = NColourLabel( - self.tr("Preferences"), SHARED.theme.helpText, parent=self, scale=1.25 + self.tr("Preferences"), SHARED.theme.helpText, + parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4) ) - self.titleLabel.setIndent(CONFIG.pxInt(4)) # Search Box self.searchText = QLineEdit(self) diff --git a/novelwriter/dialogs/projectsettings.py b/novelwriter/dialogs/projectsettings.py index 57bd4237..b7f50f24 100644 --- a/novelwriter/dialogs/projectsettings.py +++ b/novelwriter/dialogs/projectsettings.py @@ -29,15 +29,15 @@ import logging from PyQt5.QtGui import QCloseEvent, QColor, QIcon, QPixmap from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot from PyQt5.QtWidgets import ( - QColorDialog, QComboBox, QDialog, QDialogButtonBox, QHBoxLayout, QLabel, - QLineEdit, QPushButton, QStackedWidget, QTreeWidget, QTreeWidgetItem, - QVBoxLayout, QWidget, qApp + QColorDialog, QComboBox, QDialog, QDialogButtonBox, QHBoxLayout, QLineEdit, + QPushButton, QStackedWidget, QTreeWidget, QTreeWidgetItem, QVBoxLayout, + QWidget, qApp ) from novelwriter import CONFIG, SHARED from novelwriter.common import simplified from novelwriter.extensions.switch import NSwitch -from novelwriter.extensions.configlayout import NColourLabel, NScrollableForm +from novelwriter.extensions.configlayout import NColourLabel, NFixedPage, NScrollableForm from novelwriter.extensions.pagedsidebar import NPagedSideBar logger = logging.getLogger(__name__) @@ -59,21 +59,18 @@ class GuiProjectSettings(QDialog): self.setObjectName("GuiProjectSettings") self.setWindowTitle(self.tr("Project Settings")) - wW = CONFIG.pxInt(500) - wH = CONFIG.pxInt(400) options = SHARED.project.options - - self.setMinimumSize(wW, wH) + self.setMinimumSize(CONFIG.pxInt(500), CONFIG.pxInt(400)) self.resize( - CONFIG.pxInt(options.getInt("GuiProjectSettings", "winWidth", wW)), - CONFIG.pxInt(options.getInt("GuiProjectSettings", "winHeight", wH)) + CONFIG.pxInt(options.getInt("GuiProjectSettings", "winWidth", CONFIG.pxInt(650))), + CONFIG.pxInt(options.getInt("GuiProjectSettings", "winHeight", CONFIG.pxInt(500))) ) # Title self.titleLabel = NColourLabel( - self.tr("Project Settings"), SHARED.theme.helpText, parent=self, scale=1.25 + self.tr("Project Settings"), SHARED.theme.helpText, + parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4) ) - self.titleLabel.setIndent(CONFIG.pxInt(4)) # SideBar self.sidebar = NPagedSideBar(self) @@ -251,31 +248,33 @@ class _SettingsPage(NScrollableForm): self.addRow( self.tr("Author(s)"), self.projAuthor, self.tr("Only used when building the manuscript."), - stretch=(2, 1) + stretch=(3, 2) ) # Project Language self.projLang = QComboBox(self) - self.projLang.setMaximumWidth(xW) + self.projLang.setMinimumWidth(xW) for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ): self.projLang.addItem(language, tag) self.addRow( self.tr("Project language"), self.projLang, - self.tr("Only used when building the manuscript.") + self.tr("Only used when building the manuscript."), + stretch=(3, 2) ) if (idx := self.projLang.findData(data.language)) != -1: self.projLang.setCurrentIndex(idx) # Spell Check Language self.spellLang = QComboBox(self) - self.spellLang.setMaximumWidth(xW) + self.spellLang.setMinimumWidth(xW) self.spellLang.addItem(self.tr("Default"), "None") if CONFIG.hasEnchant: for tag, language in SHARED.spelling.listDictionaries(): self.spellLang.addItem(language, tag) self.addRow( self.tr("Spell check language"), self.spellLang, - self.tr("Overrides main preferences.") + self.tr("Overrides main preferences."), + stretch=(3, 2) ) if (idx := self.spellLang.findData(data.spellLang)) != -1: self.spellLang.setCurrentIndex(idx) @@ -295,7 +294,7 @@ class _SettingsPage(NScrollableForm): # END Class _SettingsPage -class _StatusPage(QWidget): +class _StatusPage(NFixedPage): COL_LABEL = 0 COL_USAGE = 1 @@ -326,9 +325,13 @@ class _StatusPage(QWidget): self.iPx = SHARED.theme.baseIconSize - # The List - # ======== + # Title + self.pageTitle = NColourLabel( + pageLabel, SHARED.theme.helpText, parent=self, + scale=NColourLabel.HEADER_SCALE + ) + # List Box self.listBox = QTreeWidget(self) self.listBox.setHeaderLabels([ self.tr("Label"), self.tr("Usage"), @@ -392,11 +395,10 @@ class _StatusPage(QWidget): self.innerBox.addLayout(self.listControls) self.outerBox = QVBoxLayout() - self.outerBox.addWidget(QLabel("%s" % pageLabel)) + self.outerBox.addWidget(self.pageTitle) self.outerBox.addLayout(self.innerBox) - self.outerBox.setContentsMargins(0, 0, 0, 0) - self.setLayout(self.outerBox) + self.setCentralLayout(self.outerBox) return @@ -422,7 +424,6 @@ class _StatusPage(QWidget): "cols": item.data(self.COL_LABEL, self.COL_ROLE), }) return newList, self._colDeleted - return [], [] def columnWidth(self) -> int: @@ -517,16 +518,16 @@ class _StatusPage(QWidget): ## def _addItem(self, key: str | None, name: str, - cols: tuple[int, int, int], count: int) -> None: + colour: tuple[int, int, int], count: int) -> None: """Add a status item to the list.""" pixmap = QPixmap(self.iPx, self.iPx) - pixmap.fill(QColor(*cols)) + pixmap.fill(QColor(*colour)) item = QTreeWidgetItem() item.setText(self.COL_LABEL, name) item.setIcon(self.COL_LABEL, QIcon(pixmap)) item.setData(self.COL_LABEL, self.KEY_ROLE, key) - item.setData(self.COL_LABEL, self.COL_ROLE, cols) + item.setData(self.COL_LABEL, self.COL_ROLE, colour) item.setData(self.COL_LABEL, self.NUM_ROLE, count) item.setText(self.COL_USAGE, self._usageString(count)) @@ -558,24 +559,23 @@ class _StatusPage(QWidget): def _getSelectedItem(self) -> QTreeWidgetItem | None: """Get the currently selected item.""" - selItem = self.listBox.selectedItems() - if len(selItem) > 0: - return selItem[0] + if items := self.listBox.selectedItems(): + return items[0] return None - def _usageString(self, nUse: int) -> str: + def _usageString(self, count: int) -> str: """Generate usage string.""" - if nUse == 0: + if count == 0: return self.tr("Not in use") - elif nUse == 1: + elif count == 1: return self.tr("Used once") else: - return self.tr("Used by {0} items").format(nUse) + return self.tr("Used by {0} items").format(count) # END Class _StatusPage -class _ReplacePage(QWidget): +class _ReplacePage(NFixedPage): COL_KEY = 0 COL_REPL = 1 @@ -588,7 +588,12 @@ class _ReplacePage(QWidget): wCol0 = CONFIG.pxInt( SHARED.project.options.getInt("GuiProjectSettings", "replaceColW", 130) ) - pageLabel = self.tr("Text Replace List for Preview and Export") + + # Title + self.pageTitle = NColourLabel( + self.tr("Text Auto-Replace for Preview and Build"), + SHARED.theme.helpText, parent=self, scale=NColourLabel.HEADER_SCALE + ) # List Box self.listBox = QTreeWidget() @@ -644,11 +649,10 @@ class _ReplacePage(QWidget): self.innerBox.addLayout(self.listControls) self.outerBox = QVBoxLayout() - self.outerBox.addWidget(QLabel("%s" % pageLabel)) + self.outerBox.addWidget(self.pageTitle) self.outerBox.addLayout(self.innerBox) - self.outerBox.setContentsMargins(0, 0, 0, 0) - self.setLayout(self.outerBox) + self.setCentralLayout(self.outerBox) return diff --git a/novelwriter/extensions/configlayout.py b/novelwriter/extensions/configlayout.py index 39e1e651..809068bc 100644 --- a/novelwriter/extensions/configlayout.py +++ b/novelwriter/extensions/configlayout.py @@ -6,6 +6,8 @@ File History: Created: 2020-05-03 [0.4.5] NConfigLayout, NColourLabel Created: 2023-05-23 [2.1b1] NSimpleLayout Created: 2024-01-08 [2.3b1] NScrollableForm +Created: 2024-01-26 [2.3b1] NScrollablePage +Created: 2024-01-26 [2.3b1] NFixedPage This file is a part of novelWriter Copyright 2018–2024, Veronica Berglyd Olsen @@ -28,17 +30,59 @@ from __future__ import annotations from PyQt5.QtGui import QColor, QPalette from PyQt5.QtCore import Qt from PyQt5.QtWidgets import ( - QAbstractButton, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QScrollArea, QSizePolicy, - QVBoxLayout, QWidget + QAbstractButton, QFrame, QGridLayout, QHBoxLayout, QLabel, QLayout, QLineEdit, + QScrollArea, QSizePolicy, QVBoxLayout, QWidget ) from novelwriter import CONFIG -FONT_SCALE = 0.9 +DEFAULT_SCALE = 0.9 RIGHT_TOP = Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignTop LEFT_TOP = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop +class NFixedPage(QFrame): + """Extension: Fixed Page Widget + + A custom widget that holds a layout. This is just a wrapper around a + QFrame that sets the same frame style as the other Page widgets. + """ + + def __init__(self, parent: QWidget) -> None: + super().__init__(parent=parent) + self.setFrameShadow(QFrame.Shadow.Sunken) + self.setFrameShape(QFrame.Shape.StyledPanel) + self.setCentralLayout = self.setLayout + return + +# END Class NFixedPage + + +class NScrollablePage(QScrollArea): + """Extension: Scrollable Page Widget + + A custom widget that holds a layout within a scrollable area. + """ + + def __init__(self, parent: QWidget) -> None: + super().__init__(parent=parent) + self._widget = QWidget(self) + self.setWidget(self._widget) + self.setWidgetResizable(True) + self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded) + self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded) + self.setFrameShadow(QFrame.Shadow.Sunken) + self.setFrameShape(QFrame.Shape.StyledPanel) + return + + def setCentralLayout(self, layout: QLayout) -> None: + """Set the central layout of the scroll page.""" + self._widget.setLayout(layout) + return + +# END Class NScrollablePage + + class NScrollableForm(QScrollArea): """Extension: Scrollable Form Widget @@ -48,7 +92,7 @@ class NScrollableForm(QScrollArea): def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) self._helpCol = QColor(0, 0, 0) - self._fontScale = FONT_SCALE + self._fontScale = DEFAULT_SCALE self._first = True self._indent = CONFIG.pxInt(12) @@ -66,6 +110,8 @@ class NScrollableForm(QScrollArea): self.setWidgetResizable(True) self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded) self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded) + self.setFrameShadow(QFrame.Shadow.Sunken) + self.setFrameShape(QFrame.Shape.StyledPanel) return @@ -81,7 +127,7 @@ class NScrollableForm(QScrollArea): # Setters ## - def setHelpTextStyle(self, color: QColor, scale: float = FONT_SCALE) -> None: + def setHelpTextStyle(self, color: QColor, scale: float = DEFAULT_SCALE) -> None: """Set the text color for the help text.""" self._helpCol = color self._fontScale = scale @@ -145,13 +191,14 @@ class NScrollableForm(QScrollArea): qLabel.setBuddy(widget) if helpText: - qHelp = NColourLabel(str(helpText), self._helpCol, scale=self._fontScale, wrap=True) - qHelp.setIndent(self._indent) + qHelp = NColourLabel( + str(helpText), self._helpCol, parent=self, + scale=self._fontScale, wrap=True, indent=self._indent + ) labelBox = QVBoxLayout() labelBox.addWidget(qLabel) labelBox.addWidget(qHelp) labelBox.setSpacing(0) - labelBox.addStretch(1) row.addLayout(labelBox, stretch[0]) if editable: self._editable[editable] = qHelp @@ -193,7 +240,7 @@ class NConfigLayout(QGridLayout): self._nextRow = 0 self._helpCol = QColor(0, 0, 0) - self._fontScale = FONT_SCALE + self._fontScale = DEFAULT_SCALE self._itemMap = {} wSp = CONFIG.pxInt(8) @@ -207,7 +254,7 @@ class NConfigLayout(QGridLayout): # Getters and Setters ## - def setHelpTextStyle(self, color: QColor, scale: float = FONT_SCALE) -> None: + def setHelpTextStyle(self, color: QColor, scale: float = DEFAULT_SCALE) -> None: """Set the text color for the help text.""" self._helpCol = color if isinstance(color, QColor) else QColor(*color) self._fontScale = scale @@ -238,8 +285,10 @@ class NConfigLayout(QGridLayout): qHelp = None if helpText is not None: - qHelp = NColourLabel(str(helpText), self._helpCol, scale=self._fontScale, wrap=True) - qHelp.setIndent(wSp) + qHelp = NColourLabel( + str(helpText), self._helpCol, + scale=self._fontScale, wrap=True, indent=wSp + ) labelBox = QVBoxLayout() labelBox.addWidget(qLabel) labelBox.addWidget(qHelp) @@ -337,17 +386,21 @@ class NColourLabel(QLabel): optionally at a specific size, and word wrapped. """ + HELP_SCALE = DEFAULT_SCALE + HEADER_SCALE = 1.25 + def __init__(self, text: str, color: QColor, parent: QWidget | None = None, - scale: float = FONT_SCALE, wrap: bool = False) -> None: + scale: float = HELP_SCALE, wrap: bool = False, indent: int = 0) -> None: super().__init__(text, parent=parent) - lblCol = self.palette() - lblCol.setColor(QPalette.WindowText, color) - self.setPalette(lblCol) + font = self.font() + font.setPointSizeF(scale*font.pointSizeF()) + colour = self.palette() + colour.setColor(QPalette.WindowText, color) - lblFont = self.font() - lblFont.setPointSizeF(scale*lblFont.pointSizeF()) - self.setFont(lblFont) + self.setPalette(colour) + self.setFont(font) + self.setIndent(indent) if wrap: self.setWordWrap(True) diff --git a/novelwriter/tools/noveldetails.py b/novelwriter/tools/noveldetails.py index 3fcd54cc..71375d7f 100644 --- a/novelwriter/tools/noveldetails.py +++ b/novelwriter/tools/noveldetails.py @@ -38,14 +38,12 @@ from novelwriter import CONFIG, SHARED from novelwriter.common import formatTime, numberToRoman from novelwriter.constants import nwUnicode from novelwriter.extensions.switch import NSwitch -from novelwriter.extensions.configlayout import NColourLabel +from novelwriter.extensions.configlayout import NColourLabel, NFixedPage, NScrollablePage from novelwriter.extensions.pagedsidebar import NPagedSideBar from novelwriter.extensions.novelselector import NovelSelector logger = logging.getLogger(__name__) -HEADER_SIZE = 1.4 - class GuiNovelDetails(QDialog): @@ -59,21 +57,18 @@ class GuiNovelDetails(QDialog): self.setObjectName("GuiNovelDetails") self.setWindowTitle(self.tr("Novel Details")) - wW = CONFIG.pxInt(500) - wH = CONFIG.pxInt(400) options = SHARED.project.options - - self.setMinimumSize(wW, wH) + self.setMinimumSize(CONFIG.pxInt(500), CONFIG.pxInt(400)) self.resize( - CONFIG.pxInt(options.getInt("GuiNovelDetails", "winWidth", wW)), - CONFIG.pxInt(options.getInt("GuiNovelDetails", "winHeight", wH)) + CONFIG.pxInt(options.getInt("GuiNovelDetails", "winWidth", CONFIG.pxInt(650))), + CONFIG.pxInt(options.getInt("GuiNovelDetails", "winHeight", CONFIG.pxInt(500))) ) # Title self.titleLabel = NColourLabel( - self.tr("Novel Details"), SHARED.theme.helpText, parent=self, scale=1.25 + self.tr("Novel Details"), SHARED.theme.helpText, + parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4) ) - self.titleLabel.setIndent(CONFIG.pxInt(4)) # Novel Selector self.novelSelector = NovelSelector(self) @@ -192,7 +187,7 @@ class GuiNovelDetails(QDialog): # END Class GuiNovelDetails -class _OverviewPage(QWidget): +class _OverviewPage(NScrollablePage): def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) @@ -204,7 +199,8 @@ class _OverviewPage(QWidget): # Project Info self.projLabel = NColourLabel( - self.tr("Project"), SHARED.theme.helpText, parent=self, scale=HEADER_SIZE + self.tr("Project"), SHARED.theme.helpText, + parent=self, scale=NColourLabel.HEADER_SCALE ) self.projName = QLabel("", self) @@ -227,7 +223,8 @@ class _OverviewPage(QWidget): # Novel Info self.novelLabel = NColourLabel( - self.tr("Selected Novel"), SHARED.theme.helpText, parent=self, scale=HEADER_SIZE + self.tr("Selected Novel"), SHARED.theme.helpText, + parent=self, scale=NColourLabel.HEADER_SCALE ) self.novelName = QLabel("", self) @@ -250,11 +247,10 @@ class _OverviewPage(QWidget): self.outerBox.addLayout(self.projForm) self.outerBox.addWidget(self.novelLabel) self.outerBox.addLayout(self.novelForm) - self.outerBox.setContentsMargins(0, 0, 0, 0) self.outerBox.setSpacing(sPx) self.outerBox.addStretch(1) - self.setLayout(self.outerBox) + self.setCentralLayout(self.outerBox) return @@ -299,7 +295,7 @@ class _OverviewPage(QWidget): # END Class _OverviewPage -class _ContentsPage(QWidget): +class _ContentsPage(NFixedPage): C_TITLE = 0 C_WORDS = 1 @@ -320,7 +316,8 @@ class _ContentsPage(QWidget): # Title self.contentLabel = NColourLabel( - self.tr("Table of Contents"), SHARED.theme.helpText, parent=self, scale=HEADER_SIZE + self.tr("Table of Contents"), SHARED.theme.helpText, + parent=self, scale=NColourLabel.HEADER_SCALE ) # Contents Tree @@ -407,20 +404,25 @@ class _ContentsPage(QWidget): self.outerBox.addWidget(self.contentLabel) self.outerBox.addWidget(self.tocTree) self.outerBox.addLayout(self.optionsBox) - self.outerBox.setContentsMargins(0, 0, 0, 0) - self.setLayout(self.outerBox) + self.setCentralLayout(self.outerBox) return def saveSettings(self) -> None: """Save the user GUI settings.""" + widthCol0 = CONFIG.rpxInt(self.tocTree.columnWidth(0)) + widthCol1 = CONFIG.rpxInt(self.tocTree.columnWidth(1)) + widthCol2 = CONFIG.rpxInt(self.tocTree.columnWidth(2)) + widthCol3 = CONFIG.rpxInt(self.tocTree.columnWidth(3)) + widthCol4 = CONFIG.rpxInt(self.tocTree.columnWidth(4)) + options = SHARED.project.options - options.setValue("GuiNovelDetails", "widthCol0", self.tocTree.columnWidth(0)) - options.setValue("GuiNovelDetails", "widthCol1", self.tocTree.columnWidth(1)) - options.setValue("GuiNovelDetails", "widthCol2", self.tocTree.columnWidth(2)) - options.setValue("GuiNovelDetails", "widthCol3", self.tocTree.columnWidth(3)) - options.setValue("GuiNovelDetails", "widthCol4", self.tocTree.columnWidth(4)) + options.setValue("GuiNovelDetails", "widthCol0", widthCol0) + options.setValue("GuiNovelDetails", "widthCol1", widthCol1) + options.setValue("GuiNovelDetails", "widthCol2", widthCol2) + options.setValue("GuiNovelDetails", "widthCol3", widthCol3) + options.setValue("GuiNovelDetails", "widthCol4", widthCol4) options.setValue("GuiNovelDetails", "wordsPerPage", self.wpValue.value()) options.setValue("GuiNovelDetails", "countFrom", self.poValue.value()) options.setValue("GuiNovelDetails", "clearDouble", self.dblValue.isChecked())