Manuscript theme update fix (#2545)

This commit is contained in:
Veronica Berglyd Olsen
2025-10-26 03:20:40 +01:00
committed by GitHub
22 changed files with 281 additions and 94 deletions
+2
View File
@@ -552,6 +552,8 @@ class NWProject:
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: NWProject")
self._data.itemStatus.refreshIcons()
self._data.itemImport.refreshIcons()
+5 -5
View File
@@ -158,7 +158,7 @@ class GuiPreferences(NDialog):
self.mainForm.addGroupLabel(title, section)
# Display Language
self.guiLocale = NComboBox(self)
self.guiLocale = NComboBox(self, scrollable=True)
self.guiLocale.setMinimumWidth(200)
for lang, name in CONFIG.listLanguages(CONFIG.LANG_NW):
self.guiLocale.addItem(name, lang)
@@ -170,9 +170,9 @@ class GuiPreferences(NDialog):
)
# Colour Theme
self.lightTheme = NComboBox(self)
self.lightTheme = NComboBox(self, scrollable=True)
self.lightTheme.setMinimumWidth(200)
self.darkTheme = NComboBox(self)
self.darkTheme = NComboBox(self, scrollable=True)
self.darkTheme.setMinimumWidth(200)
for key, theme in SHARED.theme.colourThemes.items():
if theme.dark:
@@ -193,7 +193,7 @@ class GuiPreferences(NDialog):
)
# Icon Theme
self.iconTheme = NComboBox(self)
self.iconTheme = NComboBox(self, scrollable=True)
self.iconTheme.setMinimumWidth(200)
for key, theme in SHARED.theme.iconCache.iconThemes.items():
self.iconTheme.addItem(theme.name, key)
@@ -511,7 +511,7 @@ class GuiPreferences(NDialog):
self.mainForm.addGroupLabel(title, section)
# Spell Checking
self.spellLanguage = NComboBox(self)
self.spellLanguage = NComboBox(self, scrollable=True)
self.spellLanguage.setMinimumWidth(200)
if CONFIG.hasEnchant:
+2 -2
View File
@@ -257,7 +257,7 @@ class _SettingsPage(NScrollableForm):
# Project Language
projLang = data.language or CONFIG.guiLocale
self.projLang = NComboBox(self)
self.projLang = NComboBox(self, scrollable=True)
self.projLang.setMinimumWidth(200)
for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ):
self.projLang.addItem(language, tag)
@@ -269,7 +269,7 @@ class _SettingsPage(NScrollableForm):
)
# Spell Check Language
self.spellLang = NComboBox(self)
self.spellLang = NComboBox(self, scrollable=True)
self.spellLang.setMinimumWidth(200)
self.spellLang.addItem(self.tr("Default"), "None")
if CONFIG.hasEnchant:
+2 -6
View File
@@ -27,7 +27,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
""" # noqa
from __future__ import annotations
from PyQt6.QtGui import QColor, QFont, QPalette, QPixmap
from PyQt6.QtGui import QColor, QFont, QPalette
from PyQt6.QtWidgets import (
QAbstractButton, QFrame, QHBoxLayout, QLabel, QLayout, QScrollArea,
QVBoxLayout, QWidget
@@ -170,7 +170,7 @@ class NScrollableForm(QScrollArea):
def addRow(
self,
label: str | None,
widget: QWidget | list[QWidget | QPixmap | int],
widget: QWidget | list[QWidget | int],
helpText: str = "",
unit: str | None = None,
button: QWidget | None = None,
@@ -187,10 +187,6 @@ class NScrollableForm(QScrollArea):
for item in widget:
if isinstance(item, QWidget):
wBox.addWidget(item)
elif isinstance(item, QPixmap):
icon = QLabel(self)
icon.setPixmap(item)
wBox.addWidget(icon)
elif isinstance(item, int):
wBox.addSpacing(item)
qWidget = QWidget(self)
+14 -12
View File
@@ -125,13 +125,15 @@ class NComboBox(QComboBox):
window of many widgets.
"""
def __init__(self, parent: QWidget | None = None, maxItems: int = 15) -> None:
def __init__(
self, parent: QWidget | None = None, maxItems: int = 15, scrollable: bool = False
) -> None:
super().__init__(parent=parent)
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
self.setMaxVisibleItems(maxItems)
# The style sheet disables Fusion style pop-up mode on some platforms
# and allows for scrolling of long lists of items
if scrollable:
# The style sheet disables Fusion style pop-up mode on some
# platforms and allows for scrolling of long lists of items
self.setStyleSheet("QComboBox {combobox-popup: 0;}")
def wheelEvent(self, event: QWheelEvent) -> None:
@@ -214,10 +216,10 @@ class NPushButton(QPushButton):
self._color = color
self.setText(text)
self.setIconSize(iconSize)
self.refreshIcon()
self.updateIcon()
def refreshIcon(self) -> None:
"""Reload the theme icon."""
def updateIcon(self) -> None:
"""Update the theme icon."""
if self._icon:
self.setIcon(SHARED.theme.getIcon(self._icon, self._color))
@@ -239,9 +241,9 @@ class NIconToolButton(QToolButton):
if icon:
self.setThemeIcon(icon, color)
def setThemeIcon(self, iconKey: str, color: str | None = None) -> None:
def setThemeIcon(self, icon: str, color: str | None = None) -> None:
"""Set an icon from the current theme."""
self.setIcon(SHARED.theme.getIcon(iconKey, color))
self.setIcon(SHARED.theme.getIcon(icon, color))
class NIconToggleButton(QToolButton):
@@ -260,10 +262,10 @@ class NIconToggleButton(QToolButton):
if icon:
self.setThemeIcon(icon)
def setThemeIcon(self, iconKey: str) -> None:
def setThemeIcon(self, icon: str) -> None:
"""Set an icon from the current theme."""
iconSize = self.iconSize()
self.setIcon(SHARED.theme.getToggleIcon(iconKey, (iconSize.width(), iconSize.height())))
size = self.iconSize()
self.setIcon(SHARED.theme.getToggleIcon(icon, (size.width(), size.height())))
class NClickableLabel(QLabel):
+7
View File
@@ -72,6 +72,9 @@ class NPagedSideBar(QToolBar):
def setLabelColor(self, color: QColor) -> None:
"""Set the text color for the labels."""
self._labelCol = color
for widget in self.children():
if isinstance(widget, _NPagedToolLabel):
widget.setTextColor(color)
def addLabel(self, text: str) -> None:
"""Add a new label to the toolbar."""
@@ -188,6 +191,10 @@ class _NPagedToolLabel(QLabel):
self._textCol = textColor or self.palette().text().color()
def setTextColor(self, textColor: QColor | None = None) -> None:
"""Set a new text colour."""
self._textCol = textColor or self.palette().text().color()
def paintEvent(self, event: QPaintEvent) -> None:
"""Overload the paint event to draw a simple, left aligned text
label that matches the button style.
+7 -5
View File
@@ -34,7 +34,7 @@ from novelwriter.types import QtNoPen, QtPaintAntiAlias, QtSizeFixed
class NSwitch(QAbstractButton):
"""Custom: Toggle Switch."""
__slots__ = ("_cOff", "_cOn", "_offset", "_rH", "_rR", "_xH", "_xR", "_xW")
__slots__ = ("_offset", "_rH", "_rR", "_xH", "_xR", "_xW")
def __init__(self, parent: QWidget, height: int = 0) -> None:
super().__init__(parent=parent)
@@ -45,13 +45,11 @@ class NSwitch(QAbstractButton):
self._rH = self._xH - 4
self._rR = self._xR - 2
self._cOn = SHARED.theme.accentCol
self._cOff = self.palette().alternateBase()
self.setCheckable(True)
self.setSizePolicy(QtSizeFixed, QtSizeFixed)
self.setFixedWidth(self._xW)
self.setFixedHeight(self._xH)
self.setUpdatesEnabled(True)
self._offset = self._xR
self.clicked.connect(self._onClick)
@@ -96,7 +94,7 @@ class NSwitch(QAbstractButton):
painter.setOpacity(1.0 if self.isEnabled() else 0.5)
painter.setPen(palette.highlight().color() if self.hasFocus() else palette.mid().color())
painter.setBrush(self._cOn if self.isChecked() else self._cOff)
painter.setBrush(SHARED.theme.accentCol if self.isChecked() else palette.alternateBase())
painter.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR)
painter.setPen(QtNoPen)
@@ -110,6 +108,10 @@ class NSwitch(QAbstractButton):
self.setCursor(Qt.CursorShape.PointingHandCursor)
super().enterEvent(event)
##
# Internal Functions
##
@pyqtSlot(bool)
def _onClick(self, checked: bool) -> None:
"""Animate the toggle action."""
+12 -3
View File
@@ -292,6 +292,8 @@ class GuiDocEditor(QPlainTextEdit):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiDocEditor")
self.docSearch.updateTheme()
self.docHeader.updateTheme()
self.docFooter.updateTheme()
@@ -2478,8 +2480,9 @@ class GuiDocToolBar(QWidget):
def updateTheme(self) -> None:
"""Initialise GUI elements that depend on specific settings."""
syntax = SHARED.theme.syntaxTheme
logger.debug("Theme Update: GuiDocToolBar")
syntax = SHARED.theme.syntaxTheme
palette = self.palette()
palette.setColor(QPalette.ColorRole.Window, syntax.back)
palette.setColor(QPalette.ColorRole.WindowText, syntax.text)
@@ -2590,7 +2593,7 @@ class GuiDocEditSearch(QFrame):
# Buttons
# =======
self.showReplace = NIconToggleButton(self, iSz, "unfold")
self.showReplace = NIconToggleButton(self, iSz)
self.showReplace.toggled.connect(self._doToggleReplace)
self.searchButton = NIconToolButton(self, iSz)
@@ -2716,8 +2719,9 @@ class GuiDocEditSearch(QFrame):
def updateTheme(self) -> None:
"""Update theme elements."""
palette = QApplication.palette()
logger.debug("Theme Update: GuiDocEditSearch")
palette = QApplication.palette()
self.setPalette(palette)
self.searchBox.setPalette(palette)
self.replaceBox.setPalette(palette)
@@ -2732,6 +2736,7 @@ class GuiDocEditSearch(QFrame):
self.cancelSearch.setIcon(SHARED.theme.getIcon("search_cancel"))
self.searchButton.setThemeIcon("search", "green")
self.replaceButton.setThemeIcon("search_replace", "green")
self.showReplace.setThemeIcon("unfold")
# Set stylesheets
self.searchOpt.setStyleSheet("QToolBar {padding: 0;}")
@@ -2960,6 +2965,8 @@ class GuiDocEditHeader(QWidget):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiDocEditHeader")
self.tbButton.setThemeIcon("fmt_toolbar", "blue")
self.outlineButton.setThemeIcon("list", "blue")
self.searchButton.setThemeIcon("search", "blue")
@@ -3149,6 +3156,8 @@ class GuiDocEditFooter(QWidget):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiDocEditFooter")
iPx = round(0.9*SHARED.theme.baseIconHeight)
self.linesIcon.setPixmap(SHARED.theme.getPixmap("lines", (iPx, iPx)))
self.wordsIcon.setPixmap(SHARED.theme.getPixmap("stats", (iPx, iPx)))
+5 -1
View File
@@ -141,6 +141,7 @@ class GuiDocViewer(QTextBrowser):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiDocViewer")
self.docHeader.updateTheme()
self.docFooter.updateTheme()
@@ -724,6 +725,8 @@ class GuiDocViewHeader(QWidget):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiDocViewHeader")
self.outlineButton.setThemeIcon("list", "blue")
self.backButton.setThemeIcon("chevron_left", "blue")
self.forwardButton.setThemeIcon("chevron_right", "blue")
@@ -910,7 +913,8 @@ class GuiDocViewFooter(QWidget):
def updateTheme(self) -> None:
"""Update theme elements."""
# Icons
logger.debug("Theme Update: GuiDocViewFooter")
fPx = int(0.9*SHARED.theme.fontPixelSize)
bulletIcon = SHARED.theme.getToggleIcon("bullet", (fPx, fPx), "blue")
+6
View File
@@ -106,6 +106,8 @@ class GuiDocViewerPanel(QWidget):
def updateTheme(self, updateTabs: bool = True) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiDocViewerPanel")
self.optsButton.setThemeIcon("more_vertical")
self.optsButton.setStyleSheet(SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON))
self.mainTabs.setStyleSheet(SHARED.theme.getStyleSheet(STYLES_FLAT_TABS))
@@ -270,6 +272,8 @@ class _ViewPanelBackRefs(QTreeWidget):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: _ViewPanelBackRefs")
self._editIcon = SHARED.theme.getIcon("edit", "green")
self._viewIcon = SHARED.theme.getIcon("view", "blue")
for i in range(self.topLevelItemCount()):
@@ -400,6 +404,8 @@ class _ViewPanelKeyWords(QTreeWidget):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: _ViewPanelKeyWords")
self._classIcon = SHARED.theme.getIcon(nwLabels.CLASS_ICON[self._class], "root")
self._editIcon = SHARED.theme.getIcon("edit", "green")
self._viewIcon = SHARED.theme.getIcon("view", "blue")
+1
View File
@@ -216,6 +216,7 @@ class GuiItemDetails(QWidget):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiItemDetails")
self.updateViewBox(self._handle)
def updateViewBox(self, tHandle: str | None) -> None:
+3 -2
View File
@@ -89,6 +89,7 @@ class GuiNovelView(QWidget):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiNovelView")
self.novelBar.updateTheme()
def initSettings(self) -> None:
@@ -244,12 +245,12 @@ class GuiNovelToolBar(QWidget):
def updateTheme(self) -> None:
"""Update theme elements."""
# Icons
logger.debug("Theme Update: GuiNovelToolBar")
self.tbNovel.setThemeIcon("cls_novel", "red")
self.tbRefresh.setThemeIcon("refresh", "green")
self.tbMore.setThemeIcon("more_vertical")
# StyleSheets
buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
self.tbNovel.setStyleSheet(buttonStyle)
self.tbRefresh.setStyleSheet(buttonStyle)
+6
View File
@@ -103,6 +103,8 @@ class GuiOutlineView(QWidget):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiOutlineView")
self.outlineBar.updateTheme()
self.outlineTree.updateTheme()
self.outlineTree.refreshTree(
@@ -258,6 +260,8 @@ class GuiOutlineToolBar(QToolBar):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiOutlineToolBar")
self.setStyleSheet("QToolBar {border: 0px;}")
self.novelValue.refreshNovelList()
self.aRefresh.setIcon(SHARED.theme.getIcon("refresh", "green"))
@@ -454,6 +458,8 @@ class GuiOutlineTree(QTreeWidget):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiOutlineTree")
iType = nwItemType.FILE
iClass = nwItemClass.NO_CLASS
iLayout = nwItemLayout.DOCUMENT
+3
View File
@@ -138,6 +138,7 @@ class GuiProjectView(QWidget):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiProjectView")
self.projBar.updateTheme()
def initSettings(self) -> None:
@@ -346,6 +347,8 @@ class GuiProjectToolBar(QWidget):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiProjectToolBar")
buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
self.tbQuick.setStyleSheet(buttonStyle)
self.tbMoveU.setStyleSheet(buttonStyle)
+2
View File
@@ -158,6 +158,8 @@ class GuiProjectSearch(QWidget):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiProjectSearch")
palette = QApplication.palette()
colBase = palette.base().color().name(QtHexArgb)
colFocus = palette.highlight().color().name(QtHexArgb)
+2 -1
View File
@@ -129,8 +129,9 @@ class GuiSideBar(QWidget):
def updateTheme(self) -> None:
"""Initialise GUI elements that depend on specific settings."""
buttonStyle = SHARED.theme.getStyleSheet(STYLES_BIG_TOOLBUTTON)
logger.debug("Theme Update: GuiSideBar")
buttonStyle = SHARED.theme.getStyleSheet(STYLES_BIG_TOOLBUTTON)
self.tbProject.setStyleSheet(buttonStyle)
self.tbNovel.setStyleSheet(buttonStyle)
self.tbSearch.setStyleSheet(buttonStyle)
+2
View File
@@ -133,6 +133,8 @@ class GuiMainStatus(QStatusBar):
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiMainStatus")
iPx = SHARED.theme.baseIconHeight
self.langIcon.setPixmap(SHARED.theme.getPixmap("language", (iPx, iPx)))
self.statsIcon.setPixmap(SHARED.theme.getPixmap("stats", (iPx, iPx)))
+3
View File
@@ -910,6 +910,9 @@ class GuiMain(QMainWindow):
self.mainStatus.updateTheme()
SHARED.project.tree.refreshAllItems()
if dialog := SHARED.findTopLevelWidget(GuiManuscript):
dialog.updateTheme()
if syntax:
self.docEditor.updateSyntaxColors()
+70 -23
View File
@@ -101,30 +101,20 @@ class GuiManuscript(NToolDialog):
# Build Controls
# ==============
qPalette = self.palette()
qPalette.setBrush(QPalette.ColorRole.Window, qPalette.base())
self.setPalette(qPalette)
buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
self.tbAdd = NIconToolButton(self, iSz, "add", "green")
self.tbAdd = NIconToolButton(self, iSz)
self.tbAdd.setToolTip(self.tr("Add New Build"))
self.tbAdd.setStyleSheet(buttonStyle)
self.tbAdd.clicked.connect(self._createNewBuild)
self.tbDel = NIconToolButton(self, iSz, "remove", "red")
self.tbDel = NIconToolButton(self, iSz)
self.tbDel.setToolTip(self.tr("Delete Selected Build"))
self.tbDel.setStyleSheet(buttonStyle)
self.tbDel.clicked.connect(self._deleteSelectedBuild)
self.tbCopy = NIconToolButton(self, iSz, "copy", "blue")
self.tbCopy = NIconToolButton(self, iSz)
self.tbCopy.setToolTip(self.tr("Duplicate Selected Build"))
self.tbCopy.setStyleSheet(buttonStyle)
self.tbCopy.clicked.connect(self._copySelectedBuild)
self.tbEdit = NIconToolButton(self, iSz, "edit", "green")
self.tbEdit = NIconToolButton(self, iSz)
self.tbEdit.setToolTip(self.tr("Edit Selected Build"))
self.tbEdit.setStyleSheet(buttonStyle)
self.tbEdit.clicked.connect(self._editSelectedBuild)
self.lblBuilds = QLabel("<b>{0}</b>".format(self.tr("Builds")), self)
@@ -159,7 +149,6 @@ class GuiManuscript(NToolDialog):
self.detailsTabs = QTabWidget(self)
self.detailsTabs.addTab(self.buildDetails, self.tr("Details"))
self.detailsTabs.addTab(self.buildOutline, self.tr("Outline"))
self.detailsTabs.setStyleSheet(SHARED.theme.getStyleSheet(STYLES_FLAT_TABS))
self.buildSplit = QSplitter(Qt.Orientation.Vertical, self)
self.buildSplit.addWidget(self.buildList)
@@ -247,6 +236,8 @@ class GuiManuscript(NToolDialog):
self.setLayout(self.outerBox)
self.setSizeGripEnabled(True)
self.updateTheme(init=True)
# Signals
self.buildOutline.outlineEntryClicked.connect(self.docPreview.navigateTo)
@@ -270,6 +261,37 @@ class GuiManuscript(NToolDialog):
self.buildList.setCurrentItem(self._buildMap[selected])
QTimer.singleShot(200, self._generatePreview)
def updateTheme(self, *, init: bool = False) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiManuscript, init=%s", init)
if not init:
self.btnPreview.updateIcon()
self.btnPrint.updateIcon()
self.btnBuild.updateIcon()
self.btnClose.updateIcon()
self.tbAdd.setThemeIcon("add", "green")
self.tbDel.setThemeIcon("remove", "red")
self.tbCopy.setThemeIcon("copy", "blue")
self.tbEdit.setThemeIcon("edit", "green")
buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
self.tbAdd.setStyleSheet(buttonStyle)
self.tbDel.setStyleSheet(buttonStyle)
self.tbCopy.setStyleSheet(buttonStyle)
self.tbEdit.setStyleSheet(buttonStyle)
self.detailsTabs.setStyleSheet(SHARED.theme.getStyleSheet(STYLES_FLAT_TABS))
self.buildDetails.updateTheme()
self.buildOutline.updateTheme()
self.docPreview.updateTheme()
for obj in SHARED.mainGui.children():
if isinstance(obj, GuiBuildSettings):
obj.updateTheme()
##
# Events
##
@@ -490,6 +512,7 @@ class _DetailsWidget(QWidget):
super().__init__(parent=parent)
self._initExpanded = True
self._build = None
# Tree Widget
self.listView = QTreeWidget(self)
@@ -612,9 +635,17 @@ class _DetailsWidget(QWidget):
sub.setIcon(1, on if build.getBool(key) else off)
item.addChild(sub)
self._build = build
# Restore expanded state
self.setExpandedState(expanded)
def updateTheme(self) -> None:
"""Update theme elements."""
if self._build:
logger.debug("Theme Update: _DetailsWidget")
self.updateInfo(self._build)
class _OutlineWidget(QWidget):
@@ -639,9 +670,9 @@ class _OutlineWidget(QWidget):
self.outerBox.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.outerBox)
def updateOutline(self, data: dict[str, str]) -> None:
def updateOutline(self, data: dict[str, str], *, force: bool = False) -> None:
"""Update the outline."""
if isinstance(data, dict) and data != self._outline:
if isinstance(data, dict) and (data != self._outline or force):
self.listView.clear()
tFont = self.font()
@@ -679,6 +710,11 @@ class _OutlineWidget(QWidget):
self.listView.setIndentation(SHARED.theme.baseIconHeight if indent else 4)
self._outline = data
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: _OutlineWidget")
self.updateOutline(self._outline, force=True)
##
# Private Slots
##
@@ -721,17 +757,12 @@ class _PreviewWidget(QTextBrowser):
self.anchorClicked.connect(self._linkClicked)
# Document Age
aPalette = self.palette()
aPalette.setColor(QPalette.ColorRole.Window, aPalette.toolTipBase().color())
aPalette.setColor(QPalette.ColorRole.WindowText, aPalette.toolTipText().color())
aFont = self.font()
aFont.setPointSizeF(0.9*SHARED.theme.fontPointSize)
self.ageLabel = QLabel("", self)
self.ageLabel.setIndent(0)
self.ageLabel.setFont(aFont)
self.ageLabel.setPalette(aPalette)
self.ageLabel.setAutoFillBackground(True)
self.ageLabel.setAlignment(QtAlignCenter)
self.ageLabel.setFixedHeight(int(2.1*SHARED.theme.fontPixelSize))
@@ -750,6 +781,7 @@ class _PreviewWidget(QTextBrowser):
self._updateDocMargins()
self._updateBuildAge()
self.updateTheme()
self.setTextFont(CONFIG.textFont)
# Age Timer
@@ -815,6 +847,15 @@ class _PreviewWidget(QTextBrowser):
QApplication.processEvents()
QTimer.singleShot(300, self._postUpdate)
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: _PreviewWidget")
palette = QApplication.palette()
palette.setColor(QPalette.ColorRole.Window, palette.toolTipBase().color())
palette.setColor(QPalette.ColorRole.WindowText, palette.toolTipText().color())
self.ageLabel.setPalette(palette)
##
# Events
##
@@ -907,7 +948,7 @@ class _StatsWidget(QWidget):
self.minWidget = QWidget(self)
self.maxWidget = QWidget(self)
self.toggleButton = NIconToggleButton(self, SHARED.theme.baseIconSize, "unfold")
self.toggleButton = NIconToggleButton(self, SHARED.theme.baseIconSize)
self.toggleButton.toggled.connect(self._toggleView)
self._buildBottomPanel()
@@ -922,6 +963,7 @@ class _StatsWidget(QWidget):
self.outerBox.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.outerBox)
self.updateTheme()
self._toggleView(False)
@@ -946,6 +988,11 @@ class _StatsWidget(QWidget):
self.maxHeadWordChars.setText(f"{data.get(nwStats.WCHARS_TITLE, 0):n}")
self.maxTextWordChars.setText(f"{data.get(nwStats.WCHARS_TEXT, 0):n}")
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: _StatsWidget")
self.toggleButton.setThemeIcon("unfold")
##
# Private Slots
##
+96 -30
View File
@@ -154,6 +154,7 @@ class GuiBuildSettings(NToolDialog):
self.outerBox.setSpacing(12)
self.setLayout(self.outerBox)
self.updateTheme(init=True)
# Set Default Tab
self.sidebar.setSelected(self.OPT_FILTERS)
@@ -170,6 +171,22 @@ class GuiBuildSettings(NToolDialog):
self.optTabHeadings.loadContent()
self.optTabFormatting.loadContent()
def updateTheme(self, *, init: bool = False) -> None:
"""Update theme elements."""
logger.debug("Theme Update: GuiBuildSettings, init=%s", init)
if not init:
self.btnApply.updateIcon()
self.btnSave.updateIcon()
self.btnClose.updateIcon()
self.optTabSelect.updateTheme()
self.optTabHeadings.updateTheme()
self.optTabFormatting.updateTheme()
self.titleLabel.setTextColors(color=SHARED.theme.helpText)
self.sidebar.setLabelColor(SHARED.theme.helpText)
##
# Properties
##
@@ -326,15 +343,13 @@ class _FilterTab(NFixedPage):
self.includedButton = NIconToolButton(self, iSz)
self.includedButton.setToolTip(self.tr("Always included"))
self.includedButton.setIcon(self._statusFlags[self.F_INCLUDED])
self.includedButton.clicked.connect(qtLambda(self._setSelectedMode, self.F_INCLUDED))
self.excludedButton = NIconToolButton(self, iSz)
self.excludedButton.setToolTip(self.tr("Always excluded"))
self.excludedButton.setIcon(self._statusFlags[self.F_EXCLUDED])
self.excludedButton.clicked.connect(qtLambda(self._setSelectedMode, self.F_EXCLUDED))
self.resetButton = NIconToolButton(self, iSz, "revert", "green")
self.resetButton = NIconToolButton(self, iSz)
self.resetButton.setToolTip(self.tr("Reset to default"))
self.resetButton.clicked.connect(qtLambda(self._setSelectedMode, self.F_FILTERED))
@@ -376,6 +391,7 @@ class _FilterTab(NFixedPage):
pOptions.getInt("GuiBuildSettings", "filterWidth", 300),
])
self.updateTheme(init=True)
self.setCentralWidget(self.mainSplit)
def loadContent(self) -> None:
@@ -389,6 +405,20 @@ class _FilterTab(NFixedPage):
m, n = (sizes[0], sizes[1]) if len(sizes) >= 2 else (0, 0)
return m, n
def updateTheme(self, *, init: bool = False) -> None:
"""Update theme elements."""
logger.debug("Theme Update: _FilterTab, init=%s", init)
if not init:
self._statusFlags[self.F_FILTERED] = SHARED.theme.getIcon("filter", "orange")
self._statusFlags[self.F_INCLUDED] = SHARED.theme.getIcon("pin", "blue")
self._statusFlags[self.F_EXCLUDED] = SHARED.theme.getIcon("exclude", "red")
self.loadContent()
self.includedButton.setIcon(self._statusFlags[self.F_INCLUDED])
self.excludedButton.setIcon(self._statusFlags[self.F_EXCLUDED])
self.resetButton.setThemeIcon("revert", "green")
##
# Slots
##
@@ -555,7 +585,7 @@ class _HeadingsTab(NScrollablePage):
self.lblPart = QLabel(self._build.getLabel("headings.fmtPart"), self)
self.fmtPart = QLineEdit("", self)
self.fmtPart.setReadOnly(True)
self.btnPart = NIconToolButton(self, iSz, "edit", "green")
self.btnPart = NIconToolButton(self, iSz)
self.btnPart.clicked.connect(qtLambda(self._editHeading, self.EDIT_TITLE))
self.swtPart = NSwitch(self, height=iPx)
self.hdePart = QLabel(trHide, self)
@@ -572,7 +602,7 @@ class _HeadingsTab(NScrollablePage):
self.lblChapter = QLabel(self._build.getLabel("headings.fmtChapter"), self)
self.fmtChapter = QLineEdit("", self)
self.fmtChapter.setReadOnly(True)
self.btnChapter = NIconToolButton(self, iSz, "edit", "green")
self.btnChapter = NIconToolButton(self, iSz)
self.btnChapter.clicked.connect(qtLambda(self._editHeading, self.EDIT_CHAPTER))
self.swtChapter = NSwitch(self, height=iPx)
self.hdeChapter = QLabel(trHide, self)
@@ -589,7 +619,7 @@ class _HeadingsTab(NScrollablePage):
self.lblUnnumbered = QLabel(self._build.getLabel("headings.fmtUnnumbered"), self)
self.fmtUnnumbered = QLineEdit("", self)
self.fmtUnnumbered.setReadOnly(True)
self.btnUnnumbered = NIconToolButton(self, iSz, "edit", "green")
self.btnUnnumbered = NIconToolButton(self, iSz)
self.btnUnnumbered.clicked.connect(qtLambda(self._editHeading, self.EDIT_UNNUM))
self.swtUnnumbered = NSwitch(self, height=iPx)
self.hdeUnnumbered = QLabel(trHide, self)
@@ -606,7 +636,7 @@ class _HeadingsTab(NScrollablePage):
self.lblScene = QLabel(self._build.getLabel("headings.fmtScene"), self)
self.fmtScene = QLineEdit("", self)
self.fmtScene.setReadOnly(True)
self.btnScene = NIconToolButton(self, iSz, "edit", "green")
self.btnScene = NIconToolButton(self, iSz)
self.btnScene.clicked.connect(qtLambda(self._editHeading, self.EDIT_SCENE))
self.swtScene = NSwitch(self, height=iPx)
self.hdeScene = QLabel(trHide, self)
@@ -623,7 +653,7 @@ class _HeadingsTab(NScrollablePage):
self.lblAScene = QLabel(self._build.getLabel("headings.fmtAltScene"), self)
self.fmtAScene = QLineEdit("", self)
self.fmtAScene.setReadOnly(True)
self.btnAScene = NIconToolButton(self, iSz, "edit", "green")
self.btnAScene = NIconToolButton(self, iSz)
self.btnAScene.clicked.connect(qtLambda(self._editHeading, self.EDIT_HSCENE))
self.swtAScene = NSwitch(self, height=iPx)
self.hdeAScene = QLabel(trHide, self)
@@ -640,7 +670,7 @@ class _HeadingsTab(NScrollablePage):
self.lblSection = QLabel(self._build.getLabel("headings.fmtSection"), self)
self.fmtSection = QLineEdit("", self)
self.fmtSection.setReadOnly(True)
self.btnSection = NIconToolButton(self, iSz, "edit", "green")
self.btnSection = NIconToolButton(self, iSz)
self.btnSection.clicked.connect(qtLambda(self._editHeading, self.EDIT_SECTION))
self.swtSection = NSwitch(self, height=iPx)
self.hdeSection = QLabel(trHide, self)
@@ -774,8 +804,23 @@ class _HeadingsTab(NScrollablePage):
self.outerBox.addLayout(self.layoutMatrix)
self.outerBox.addStretch(1)
self.updateTheme()
self.setCentralLayout(self.outerBox)
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: _HeadingsTab")
self.btnPart.setThemeIcon("edit", "green")
self.btnChapter.setThemeIcon("edit", "green")
self.btnUnnumbered.setThemeIcon("edit", "green")
self.btnScene.setThemeIcon("edit", "green")
self.btnAScene.setThemeIcon("edit", "green")
self.btnSection.setThemeIcon("edit", "green")
self.formSyntax.initHighlighter()
self.formSyntax.rehighlight()
def loadContent(self) -> None:
"""Populate the widgets."""
def fmtBreak(text: str) -> str:
@@ -907,10 +952,14 @@ class _HeadingSyntaxHighlighter(QSyntaxHighlighter):
def __init__(self, document: QTextDocument | None) -> None:
super().__init__(document)
syntax = SHARED.theme.syntaxTheme
self._fmtSymbol = QTextCharFormat()
self._fmtSymbol.setForeground(syntax.head)
self._fmtFormat = QTextCharFormat()
self.initHighlighter()
def initHighlighter(self) -> None:
"""Update theme elements."""
syntax = SHARED.theme.syntaxTheme
self._fmtSymbol.setForeground(syntax.head)
self._fmtFormat.setForeground(syntax.emph)
def highlightBlock(self, text: str) -> None:
@@ -936,6 +985,7 @@ class _FormattingTab(NScrollableForm):
self.setHelpTextStyle(SHARED.theme.helpText)
self.buildForm()
self.updateTheme()
def buildForm(self) -> None:
"""Build the formatting form."""
@@ -979,7 +1029,7 @@ class _FormattingTab(NScrollableForm):
lambda keyword=keyword: self._updateIgnoredKeywords(keyword)
)
self.ignoredKeywordsButton = NIconToolButton(self, iSz, "add", "green")
self.ignoredKeywordsButton = NIconToolButton(self, iSz)
self.ignoredKeywordsButton.setMenu(self.mnKeywords)
self.addRow(
self._build.getLabel("text.ignoredKeywords"), self.ignoredKeywords,
@@ -1001,7 +1051,7 @@ class _FormattingTab(NScrollableForm):
# Text Font
self.textFont = QLineEdit(self)
self.textFont.setReadOnly(True)
self.btnTextFont = NIconToolButton(self, iSz, "font")
self.btnTextFont = NIconToolButton(self, iSz)
self.btnTextFont.clicked.connect(self._selectFont)
self.addRow(
self._build.getLabel("format.textFont"), self.textFont,
@@ -1060,12 +1110,12 @@ class _FormattingTab(NScrollableForm):
self._sidebar.addButton(title, section)
self.addGroupLabel(title, section)
pixT = SHARED.theme.getPixmap("margin_top", (iPx, iPx))
pixB = SHARED.theme.getPixmap("margin_bottom", (iPx, iPx))
pixL = SHARED.theme.getPixmap("margin_left", (iPx, iPx))
pixR = SHARED.theme.getPixmap("margin_right", (iPx, iPx))
pixH = SHARED.theme.getPixmap("fit_height", (iPx, iPx))
pixW = SHARED.theme.getPixmap("fit_width", (iPx, iPx))
self.pixT = QLabel(self)
self.pixB = QLabel(self)
self.pixL = QLabel(self)
self.pixR = QLabel(self)
self.pixH = QLabel(self)
self.pixW = QLabel(self)
# Title
self.titleMarginT = NDoubleSpinBox(self)
@@ -1076,7 +1126,7 @@ class _FormattingTab(NScrollableForm):
self.addRow(
self._build.getLabel("format.titleMargin"),
[pixT, self.titleMarginT, 6, pixB, self.titleMarginB],
[self.pixT, self.titleMarginT, 6, self.pixB, self.titleMarginB],
unit="em",
)
@@ -1089,7 +1139,7 @@ class _FormattingTab(NScrollableForm):
self.addRow(
self._build.getLabel("format.h1Margin"),
[pixT, self.h1MarginT, 6, pixB, self.h1MarginB],
[self.pixT, self.h1MarginT, 6, self.pixB, self.h1MarginB],
unit="em",
)
@@ -1102,7 +1152,7 @@ class _FormattingTab(NScrollableForm):
self.addRow(
self._build.getLabel("format.h2Margin"),
[pixT, self.h2MarginT, 6, pixB, self.h2MarginB],
[self.pixT, self.h2MarginT, 6, self.pixB, self.h2MarginB],
unit="em",
)
@@ -1115,7 +1165,7 @@ class _FormattingTab(NScrollableForm):
self.addRow(
self._build.getLabel("format.h3Margin"),
[pixT, self.h3MarginT, 6, pixB, self.h3MarginB],
[self.pixT, self.h3MarginT, 6, self.pixB, self.h3MarginB],
unit="em",
)
@@ -1128,7 +1178,7 @@ class _FormattingTab(NScrollableForm):
self.addRow(
self._build.getLabel("format.h4Margin"),
[pixT, self.h4MarginT, 6, pixB, self.h4MarginB],
[self.pixT, self.h4MarginT, 6, self.pixB, self.h4MarginB],
unit="em",
)
@@ -1141,7 +1191,7 @@ class _FormattingTab(NScrollableForm):
self.addRow(
self._build.getLabel("format.textMargin"),
[pixT, self.textMarginT, 6, pixB, self.textMarginB],
[self.pixT, self.textMarginT, 6, self.pixB, self.textMarginB],
unit="em",
)
@@ -1154,7 +1204,7 @@ class _FormattingTab(NScrollableForm):
self.addRow(
self._build.getLabel("format.sepMargin"),
[pixT, self.sepMarginT, 6, pixB, self.sepMarginB],
[self.pixT, self.sepMarginT, 6, self.pixB, self.sepMarginB],
unit="em",
)
@@ -1188,7 +1238,7 @@ class _FormattingTab(NScrollableForm):
self.addRow(
self._build.getLabel("format.pageSize"),
[self.pageSize, 6, pixW, self.pageWidth, 6, pixH, self.pageHeight],
[self.pageSize, 6, self.pixW, self.pageWidth, 6, self.pixH, self.pageHeight],
)
# Page Margins
@@ -1206,11 +1256,11 @@ class _FormattingTab(NScrollableForm):
self.addRow(
self._build.getLabel("format.pageMargins"),
[pixT, self.topMargin, 6, pixB, self.bottomMargin],
[self.pixT, self.topMargin, 6, self.pixB, self.bottomMargin],
)
self.addRow(
"",
[pixL, self.leftMargin, 6, pixR, self.rightMargin],
[self.pixL, self.leftMargin, 6, self.pixR, self.rightMargin],
)
# Open Document
@@ -1224,7 +1274,7 @@ class _FormattingTab(NScrollableForm):
# Header
self.odtPageHeader = QLineEdit(self)
self.odtPageHeader.setMinimumWidth(200)
self.btnPageHeader = NIconToolButton(self, iSz, "revert", "green")
self.btnPageHeader = NIconToolButton(self, iSz)
self.btnPageHeader.clicked.connect(self._resetPageHeader)
self.addRow(
self._build.getLabel("doc.pageHeader"), self.odtPageHeader,
@@ -1264,6 +1314,22 @@ class _FormattingTab(NScrollableForm):
# Finalise
self.finalise()
def updateTheme(self) -> None:
"""Update theme elements."""
logger.debug("Theme Update: _FormattingTab")
self.ignoredKeywordsButton.setThemeIcon("add", "green")
self.btnTextFont.setThemeIcon("font")
self.btnPageHeader.setThemeIcon("revert", "green")
iPx = SHARED.theme.baseIconHeight
self.pixT.setPixmap(SHARED.theme.getPixmap("margin_top", (iPx, iPx)))
self.pixB.setPixmap(SHARED.theme.getPixmap("margin_bottom", (iPx, iPx)))
self.pixL.setPixmap(SHARED.theme.getPixmap("margin_left", (iPx, iPx)))
self.pixR.setPixmap(SHARED.theme.getPixmap("margin_right", (iPx, iPx)))
self.pixH.setPixmap(SHARED.theme.getPixmap("fit_height", (iPx, iPx)))
self.pixW.setPixmap(SHARED.theme.getPixmap("fit_width", (iPx, iPx)))
def loadContent(self) -> None:
"""Populate the widgets."""
# Text Content
+24 -3
View File
@@ -22,12 +22,13 @@ from __future__ import annotations
import pytest
from PyQt6.QtCore import QEvent, QPoint, QPointF, Qt
from PyQt6.QtCore import QEvent, QPoint, QPointF, QSize, Qt
from PyQt6.QtGui import QKeyEvent, QMouseEvent, QStandardItem, QStandardItemModel, QWheelEvent
from PyQt6.QtWidgets import QWidget
from novelwriter.extensions.modified import (
NClickableLabel, NComboBox, NDialog, NDoubleSpinBox, NSpinBox, NTreeView
NClickableLabel, NComboBox, NDialog, NDoubleSpinBox, NIconToggleButton,
NIconToolButton, NSpinBox, NTreeView
)
from novelwriter.types import QtModNone, QtMouseLeft, QtMouseMiddle, QtRejected
@@ -168,7 +169,7 @@ def testExtModified_NDoubleSpinBox(qtbot, monkeypatch):
@pytest.mark.gui
def testExtModified_NClickableLabel(qtbot, monkeypatch):
def testExtModified_NClickableLabel(qtbot):
"""Test the NClickableLabel class."""
widget = NClickableLabel()
dialog = SimpleDialog(widget)
@@ -181,3 +182,23 @@ def testExtModified_NClickableLabel(qtbot, monkeypatch):
with qtbot.waitSignal(widget.mouseClicked):
widget.mousePressEvent(event)
@pytest.mark.gui
def testExtModified_ToolButtons(qtbot):
"""Test the NIconToolButton and NIconToggleButton classes."""
dialog = SimpleDialog(None)
size = QSize(16, 16)
button1 = NIconToolButton(dialog, size, "add", "green")
button2 = NIconToggleButton(dialog, size, "bullet")
assert button1.iconSize() == size
assert button2.iconSize() == size
assert button1.icon().isNull() is False
assert button2.icon().isNull() is False
dialog.addWidget(button1)
dialog.addWidget(button2)
dialog.show()
@@ -72,6 +72,9 @@ def testToolManuscript_Init(monkeypatch, qtbot, nwGUI, projPath, mockRnd):
manus.btnPreview.click()
assert manus.docPreview.toPlainText().strip() == allText
# Trigger a theme update, which is only a visual refresh, but it shouldn't crash
manus.updateTheme()
nwGUI.closeProject() # This should auto-close the manuscript tool
# qtbot.stop()
@@ -148,6 +151,9 @@ def testToolManuscript_Builds(qtbot, nwGUI, projPath):
assert new is not None
assert new.name == "Test Build 2"
# Trigger a theme update, which should propagate to settings
nwGUI.refreshThemeColors()
# Close the dialog should also close the child dialogs
manus.btnClose.click()
if isinstance(bSettings, GuiBuildSettings):