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())