Update buttons and manuscript build (#2553)

This commit is contained in:
Veronica Berglyd Olsen
2025-10-27 21:12:59 +01:00
committed by GitHub
8 changed files with 80 additions and 40 deletions
+1 -1
View File
@@ -56,7 +56,7 @@ VALID_MAP: dict[str, set[str]] = {
"GuiWordList": {"winWidth", "winHeight"}, "GuiWordList": {"winWidth", "winHeight"},
"GuiNovelView": {"lastCol", "lastColSize"}, "GuiNovelView": {"lastCol", "lastColSize"},
"GuiBuildSettings": { "GuiBuildSettings": {
"winWidth", "winHeight", "treeWidth", "filterWidth", "winWidth", "winHeight", "treeWidth", "filterWidth", "autoPreview",
}, },
"GuiManuscript": { "GuiManuscript": {
"winWidth", "winHeight", "optsWidth", "viewWidth", "listHeight", "winWidth", "winHeight", "optsWidth", "viewWidth", "listHeight",
+5 -5
View File
@@ -158,7 +158,7 @@ class GuiPreferences(NDialog):
self.mainForm.addGroupLabel(title, section) self.mainForm.addGroupLabel(title, section)
# Display Language # Display Language
self.guiLocale = NComboBox(self, scrollable=True) self.guiLocale = NComboBox(self)
self.guiLocale.setMinimumWidth(200) self.guiLocale.setMinimumWidth(200)
for lang, name in CONFIG.listLanguages(CONFIG.LANG_NW): for lang, name in CONFIG.listLanguages(CONFIG.LANG_NW):
self.guiLocale.addItem(name, lang) self.guiLocale.addItem(name, lang)
@@ -170,9 +170,9 @@ class GuiPreferences(NDialog):
) )
# Colour Theme # Colour Theme
self.lightTheme = NComboBox(self, scrollable=True) self.lightTheme = NComboBox(self)
self.lightTheme.setMinimumWidth(200) self.lightTheme.setMinimumWidth(200)
self.darkTheme = NComboBox(self, scrollable=True) self.darkTheme = NComboBox(self)
self.darkTheme.setMinimumWidth(200) self.darkTheme.setMinimumWidth(200)
for key, theme in SHARED.theme.colourThemes.items(): for key, theme in SHARED.theme.colourThemes.items():
if theme.dark: if theme.dark:
@@ -193,7 +193,7 @@ class GuiPreferences(NDialog):
) )
# Icon Theme # Icon Theme
self.iconTheme = NComboBox(self, scrollable=True) self.iconTheme = NComboBox(self)
self.iconTheme.setMinimumWidth(200) self.iconTheme.setMinimumWidth(200)
for key, theme in SHARED.theme.iconCache.iconThemes.items(): for key, theme in SHARED.theme.iconCache.iconThemes.items():
self.iconTheme.addItem(theme.name, key) self.iconTheme.addItem(theme.name, key)
@@ -513,7 +513,7 @@ class GuiPreferences(NDialog):
self.mainForm.addGroupLabel(title, section) self.mainForm.addGroupLabel(title, section)
# Spell Checking # Spell Checking
self.spellLanguage = NComboBox(self, scrollable=True) self.spellLanguage = NComboBox(self)
self.spellLanguage.setMinimumWidth(200) self.spellLanguage.setMinimumWidth(200)
if CONFIG.hasEnchant: if CONFIG.hasEnchant:
+2 -2
View File
@@ -257,7 +257,7 @@ class _SettingsPage(NScrollableForm):
# Project Language # Project Language
projLang = data.language or CONFIG.guiLocale projLang = data.language or CONFIG.guiLocale
self.projLang = NComboBox(self, scrollable=True) self.projLang = NComboBox(self)
self.projLang.setMinimumWidth(200) self.projLang.setMinimumWidth(200)
for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ): for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ):
self.projLang.addItem(language, tag) self.projLang.addItem(language, tag)
@@ -269,7 +269,7 @@ class _SettingsPage(NScrollableForm):
) )
# Spell Check Language # Spell Check Language
self.spellLang = NComboBox(self, scrollable=True) self.spellLang = NComboBox(self)
self.spellLang.setMinimumWidth(200) self.spellLang.setMinimumWidth(200)
self.spellLang.addItem(self.tr("Default"), "None") self.spellLang.addItem(self.tr("Default"), "None")
if CONFIG.hasEnchant: if CONFIG.hasEnchant:
+8 -7
View File
@@ -125,16 +125,11 @@ class NComboBox(QComboBox):
window of many widgets. window of many widgets.
""" """
def __init__( def __init__(self, parent: QWidget | None = None, maxItems: int = 15) -> None:
self, parent: QWidget | None = None, maxItems: int = 15, scrollable: bool = False
) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
self.setMaxVisibleItems(maxItems) self.setMaxVisibleItems(maxItems)
if scrollable: self.updateStyle()
# 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: def wheelEvent(self, event: QWheelEvent) -> None:
"""Only capture the mouse wheel if the widget has focus.""" """Only capture the mouse wheel if the widget has focus."""
@@ -148,6 +143,12 @@ class NComboBox(QComboBox):
idx = self.findData(data) idx = self.findData(data)
self.setCurrentIndex(self.findData(default) if idx < 0 else idx) self.setCurrentIndex(self.findData(default) if idx < 0 else idx)
def updateStyle(self) -> None:
"""Update the style sheet."""
# 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;}")
class NSpinBox(QSpinBox): class NSpinBox(QSpinBox):
"""Custom: Modified QSpinBox. """Custom: Modified QSpinBox.
+17 -14
View File
@@ -92,10 +92,10 @@ class GuiManuscript(NToolDialog):
iPx = SHARED.theme.baseIconHeight iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize iSz = SHARED.theme.baseIconSize
pOptions = SHARED.project.options options = SHARED.project.options
self.resize( self.resize(
pOptions.getInt("GuiManuscript", "winWidth", 900), options.getInt("GuiManuscript", "winWidth", 900),
pOptions.getInt("GuiManuscript", "winHeight", 600), options.getInt("GuiManuscript", "winHeight", 600),
) )
# Build Controls # Build Controls
@@ -142,7 +142,7 @@ class GuiManuscript(NToolDialog):
# ============ # ============
self.buildDetails = _DetailsWidget(self) self.buildDetails = _DetailsWidget(self)
self.buildDetails.setColumnWidth(pOptions.getInt("GuiManuscript", "detailsWidth", 100)) self.buildDetails.setColumnWidth(options.getInt("GuiManuscript", "detailsWidth", 100))
self.buildOutline = _OutlineWidget(self) self.buildOutline = _OutlineWidget(self)
@@ -154,8 +154,8 @@ class GuiManuscript(NToolDialog):
self.buildSplit.addWidget(self.buildList) self.buildSplit.addWidget(self.buildList)
self.buildSplit.addWidget(self.detailsTabs) self.buildSplit.addWidget(self.detailsTabs)
self.buildSplit.setSizes([ self.buildSplit.setSizes([
pOptions.getInt("GuiManuscript", "listHeight", 50), options.getInt("GuiManuscript", "listHeight", 50),
pOptions.getInt("GuiManuscript", "detailsHeight", 50), options.getInt("GuiManuscript", "detailsHeight", 50),
]) ])
# Process Controls # Process Controls
@@ -183,7 +183,7 @@ class GuiManuscript(NToolDialog):
# =============== # ===============
self.swtNewPage = NSwitch(self, height=iPx) self.swtNewPage = NSwitch(self, height=iPx)
self.swtNewPage.setChecked(pOptions.getBool("GuiManuscript", "showNewPage", True)) self.swtNewPage.setChecked(options.getBool("GuiManuscript", "showNewPage", True))
self.swtNewPage.clicked.connect(self._generatePreview) self.swtNewPage.clicked.connect(self._generatePreview)
self.lblNewPage = QLabel(self.tr("Show Page Breaks"), self) self.lblNewPage = QLabel(self.tr("Show Page Breaks"), self)
@@ -226,8 +226,8 @@ class GuiManuscript(NToolDialog):
self.mainSplit.setStretchFactor(0, 0) self.mainSplit.setStretchFactor(0, 0)
self.mainSplit.setStretchFactor(1, 1) self.mainSplit.setStretchFactor(1, 1)
self.mainSplit.setSizes([ self.mainSplit.setSizes([
pOptions.getInt("GuiManuscript", "optsWidth", 225), options.getInt("GuiManuscript", "optsWidth", 225),
pOptions.getInt("GuiManuscript", "viewWidth", 675), options.getInt("GuiManuscript", "viewWidth", 675),
]) ])
self.outerBox = QVBoxLayout() self.outerBox = QVBoxLayout()
@@ -273,7 +273,7 @@ class GuiManuscript(NToolDialog):
self.tbAdd.setThemeIcon("add", "add") self.tbAdd.setThemeIcon("add", "add")
self.tbDel.setThemeIcon("remove", "remove") self.tbDel.setThemeIcon("remove", "remove")
self.tbCopy.setThemeIcon("copy", "accept") self.tbCopy.setThemeIcon("copy", "action")
self.tbEdit.setThemeIcon("edit", "change") self.tbEdit.setThemeIcon("edit", "change")
buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON) buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
@@ -352,13 +352,16 @@ class GuiManuscript(NToolDialog):
self._builds.removeBuild(build.buildID) self._builds.removeBuild(build.buildID)
self._updateBuildsList() self._updateBuildsList()
@pyqtSlot(BuildSettings) @pyqtSlot(BuildSettings, bool)
def _processNewSettings(self, build: BuildSettings) -> None: def _processNewSettings(self, build: BuildSettings, refreshPreview: bool) -> None:
"""Process new build settings from the settings dialog.""" """Process new build settings from the settings dialog."""
self._builds.setBuild(build) self._builds.setBuild(build)
self._updateBuildItem(build) self._updateBuildItem(build)
if (current := self.buildList.currentItem()) and current.data(self.D_KEY) == build.buildID: if refreshPreview:
self._updateBuildDetails(current, current) self.buildList.setCurrentItem(self._buildMap[build.buildID])
self._generatePreview()
elif (item := self.buildList.currentItem()) and item.data(self.D_KEY) == build.buildID:
self._updateBuildDetails(item, item)
@pyqtSlot() @pyqtSlot()
def _generatePreview(self) -> None: def _generatePreview(self) -> None:
+28 -9
View File
@@ -72,7 +72,7 @@ class GuiBuildSettings(NToolDialog):
OPT_HEADINGS = 2 OPT_HEADINGS = 2
OPT_FORMATTING = 10 OPT_FORMATTING = 10
newSettingsReady = pyqtSignal(BuildSettings) newSettingsReady = pyqtSignal(BuildSettings, bool)
def __init__(self, parent: GuiMain, build: BuildSettings) -> None: def __init__(self, parent: GuiMain, build: BuildSettings) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
@@ -86,6 +86,7 @@ class GuiBuildSettings(NToolDialog):
self.setWindowTitle(self.tr("Manuscript Build Settings")) self.setWindowTitle(self.tr("Manuscript Build Settings"))
self.setMinimumSize(700, 400) self.setMinimumSize(700, 400)
iPx = SHARED.theme.baseIconHeight
options = SHARED.project.options options = SHARED.project.options
self.resize( self.resize(
options.getInt("GuiBuildSettings", "winWidth", 750), options.getInt("GuiBuildSettings", "winWidth", 750),
@@ -124,6 +125,13 @@ class GuiBuildSettings(NToolDialog):
self.toolStack.addWidget(self.optTabHeadings) self.toolStack.addWidget(self.optTabHeadings)
self.toolStack.addWidget(self.optTabFormatting) self.toolStack.addWidget(self.optTabFormatting)
# Preview
self.swtAutoPreview = NSwitch(self, height=iPx)
self.swtAutoPreview.setChecked(options.getBool("GuiBuildSettings", "autoPreview", True))
self.lblAutoPreview = QLabel(self.tr("Auro-Update Preview"), self)
self.lblAutoPreview.setBuddy(self.swtAutoPreview)
# Buttons # Buttons
self.btnApply = SHARED.theme.getStandardButton(nwStandardButton.APPLY, self) self.btnApply = SHARED.theme.getStandardButton(nwStandardButton.APPLY, self)
self.btnSave = SHARED.theme.getStandardButton(nwStandardButton.SAVE, self) self.btnSave = SHARED.theme.getStandardButton(nwStandardButton.SAVE, self)
@@ -147,10 +155,17 @@ class GuiBuildSettings(NToolDialog):
self.mainBox.addWidget(self.toolStack) self.mainBox.addWidget(self.toolStack)
self.mainBox.setContentsMargins(0, 0, 0, 0) self.mainBox.setContentsMargins(0, 0, 0, 0)
self.bottomBox = QHBoxLayout()
self.bottomBox.addWidget(self.lblAutoPreview, 0)
self.bottomBox.addWidget(self.swtAutoPreview, 0)
self.bottomBox.addSpacing(8)
self.bottomBox.addWidget(self.btnBox, 1)
self.bottomBox.setContentsMargins(0, 0, 0, 0)
self.outerBox = QVBoxLayout() self.outerBox = QVBoxLayout()
self.outerBox.addLayout(self.topBox) self.outerBox.addLayout(self.topBox)
self.outerBox.addLayout(self.mainBox) self.outerBox.addLayout(self.mainBox)
self.outerBox.addWidget(self.btnBox) self.outerBox.addLayout(self.bottomBox)
self.outerBox.setSpacing(12) self.outerBox.setSpacing(12)
self.setLayout(self.outerBox) self.setLayout(self.outerBox)
@@ -259,12 +274,13 @@ class GuiBuildSettings(NToolDialog):
"""Save the various user settings.""" """Save the various user settings."""
treeWidth, filterWidth = self.optTabSelect.mainSplitSizes() treeWidth, filterWidth = self.optTabSelect.mainSplitSizes()
logger.debug("Saving State: GuiBuildSettings") logger.debug("Saving State: GuiBuildSettings")
pOptions = SHARED.project.options options = SHARED.project.options
pOptions.setValue("GuiBuildSettings", "winWidth", self.width()) options.setValue("GuiBuildSettings", "winWidth", self.width())
pOptions.setValue("GuiBuildSettings", "winHeight", self.height()) options.setValue("GuiBuildSettings", "winHeight", self.height())
pOptions.setValue("GuiBuildSettings", "treeWidth", treeWidth) options.setValue("GuiBuildSettings", "treeWidth", treeWidth)
pOptions.setValue("GuiBuildSettings", "filterWidth", filterWidth) options.setValue("GuiBuildSettings", "filterWidth", filterWidth)
pOptions.saveSettings() options.setValue("GuiBuildSettings", "autoPreview", self.swtAutoPreview.isChecked())
options.saveSettings()
def _applyChanges(self) -> None: def _applyChanges(self) -> None:
"""Apply all settings changes to the build object.""" """Apply all settings changes to the build object."""
@@ -274,7 +290,7 @@ class GuiBuildSettings(NToolDialog):
def _emitBuildData(self) -> None: def _emitBuildData(self) -> None:
"""Assemble the build data and emit the signal.""" """Assemble the build data and emit the signal."""
self.newSettingsReady.emit(self._build) self.newSettingsReady.emit(self._build, self.swtAutoPreview.isChecked())
self._build.resetChangedState() self._build.resetChangedState()
@@ -1329,6 +1345,9 @@ class _FormattingTab(NScrollableForm):
self.pixH.setPixmap(SHARED.theme.getPixmap("fit_height", (iPx, iPx))) self.pixH.setPixmap(SHARED.theme.getPixmap("fit_height", (iPx, iPx)))
self.pixW.setPixmap(SHARED.theme.getPixmap("fit_width", (iPx, iPx))) self.pixW.setPixmap(SHARED.theme.getPixmap("fit_width", (iPx, iPx)))
self.pageSize.updateStyle()
self.pageUnit.updateStyle()
def loadContent(self) -> None: def loadContent(self) -> None:
"""Populate the widgets.""" """Populate the widgets."""
# Text Content # Text Content
+18 -1
View File
@@ -108,10 +108,11 @@ def testToolManuscript_Builds(qtbot, nwGUI, projPath):
bSettings = SHARED.findTopLevelWidget(GuiBuildSettings) bSettings = SHARED.findTopLevelWidget(GuiBuildSettings)
assert isinstance(bSettings, GuiBuildSettings) assert isinstance(bSettings, GuiBuildSettings)
bSettings.editBuildName.setText("Test Build") bSettings.editBuildName.setText("Test Build")
bSettings.swtAutoPreview.setChecked(False)
build = None build = None
@pyqtSlot(BuildSettings) @pyqtSlot(BuildSettings)
def _testNewSettingsReady(new: BuildSettings): def _testNewSettingsReady(new: BuildSettings, refresh: bool):
nonlocal build nonlocal build
build = new build = new
@@ -151,6 +152,22 @@ def testToolManuscript_Builds(qtbot, nwGUI, projPath):
assert new is not None assert new is not None
assert new.name == "Test Build 2" assert new.name == "Test Build 2"
# Processing first build with refresh should change selection
manus.docPreview._docTime = 0
manus._processNewSettings(build, True)
assert manus.docPreview._docTime > 0 # Refreshed
current = manus._getSelectedBuild()
assert current is not None
assert current.name == "Test Build"
# Processing new build without refresh should keep selection
manus.docPreview._docTime = 0
manus._processNewSettings(new, False)
assert manus.docPreview._docTime == 0 # No refresh
current = manus._getSelectedBuild()
assert current is not None
assert current.name == "Test Build"
# Trigger a theme update, which should propagate to settings # Trigger a theme update, which should propagate to settings
nwGUI.refreshThemeColors() nwGUI.refreshThemeColors()
+1 -1
View File
@@ -69,7 +69,7 @@ def testToolBuildSettings_Init(qtbot, nwGUI, projPath, mockRnd):
triggered = False triggered = False
@pyqtSlot(BuildSettings) @pyqtSlot(BuildSettings)
def _testNewSettingsReady(new: BuildSettings): def _testNewSettingsReady(new: BuildSettings, refresh: bool):
nonlocal triggered nonlocal triggered
assert new.buildID == build.buildID assert new.buildID == build.buildID
triggered = True triggered = True