From d9d8e54ed301381e8cc2effb9150787aedc63de1 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Mon, 27 Oct 2025 21:02:50 +0100 Subject: [PATCH] Add am auto-update button to manuscript build settings --- novelwriter/core/options.py | 2 +- novelwriter/tools/manuscript.py | 29 +++++++++-------- novelwriter/tools/manussettings.py | 34 ++++++++++++++------ tests/test_tools/test_tools_manuscript.py | 19 ++++++++++- tests/test_tools/test_tools_manussettings.py | 2 +- 5 files changed, 61 insertions(+), 25 deletions(-) diff --git a/novelwriter/core/options.py b/novelwriter/core/options.py index de8c5759..9f78ed7a 100644 --- a/novelwriter/core/options.py +++ b/novelwriter/core/options.py @@ -56,7 +56,7 @@ VALID_MAP: dict[str, set[str]] = { "GuiWordList": {"winWidth", "winHeight"}, "GuiNovelView": {"lastCol", "lastColSize"}, "GuiBuildSettings": { - "winWidth", "winHeight", "treeWidth", "filterWidth", + "winWidth", "winHeight", "treeWidth", "filterWidth", "autoPreview", }, "GuiManuscript": { "winWidth", "winHeight", "optsWidth", "viewWidth", "listHeight", diff --git a/novelwriter/tools/manuscript.py b/novelwriter/tools/manuscript.py index feacf3e1..34e0056c 100644 --- a/novelwriter/tools/manuscript.py +++ b/novelwriter/tools/manuscript.py @@ -92,10 +92,10 @@ class GuiManuscript(NToolDialog): iPx = SHARED.theme.baseIconHeight iSz = SHARED.theme.baseIconSize - pOptions = SHARED.project.options + options = SHARED.project.options self.resize( - pOptions.getInt("GuiManuscript", "winWidth", 900), - pOptions.getInt("GuiManuscript", "winHeight", 600), + options.getInt("GuiManuscript", "winWidth", 900), + options.getInt("GuiManuscript", "winHeight", 600), ) # Build Controls @@ -142,7 +142,7 @@ class GuiManuscript(NToolDialog): # ============ self.buildDetails = _DetailsWidget(self) - self.buildDetails.setColumnWidth(pOptions.getInt("GuiManuscript", "detailsWidth", 100)) + self.buildDetails.setColumnWidth(options.getInt("GuiManuscript", "detailsWidth", 100)) self.buildOutline = _OutlineWidget(self) @@ -154,8 +154,8 @@ class GuiManuscript(NToolDialog): self.buildSplit.addWidget(self.buildList) self.buildSplit.addWidget(self.detailsTabs) self.buildSplit.setSizes([ - pOptions.getInt("GuiManuscript", "listHeight", 50), - pOptions.getInt("GuiManuscript", "detailsHeight", 50), + options.getInt("GuiManuscript", "listHeight", 50), + options.getInt("GuiManuscript", "detailsHeight", 50), ]) # Process Controls @@ -183,7 +183,7 @@ class GuiManuscript(NToolDialog): # =============== 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.lblNewPage = QLabel(self.tr("Show Page Breaks"), self) @@ -226,8 +226,8 @@ class GuiManuscript(NToolDialog): self.mainSplit.setStretchFactor(0, 0) self.mainSplit.setStretchFactor(1, 1) self.mainSplit.setSizes([ - pOptions.getInt("GuiManuscript", "optsWidth", 225), - pOptions.getInt("GuiManuscript", "viewWidth", 675), + options.getInt("GuiManuscript", "optsWidth", 225), + options.getInt("GuiManuscript", "viewWidth", 675), ]) self.outerBox = QVBoxLayout() @@ -352,13 +352,16 @@ class GuiManuscript(NToolDialog): self._builds.removeBuild(build.buildID) self._updateBuildsList() - @pyqtSlot(BuildSettings) - def _processNewSettings(self, build: BuildSettings) -> None: + @pyqtSlot(BuildSettings, bool) + def _processNewSettings(self, build: BuildSettings, refreshPreview: bool) -> None: """Process new build settings from the settings dialog.""" self._builds.setBuild(build) self._updateBuildItem(build) - if (current := self.buildList.currentItem()) and current.data(self.D_KEY) == build.buildID: - self._updateBuildDetails(current, current) + if refreshPreview: + 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() def _generatePreview(self) -> None: diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py index a47b58ff..5808f954 100644 --- a/novelwriter/tools/manussettings.py +++ b/novelwriter/tools/manussettings.py @@ -72,7 +72,7 @@ class GuiBuildSettings(NToolDialog): OPT_HEADINGS = 2 OPT_FORMATTING = 10 - newSettingsReady = pyqtSignal(BuildSettings) + newSettingsReady = pyqtSignal(BuildSettings, bool) def __init__(self, parent: GuiMain, build: BuildSettings) -> None: super().__init__(parent=parent) @@ -86,6 +86,7 @@ class GuiBuildSettings(NToolDialog): self.setWindowTitle(self.tr("Manuscript Build Settings")) self.setMinimumSize(700, 400) + iPx = SHARED.theme.baseIconHeight options = SHARED.project.options self.resize( options.getInt("GuiBuildSettings", "winWidth", 750), @@ -124,6 +125,13 @@ class GuiBuildSettings(NToolDialog): self.toolStack.addWidget(self.optTabHeadings) 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 self.btnApply = SHARED.theme.getStandardButton(nwStandardButton.APPLY, self) self.btnSave = SHARED.theme.getStandardButton(nwStandardButton.SAVE, self) @@ -147,10 +155,17 @@ class GuiBuildSettings(NToolDialog): self.mainBox.addWidget(self.toolStack) 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.addLayout(self.topBox) self.outerBox.addLayout(self.mainBox) - self.outerBox.addWidget(self.btnBox) + self.outerBox.addLayout(self.bottomBox) self.outerBox.setSpacing(12) self.setLayout(self.outerBox) @@ -259,12 +274,13 @@ class GuiBuildSettings(NToolDialog): """Save the various user settings.""" treeWidth, filterWidth = self.optTabSelect.mainSplitSizes() logger.debug("Saving State: GuiBuildSettings") - pOptions = SHARED.project.options - pOptions.setValue("GuiBuildSettings", "winWidth", self.width()) - pOptions.setValue("GuiBuildSettings", "winHeight", self.height()) - pOptions.setValue("GuiBuildSettings", "treeWidth", treeWidth) - pOptions.setValue("GuiBuildSettings", "filterWidth", filterWidth) - pOptions.saveSettings() + options = SHARED.project.options + options.setValue("GuiBuildSettings", "winWidth", self.width()) + options.setValue("GuiBuildSettings", "winHeight", self.height()) + options.setValue("GuiBuildSettings", "treeWidth", treeWidth) + options.setValue("GuiBuildSettings", "filterWidth", filterWidth) + options.setValue("GuiBuildSettings", "autoPreview", self.swtAutoPreview.isChecked()) + options.saveSettings() def _applyChanges(self) -> None: """Apply all settings changes to the build object.""" @@ -274,7 +290,7 @@ class GuiBuildSettings(NToolDialog): def _emitBuildData(self) -> None: """Assemble the build data and emit the signal.""" - self.newSettingsReady.emit(self._build) + self.newSettingsReady.emit(self._build, self.swtAutoPreview.isChecked()) self._build.resetChangedState() diff --git a/tests/test_tools/test_tools_manuscript.py b/tests/test_tools/test_tools_manuscript.py index 3d8b86cb..f8348593 100644 --- a/tests/test_tools/test_tools_manuscript.py +++ b/tests/test_tools/test_tools_manuscript.py @@ -108,10 +108,11 @@ def testToolManuscript_Builds(qtbot, nwGUI, projPath): bSettings = SHARED.findTopLevelWidget(GuiBuildSettings) assert isinstance(bSettings, GuiBuildSettings) bSettings.editBuildName.setText("Test Build") + bSettings.swtAutoPreview.setChecked(False) build = None @pyqtSlot(BuildSettings) - def _testNewSettingsReady(new: BuildSettings): + def _testNewSettingsReady(new: BuildSettings, refresh: bool): nonlocal build build = new @@ -151,6 +152,22 @@ def testToolManuscript_Builds(qtbot, nwGUI, projPath): assert new is not None 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 nwGUI.refreshThemeColors() diff --git a/tests/test_tools/test_tools_manussettings.py b/tests/test_tools/test_tools_manussettings.py index 853fe6cb..3d8804cb 100644 --- a/tests/test_tools/test_tools_manussettings.py +++ b/tests/test_tools/test_tools_manussettings.py @@ -69,7 +69,7 @@ def testToolBuildSettings_Init(qtbot, nwGUI, projPath, mockRnd): triggered = False @pyqtSlot(BuildSettings) - def _testNewSettingsReady(new: BuildSettings): + def _testNewSettingsReady(new: BuildSettings, refresh: bool): nonlocal triggered assert new.buildID == build.buildID triggered = True