Change behaviour of build settings dialog (#2351)

This commit is contained in:
Veronica Berglyd Olsen
2025-05-20 21:43:02 +02:00
committed by GitHub
3 changed files with 79 additions and 55 deletions
+1 -1
View File
@@ -390,7 +390,7 @@ class BuildSettings:
def setValue(self, key: str, value: T_BuildValue) -> None: def setValue(self, key: str, value: T_BuildValue) -> None:
"""Set a specific value for a build setting.""" """Set a specific value for a build setting."""
if (d := SETTINGS_TEMPLATE.get(key)) and len(d) == 2 and isinstance(value, d[0]): if (d := SETTINGS_TEMPLATE.get(key)) and len(d) == 2 and isinstance(value, d[0]):
self._changed = value != self._settings[key] self._changed |= (value != self._settings[key])
self._settings[key] = value self._settings[key] = value
return return
+14 -7
View File
@@ -80,7 +80,8 @@ class GuiBuildSettings(NToolDialog):
logger.debug("Create: GuiBuildSettings") logger.debug("Create: GuiBuildSettings")
self.setObjectName("GuiBuildSettings") self.setObjectName("GuiBuildSettings")
self._build = build # Make a copy of the build object
self._build = BuildSettings.fromDict(build.pack())
self.setWindowTitle(self.tr("Manuscript Build Settings")) self.setWindowTitle(self.tr("Manuscript Build Settings"))
self.setMinimumSize(700, 400) self.setMinimumSize(700, 400)
@@ -184,6 +185,7 @@ class GuiBuildSettings(NToolDialog):
settings. settings.
""" """
logger.debug("Closing: GuiBuildSettings") logger.debug("Closing: GuiBuildSettings")
self._applyChanges()
self._askToSaveBuild() self._askToSaveBuild()
self._saveSettings() self._saveSettings()
event.accept() event.accept()
@@ -211,11 +213,14 @@ class GuiBuildSettings(NToolDialog):
"""Handle button clicks from the dialog button box.""" """Handle button clicks from the dialog button box."""
role = self.buttonBox.buttonRole(button) role = self.buttonBox.buttonRole(button)
if role == QtRoleApply: if role == QtRoleApply:
self._applyChanges()
self._emitBuildData() self._emitBuildData()
elif role == QtRoleAccept: elif role == QtRoleAccept:
self._applyChanges()
self._emitBuildData() self._emitBuildData()
self.close() self.close()
elif role == QtRoleReject: elif role == QtRoleReject:
self._build.resetChangedState()
self.close() self.close()
return return
@@ -228,10 +233,9 @@ class GuiBuildSettings(NToolDialog):
whether the user wants to save them. whether the user wants to save them.
""" """
if self._build.changed: if self._build.changed:
response = SHARED.question(self.tr( if SHARED.question(self.tr(
"Do you want to save your changes to '{0}'?" "Do you want to save your changes to '{0}'?"
).format(self._build.name)) ).format(self._build.name)):
if response:
self._emitBuildData() self._emitBuildData()
self._build.resetChangedState() self._build.resetChangedState()
return return
@@ -246,14 +250,17 @@ class GuiBuildSettings(NToolDialog):
pOptions.setValue("GuiBuildSettings", "treeWidth", treeWidth) pOptions.setValue("GuiBuildSettings", "treeWidth", treeWidth)
pOptions.setValue("GuiBuildSettings", "filterWidth", filterWidth) pOptions.setValue("GuiBuildSettings", "filterWidth", filterWidth)
pOptions.saveSettings() pOptions.saveSettings()
return
def _applyChanges(self) -> None:
"""Apply all settings changes to the build object."""
self._build.setName(self.editBuildName.text())
self.optTabHeadings.saveContent()
self.optTabFormatting.saveContent()
return return
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._build.setName(self.editBuildName.text())
self.optTabHeadings.saveContent()
self.optTabFormatting.saveContent()
self.newSettingsReady.emit(self._build) self.newSettingsReady.emit(self._build)
self._build.resetChangedState() self._build.resetChangedState()
return return
+64 -47
View File
@@ -72,7 +72,7 @@ def testToolBuildSettings_Init(qtbot, nwGUI, projPath, mockRnd):
@pyqtSlot(BuildSettings) @pyqtSlot(BuildSettings)
def _testNewSettingsReady(new: BuildSettings): def _testNewSettingsReady(new: BuildSettings):
nonlocal triggered nonlocal triggered
assert new is build assert new.buildID == build.buildID
triggered = True triggered = True
# Capture Apply button # Capture Apply button
@@ -103,6 +103,7 @@ def testToolBuildSettings_Init(qtbot, nwGUI, projPath, mockRnd):
with qtbot.waitSignal(bSettings.newSettingsReady, timeout=5000): with qtbot.waitSignal(bSettings.newSettingsReady, timeout=5000):
bSettings.newSettingsReady.connect(_testNewSettingsReady) bSettings.newSettingsReady.connect(_testNewSettingsReady)
bSettings._build._changed = True
bSettings.close() bSettings.close()
assert triggered assert triggered
@@ -140,6 +141,9 @@ def testToolBuildSettings_Filter(qtbot, nwGUI, projPath, mockRnd):
bSettings.show() bSettings.show()
bSettings.loadContent() bSettings.loadContent()
sBuild = bSettings._build
assert sBuild.buildID == build.buildID
filterTab = bSettings.optTabSelect filterTab = bSettings.optTabSelect
button = bSettings.sidebar._group.button(bSettings.OPT_FILTERS) button = bSettings.sidebar._group.button(bSettings.OPT_FILTERS)
assert button is not None assert button is not None
@@ -153,15 +157,15 @@ def testToolBuildSettings_Filter(qtbot, nwGUI, projPath, mockRnd):
# Un-toggle note folders # Un-toggle note folders
filterTab.filterOpt._widgets[switchMap["worldRoot"]].setChecked(False) # World Root filterTab.filterOpt._widgets[switchMap["worldRoot"]].setChecked(False) # World Root
assert filterTab.optTree.topLevelItemCount() == 3 assert filterTab.optTree.topLevelItemCount() == 3
assert C.hWorldRoot in build._skipRoot assert C.hWorldRoot in sBuild._skipRoot
filterTab.filterOpt._widgets[switchMap["charRoot"]].setChecked(False) # Char Root filterTab.filterOpt._widgets[switchMap["charRoot"]].setChecked(False) # Char Root
assert filterTab.optTree.topLevelItemCount() == 2 assert filterTab.optTree.topLevelItemCount() == 2
assert C.hCharRoot in build._skipRoot assert C.hCharRoot in sBuild._skipRoot
filterTab.filterOpt._widgets[switchMap["plotRoot"]].setChecked(False) # Plot Root filterTab.filterOpt._widgets[switchMap["plotRoot"]].setChecked(False) # Plot Root
assert filterTab.optTree.topLevelItemCount() == 1 assert filterTab.optTree.topLevelItemCount() == 1
assert C.hPlotRoot in build._skipRoot assert C.hPlotRoot in sBuild._skipRoot
# Reset Plot and Char # Reset Plot and Char
filterTab.filterOpt._widgets[switchMap["plotRoot"]].setChecked(True) filterTab.filterOpt._widgets[switchMap["plotRoot"]].setChecked(True)
@@ -170,7 +174,7 @@ def testToolBuildSettings_Filter(qtbot, nwGUI, projPath, mockRnd):
# Switch off novel docs # Switch off novel docs
filterTab.filterOpt._widgets[switchMap["incNovel"]].setChecked(False) filterTab.filterOpt._widgets[switchMap["incNovel"]].setChecked(False)
assert build.buildItemFilter(SHARED.project) == { assert sBuild.buildItemFilter(SHARED.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (False, FilterMode.FILTERED), C.hTitlePage: (False, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -186,7 +190,7 @@ def testToolBuildSettings_Filter(qtbot, nwGUI, projPath, mockRnd):
# Switch on note docs # Switch on note docs
filterTab.filterOpt._widgets[switchMap["incNotes"]].setChecked(True) filterTab.filterOpt._widgets[switchMap["incNotes"]].setChecked(True)
assert build.buildItemFilter(SHARED.project) == { assert sBuild.buildItemFilter(SHARED.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (False, FilterMode.FILTERED), C.hTitlePage: (False, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -202,7 +206,7 @@ def testToolBuildSettings_Filter(qtbot, nwGUI, projPath, mockRnd):
# Switch on inactive docs # Switch on inactive docs
filterTab.filterOpt._widgets[switchMap["incInactive"]].setChecked(True) filterTab.filterOpt._widgets[switchMap["incInactive"]].setChecked(True)
assert build.buildItemFilter(SHARED.project) == { assert sBuild.buildItemFilter(SHARED.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (False, FilterMode.FILTERED), C.hTitlePage: (False, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -220,7 +224,7 @@ def testToolBuildSettings_Filter(qtbot, nwGUI, projPath, mockRnd):
filterTab._treeMap[C.hChapterDoc].setSelected(True) filterTab._treeMap[C.hChapterDoc].setSelected(True)
filterTab._treeMap[C.hSceneDoc].setSelected(True) filterTab._treeMap[C.hSceneDoc].setSelected(True)
filterTab.includedButton.click() filterTab.includedButton.click()
assert build.buildItemFilter(SHARED.project) == { assert sBuild.buildItemFilter(SHARED.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (False, FilterMode.FILTERED), C.hTitlePage: (False, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -239,7 +243,7 @@ def testToolBuildSettings_Filter(qtbot, nwGUI, projPath, mockRnd):
filterTab._treeMap[hPlotDoc].setSelected(True) # type: ignore filterTab._treeMap[hPlotDoc].setSelected(True) # type: ignore
filterTab._treeMap[hCharDoc].setSelected(True) # type: ignore filterTab._treeMap[hCharDoc].setSelected(True) # type: ignore
filterTab.excludedButton.click() filterTab.excludedButton.click()
assert build.buildItemFilter(SHARED.project) == { assert sBuild.buildItemFilter(SHARED.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (False, FilterMode.FILTERED), C.hTitlePage: (False, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -255,7 +259,7 @@ def testToolBuildSettings_Filter(qtbot, nwGUI, projPath, mockRnd):
# Switch on novel docs # Switch on novel docs
filterTab.filterOpt._widgets[switchMap["incNovel"]].setChecked(True) filterTab.filterOpt._widgets[switchMap["incNovel"]].setChecked(True)
assert build.buildItemFilter(SHARED.project) == { assert sBuild.buildItemFilter(SHARED.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (True, FilterMode.FILTERED), # Now enabled C.hTitlePage: (True, FilterMode.FILTERED), # Now enabled
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -273,7 +277,7 @@ def testToolBuildSettings_Filter(qtbot, nwGUI, projPath, mockRnd):
filterTab.optTree.clearSelection() filterTab.optTree.clearSelection()
filterTab._treeMap[C.hNovelRoot].setSelected(True) filterTab._treeMap[C.hNovelRoot].setSelected(True)
filterTab.resetButton.click() filterTab.resetButton.click()
assert build.buildItemFilter(SHARED.project) == { assert sBuild.buildItemFilter(SHARED.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (True, FilterMode.FILTERED), C.hTitlePage: (True, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -294,7 +298,7 @@ def testToolBuildSettings_Filter(qtbot, nwGUI, projPath, mockRnd):
filterTab._treeMap[hPlotDoc].setSelected(True) # type: ignore filterTab._treeMap[hPlotDoc].setSelected(True) # type: ignore
filterTab._treeMap[hCharDoc].setSelected(True) # type: ignore filterTab._treeMap[hCharDoc].setSelected(True) # type: ignore
filterTab.resetButton.click() filterTab.resetButton.click()
assert build.buildItemFilter(SHARED.project) == { assert sBuild.buildItemFilter(SHARED.project) == {
C.hNovelRoot: (False, FilterMode.SKIPPED), C.hNovelRoot: (False, FilterMode.SKIPPED),
C.hTitlePage: (True, FilterMode.FILTERED), C.hTitlePage: (True, FilterMode.FILTERED),
C.hChapterDir: (False, FilterMode.SKIPPED), C.hChapterDir: (False, FilterMode.SKIPPED),
@@ -429,6 +433,8 @@ def testToolBuildSettings_Headings(qtbot, nwGUI):
# Edit a Heading # Edit a Heading
# ============== # ==============
sBuild = bSettings._build
assert sBuild.buildID == build.buildID
# Create new format of all bits # Create new format of all bits
headTab.btnChapter.click() headTab.btnChapter.click()
@@ -447,13 +453,13 @@ def testToolBuildSettings_Headings(qtbot, nwGUI):
headTab.aInsScAbs.trigger() headTab.aInsScAbs.trigger()
assert headTab.editTextBox.toPlainText() == allFmt assert headTab.editTextBox.toPlainText() == allFmt
headTab.btnApply.click() headTab.btnApply.click()
assert build.getStr("headings.fmtChapter") == allFmt assert sBuild.getStr("headings.fmtChapter") == allFmt
# Check complex format # Check complex format
headTab.btnChapter.click() headTab.btnChapter.click()
headTab.editTextBox.setPlainText(f"Chapter {nwHeadFmt.CH_NUM}\n{nwHeadFmt.TITLE}\n") headTab.editTextBox.setPlainText(f"Chapter {nwHeadFmt.CH_NUM}\n{nwHeadFmt.TITLE}\n")
headTab.btnApply.click() headTab.btnApply.click()
assert build.getStr("headings.fmtChapter") == ( assert sBuild.getStr("headings.fmtChapter") == (
f"Chapter {nwHeadFmt.CH_NUM}{nwHeadFmt.BR}{nwHeadFmt.TITLE}" f"Chapter {nwHeadFmt.CH_NUM}{nwHeadFmt.BR}{nwHeadFmt.TITLE}"
) )
@@ -461,39 +467,42 @@ def testToolBuildSettings_Headings(qtbot, nwGUI):
headTab.btnPart.click() headTab.btnPart.click()
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE) headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
headTab.btnApply.click() headTab.btnApply.click()
assert build.getStr("headings.fmtPart") == nwHeadFmt.TITLE assert sBuild.getStr("headings.fmtPart") == nwHeadFmt.TITLE
headTab.btnChapter.click() headTab.btnChapter.click()
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE) headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
headTab.btnApply.click() headTab.btnApply.click()
assert build.getStr("headings.fmtChapter") == nwHeadFmt.TITLE assert sBuild.getStr("headings.fmtChapter") == nwHeadFmt.TITLE
headTab.btnUnnumbered.click() headTab.btnUnnumbered.click()
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE) headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
headTab.btnApply.click() headTab.btnApply.click()
assert build.getStr("headings.fmtUnnumbered") == nwHeadFmt.TITLE assert sBuild.getStr("headings.fmtUnnumbered") == nwHeadFmt.TITLE
headTab.btnScene.click() headTab.btnScene.click()
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE) headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
headTab.btnApply.click() headTab.btnApply.click()
assert build.getStr("headings.fmtScene") == nwHeadFmt.TITLE assert sBuild.getStr("headings.fmtScene") == nwHeadFmt.TITLE
headTab.btnAScene.click() headTab.btnAScene.click()
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE) headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
headTab.btnApply.click() headTab.btnApply.click()
assert build.getStr("headings.fmtAltScene") == nwHeadFmt.TITLE assert sBuild.getStr("headings.fmtAltScene") == nwHeadFmt.TITLE
headTab.btnSection.click() headTab.btnSection.click()
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE) headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
headTab.btnApply.click() headTab.btnApply.click()
assert build.getStr("headings.fmtSection") == nwHeadFmt.TITLE assert sBuild.getStr("headings.fmtSection") == nwHeadFmt.TITLE
# Check hide switches # Check hide switches
headTab.swtScene.setChecked(True) headTab.swtScene.setChecked(True)
headTab.swtSection.setChecked(True) headTab.swtSection.setChecked(True)
headTab.saveContent() headTab.saveContent()
assert build.getBool("headings.hideScene") is True sBuild = bSettings._build
assert build.getBool("headings.hideSection") is True assert sBuild.buildID == build.buildID
assert sBuild.getBool("headings.hideScene") is True
assert sBuild.getBool("headings.hideSection") is True
# Finish # Finish
button = bSettings.buttonBox.button(QtDialogClose) button = bSettings.buttonBox.button(QtDialogClose)
@@ -556,16 +565,18 @@ def testToolBuildSettings_FormatTextContent(qtbot, nwGUI):
# Save values # Save values
fmtTab.saveContent() fmtTab.saveContent()
sBuild = bSettings._build
assert sBuild.buildID == build.buildID
assert build.getBool("text.includeBodyText") is True assert sBuild.getBool("text.includeBodyText") is True
assert build.getBool("text.includeSynopsis") is True assert sBuild.getBool("text.includeSynopsis") is True
assert build.getBool("text.includeComments") is True assert sBuild.getBool("text.includeComments") is True
assert build.getBool("text.includeStory") is True assert sBuild.getBool("text.includeStory") is True
assert build.getBool("text.includeNotes") is True assert sBuild.getBool("text.includeNotes") is True
assert build.getBool("text.includeKeywords") is True assert sBuild.getBool("text.includeKeywords") is True
assert build.getStr("text.ignoredKeywords") in ("@custom, @object", "@object, @custom") assert sBuild.getStr("text.ignoredKeywords") in ("@custom, @object", "@object, @custom")
assert build.getBool("text.addNoteHeadings") is True assert sBuild.getBool("text.addNoteHeadings") is True
# Finish # Finish
button = bSettings.buttonBox.button(QtDialogClose) button = bSettings.buttonBox.button(QtDialogClose)
@@ -623,15 +634,17 @@ def testToolBuildSettings_FormatTextFormat(monkeypatch, qtbot, nwGUI):
# Save values # Save values
fmtTab.saveContent() fmtTab.saveContent()
sBuild = bSettings._build
assert sBuild.buildID == build.buildID
assert build.getStr("format.textFont") == testFont.toString() assert sBuild.getStr("format.textFont") == testFont.toString()
assert build.getFloat("format.lineHeight") == 1.15 assert sBuild.getFloat("format.lineHeight") == 1.15
assert build.getBool("format.justifyText") is True assert sBuild.getBool("format.justifyText") is True
assert build.getBool("format.stripUnicode") is True assert sBuild.getBool("format.stripUnicode") is True
assert build.getBool("format.replaceTabs") is True assert sBuild.getBool("format.replaceTabs") is True
assert build.getBool("format.keepBreaks") is False assert sBuild.getBool("format.keepBreaks") is False
assert build.getBool("format.showDialogue") is True assert sBuild.getBool("format.showDialogue") is True
# Check that the font dialog doesn't fail # Check that the font dialog doesn't fail
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
@@ -682,10 +695,12 @@ def testToolBuildSettings_FormatFirstLineIndent(monkeypatch, qtbot, nwGUI):
# Save values # Save values
fmtTab.saveContent() fmtTab.saveContent()
sBuild = bSettings._build
assert sBuild.buildID == build.buildID
assert build.getBool("format.firstLineIndent") is True assert sBuild.getBool("format.firstLineIndent") is True
assert build.getFloat("format.firstIndentWidth") == 2.0 assert sBuild.getFloat("format.firstIndentWidth") == 2.0
assert build.getBool("format.indentFirstPar") is True assert sBuild.getBool("format.indentFirstPar") is True
# Finish # Finish
button = bSettings.buttonBox.button(QtDialogClose) button = bSettings.buttonBox.button(QtDialogClose)
@@ -800,15 +815,17 @@ def testToolBuildSettings_FormatOutput(qtbot, nwGUI):
# Save values # Save values
fmtTab.saveContent() fmtTab.saveContent()
sBuild = bSettings._build
assert sBuild.buildID == build.buildID
assert build.getStr("doc.pageHeader") == "Stuff" assert sBuild.getStr("doc.pageHeader") == "Stuff"
assert build.getInt("doc.pageCountOffset") == 1 assert sBuild.getInt("doc.pageCountOffset") == 1
assert build.getBool("doc.colorHeadings") is False assert sBuild.getBool("doc.colorHeadings") is False
assert build.getBool("doc.scaleHeadings") is False assert sBuild.getBool("doc.scaleHeadings") is False
assert build.getBool("doc.boldHeadings") is False assert sBuild.getBool("doc.boldHeadings") is False
assert build.getBool("html.addStyles") is True assert sBuild.getBool("html.addStyles") is True
assert build.getBool("html.preserveTabs") is True assert sBuild.getBool("html.preserveTabs") is True
# Reset header format # Reset header format
fmtTab.btnPageHeader.click() fmtTab.btnPageHeader.click()