From f2f2e6cb46933800632f5888b640669c93f3830d Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 18 May 2025 21:56:58 +0200 Subject: [PATCH 1/6] Extend completer for editor to also support comments --- novelwriter/gui/doceditor.py | 81 ++++++++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index adbab414..74ae1c71 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -3,15 +3,16 @@ novelWriter – GUI Document Editor ================================= File History: -Created: 2018-09-29 [0.0.1] GuiDocEditor -Created: 2019-04-22 [0.0.1] BackgroundWordCounter -Created: 2019-09-29 [0.2.1] GuiDocEditSearch -Created: 2020-04-25 [0.4.5] GuiDocEditHeader -Rewritten: 2020-06-15 [0.9] GuiDocEditSearch -Created: 2020-06-27 [0.10] GuiDocEditFooter -Rewritten: 2020-10-07 [1.0b3] BackgroundWordCounter -Created: 2023-11-06 [2.2b1] MetaCompleter -Created: 2023-11-07 [2.2b1] GuiDocToolBar +Created: 2018-09-29 [0.0.1] GuiDocEditor +Created: 2019-04-22 [0.0.1] BackgroundWordCounter +Created: 2019-09-29 [0.2.1] GuiDocEditSearch +Created: 2020-04-25 [0.4.5] GuiDocEditHeader +Rewritten: 2020-06-15 [0.9] GuiDocEditSearch +Created: 2020-06-27 [0.10] GuiDocEditFooter +Rewritten: 2020-10-07 [1.0b3] BackgroundWordCounter +Created: 2023-11-06 [2.2b1] MetaCompleter +Created: 2023-11-07 [2.2b1] GuiDocToolBar +Extended: 2025-05-18 [2.7rc1] CommandCompleter This file is a part of novelWriter Copyright (C) 2018 Veronica Berglyd Olsen and novelWriter contributors @@ -149,7 +150,7 @@ class GuiDocEditor(QPlainTextEdit): self._autoReplace = TextAutoReplace() # Completer - self._completer = MetaCompleter(self) + self._completer = CommandCompleter(self) self._completer.complete.connect(self._insertCompletion) # Create Custom Document @@ -1079,13 +1080,16 @@ class GuiDocEditor(QPlainTextEdit): if (block := self._qDocument.findBlock(pos)).isValid(): text = block.text() - if text.startswith("@") and added + removed == 1: + if text and text[0] in "@%" and added + removed == 1: # Only run on single character changes, or it will trigger # at unwanted times when other changes are made to the document cursor = self.textCursor() bPos = cursor.positionInBlock() if bPos > 0 and (viewport := self.viewport()): - show = self._completer.updateText(text, bPos) + if text[0] == "@": + show = self._completer.updateMetaText(text, bPos) + else: + show = self._completer.updateCommentText(text, bPos) point = self.cursorRect().bottomRight() self._completer.move(viewport.mapToGlobal(point)) self._completer.setVisible(show) @@ -2073,13 +2077,13 @@ class GuiDocEditor(QPlainTextEdit): return -class MetaCompleter(QMenu): - """GuiWidget: Meta Completer Menu +class CommandCompleter(QMenu): + """GuiWidget: Command Completer Menu This is a context menu with options populated from the user's - defined tags. It also helps to type the meta data keyword on a new - line starting with an @. The updateText function should be called on - every keystroke on a line starting with @. + defined tags and keys. It also helps to type the meta data keyword + on a new line starting with @ or %. The update functions should be + called on every keystroke on a line starting with @ or %. """ complete = pyqtSignal(int, int, str) @@ -2088,14 +2092,14 @@ class MetaCompleter(QMenu): super().__init__(parent=parent) return - def updateText(self, text: str, pos: int) -> bool: + def updateMetaText(self, text: str, pos: int) -> bool: """Update the menu options based on the line of text.""" self.clear() kw, sep, _ = text.partition(":") if pos <= len(kw): offset = 0 length = len(kw.rstrip()) - suffix = "" if sep else ":" + suffix = "" if sep else ": " options = list(filter( lambda x: x.startswith(kw.rstrip()), nwKeyWords.VALID_KEYS )) @@ -2108,7 +2112,7 @@ class MetaCompleter(QMenu): offset = tPos[index] if lookup else pos length = len(lookup) suffix = "" - options = list(filter( + options = sorted(filter( lambda x: lookup in x.lower(), SHARED.project.index.getClassTags( nwKeyWords.KEY_CLASS.get(kw.strip()) ) @@ -2117,13 +2121,48 @@ class MetaCompleter(QMenu): if not options: return False - for value in sorted(options): + for value in options: rep = value + suffix action = qtAddAction(self, value) action.triggered.connect(qtLambda(self._emitComplete, offset, length, rep)) return True + def updateCommentText(self, text: str, pos: int) -> bool: + """Update the menu options based on the line of text.""" + self.clear() + cmd, sep, _ = text.partition(":") + if pos <= len(cmd): + clean = text[1:].lstrip()[:6].lower() + if clean[:6] == "story.": + pre, _, key = cmd.partition(".") + offset = len(pre) + 1 + length = len(key) + suffix = "" if sep else ": " + options = sorted(filter( + lambda x: x.startswith(key.rstrip()), + SHARED.project.index.getStoryKeys(), + )) + elif pos < 12: + offset = 0 + length = len(cmd.rstrip()) + suffix = "" + options = list(filter( + lambda x: x.startswith(cmd.rstrip()), + ["%Synopsis: ", "%Short: ", "%Story", "%Note"], + )) + else: + return False + + if options: + for value in options: + rep = value + suffix + action = qtAddAction(self, rep.rstrip(":. ")) + action.triggered.connect(qtLambda(self._emitComplete, offset, length, rep)) + return True + + return False + ## # Events ## From f22f80dafd366a330f8a2d0e17d3e06f8501cb97 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 18 May 2025 22:36:25 +0200 Subject: [PATCH 2/6] Add support for indexing and auto-completing story notes --- novelwriter/core/index.py | 11 ++++++++++- novelwriter/core/indexdata.py | 5 ++++- novelwriter/gui/doceditor.py | 13 +++++++++++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index 6720d171..fb357842 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -618,6 +618,10 @@ class Index: """Return all story structure keys.""" return self._itemIndex.allStoryKeys() + def getNoteKeys(self) -> set[str]: + """Return all note comment keys.""" + return self._itemIndex.allNoteKeys() + def novelStructure( self, rootHandle: str | None = None, activeOnly: bool = True ) -> Iterable[tuple[str, str, str, IndexHeading]]: @@ -920,11 +924,12 @@ class IndexCache: which provides lookup capabilities and caching for shared data. """ - __slots__ = ("story", "tags") + __slots__ = ("note", "story", "tags") def __init__(self, tagsIndex: TagsIndex) -> None: self.tags: TagsIndex = tagsIndex self.story: set[str] = set() + self.note: set[str] = set() return @@ -979,6 +984,10 @@ class ItemIndex: """Return all story structure keys.""" return self._cache.story.copy() + def allNoteKeys(self) -> set[str]: + """Return all note comment keys.""" + return self._cache.note.copy() + def allItemTags(self, tHandle: str) -> list[str]: """Get all tags set for headings of an item.""" if tHandle in self._items: diff --git a/novelwriter/core/indexdata.py b/novelwriter/core/indexdata.py index 131361f0..070558ce 100644 --- a/novelwriter/core/indexdata.py +++ b/novelwriter/core/indexdata.py @@ -316,6 +316,9 @@ class IndexHeading: case "story" if key: self._cache.story.add(key) self._comments[f"story.{key}"] = str(text) + case "note" if key: + self._cache.note.add(key) + self._comments[f"note.{key}"] = str(text) return def setTag(self, tag: str) -> None: @@ -395,7 +398,7 @@ class IndexHeading: self.addReference(tag, keyword) else: raise ValueError("Heading reference contains an invalid keyword") - elif key == "summary" or key.startswith("story"): + elif key == "summary" or key.startswith(("story", "note")): comment, _, kind = str(key).partition(".") self.setComment(comment, compact(kind), str(entry)) else: diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index 74ae1c71..0892bb71 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -2099,8 +2099,8 @@ class CommandCompleter(QMenu): if pos <= len(kw): offset = 0 length = len(kw.rstrip()) - suffix = "" if sep else ": " - options = list(filter( + suffix = "" if sep else ":" + options = sorted(filter( lambda x: x.startswith(kw.rstrip()), nwKeyWords.VALID_KEYS )) else: @@ -2143,6 +2143,15 @@ class CommandCompleter(QMenu): lambda x: x.startswith(key.rstrip()), SHARED.project.index.getStoryKeys(), )) + elif clean[:5] == "note.": + pre, _, key = cmd.partition(".") + offset = len(pre) + 1 + length = len(key) + suffix = "" if sep else ": " + options = sorted(filter( + lambda x: x.startswith(key.rstrip()), + SHARED.project.index.getNoteKeys(), + )) elif pos < 12: offset = 0 length = len(cmd.rstrip()) From ceadd52d1ade7b7a20bf37e7b759acb5654bc412 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 18 May 2025 22:37:15 +0200 Subject: [PATCH 3/6] Update tests --- sample/content/636b6aa9b697b.nwd | 5 ++- sample/nwProject.nwx | 6 +-- tests/test_core/test_core_indexdata.py | 9 ++++ tests/test_gui/test_gui_doceditor.py | 60 +++++++++++++++++++++++++- 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/sample/content/636b6aa9b697b.nwd b/sample/content/636b6aa9b697b.nwd index 5fd4bb76..e3d4bfde 100644 --- a/sample/content/636b6aa9b697b.nwd +++ b/sample/content/636b6aa9b697b.nwd @@ -1,8 +1,8 @@ %%~name: Making a Scene %%~path: 6a2d6d5f4f401/636b6aa9b697b %%~kind: NOVEL/DOCUMENT -%%~hash: c057a5e9309b0e764c367b0fe9ab0607e3308622 -%%~date: Unknown/2025-04-08 20:10:33 +%%~hash: 1f3d98a6a27b4f9a7f2239fcd78f9e2263cd3b97 +%%~date: Unknown/2025-05-18 22:36:59 ### Making a Scene @pov: Jane @@ -11,6 +11,7 @@ @mention: Space %Story.Resolution: You can describe the scene structure with story comments. +%Note.Consistency: You can also make notes about things like consistency of the story. A scene is defined by a level three heading, like the one at the top of this page. The scene will be assigned to the chapter preceding it in the project tree. The scene document can be sorted after the chapter document, or as a child of the chapter. Both result in the same output in the end, so it is a matter of preference. diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index d20ba88e..b6fa62e7 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,6 +1,6 @@ - - + + Sample Project Jane Smith @@ -58,7 +58,7 @@ Chapter One - + Making a Scene diff --git a/tests/test_core/test_core_indexdata.py b/tests/test_core/test_core_indexdata.py index 9e1080f8..5bf45c8a 100644 --- a/tests/test_core/test_core_indexdata.py +++ b/tests/test_core/test_core_indexdata.py @@ -224,6 +224,14 @@ def testCoreIndexData_IndexHeading(): "story.crisis": "It exploded!", } + # Set Note Comment + head.setComment(nwComment.NOTE.name, "consitency", "Only explode once") + assert head.comments == { + "summary": "In the beginning ...", + "story.crisis": "It exploded!", + "note.consitency": "Only explode once", + } + # Set Tag head.setTag("Stuff") assert head.tag == "stuff" # Case insensitive @@ -240,6 +248,7 @@ def testCoreIndexData_IndexHeading(): "refs": {"stuff": "@object"}, "summary": "In the beginning ...", "story.crisis": "It exploded!", + "note.consitency": "Only explode once", } # Unpack KeyError diff --git a/tests/test_gui/test_gui_doceditor.py b/tests/test_gui/test_gui_doceditor.py index b198a71a..5bda2d82 100644 --- a/tests/test_gui/test_gui_doceditor.py +++ b/tests/test_gui/test_gui_doceditor.py @@ -1877,10 +1877,68 @@ def testGuiEditor_Completer(qtbot, nwGUI, projPath, mockRnd): qtbot.keyClick(completer, Qt.Key.Key_Down, delay=KEY_DELAY) qtbot.keyClick(completer, Qt.Key.Key_Return, delay=KEY_DELAY) qtbot.keyClick(completer, Qt.Key.Key_Escape, delay=KEY_DELAY) + qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY) assert docEditor.getText() == ( "### Scene One\n\n" "@char: Jane\n" - "@focus: John" + "@focus: John\n" + ) + + # Send keypresses to the completer object for a comment + qtbot.keyClick(docEditor, "%", delay=KEY_DELAY) + assert len(completer.actions()) == 4 + qtbot.keyClick(completer, Qt.Key.Key_Down, delay=KEY_DELAY) + qtbot.keyClick(completer, Qt.Key.Key_Return, delay=KEY_DELAY) + qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY) + assert docEditor.getText() == ( + "### Scene One\n\n" + "@char: Jane\n" + "@focus: John\n" + "%Synopsis: \n" + ) + + # Auto-complete story comment + SHARED.project.index._itemIndex._cache.story.add("Resolution") + qtbot.keyClick(docEditor, "%", delay=KEY_DELAY) + assert len(completer.actions()) == 4 + qtbot.keyClick(completer, Qt.Key.Key_Down, delay=KEY_DELAY) + qtbot.keyClick(completer, Qt.Key.Key_Down, delay=KEY_DELAY) + qtbot.keyClick(completer, Qt.Key.Key_Down, delay=KEY_DELAY) + qtbot.keyClick(completer, Qt.Key.Key_Return, delay=KEY_DELAY) + qtbot.keyClick(completer, ".", delay=KEY_DELAY) + assert len(completer.actions()) == 1 + qtbot.keyClick(completer, Qt.Key.Key_Down, delay=KEY_DELAY) + qtbot.keyClick(completer, Qt.Key.Key_Return, delay=KEY_DELAY) + qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY) + assert docEditor.getText() == ( + "### Scene One\n\n" + "@char: Jane\n" + "@focus: John\n" + "%Synopsis: \n" + "%Story.Resolution: \n" + ) + + # Auto-complete note comment + SHARED.project.index._itemIndex._cache.note.add("Consistency") + qtbot.keyClick(docEditor, "%", delay=KEY_DELAY) + assert len(completer.actions()) == 4 + qtbot.keyClick(completer, Qt.Key.Key_Down, delay=KEY_DELAY) + qtbot.keyClick(completer, Qt.Key.Key_Down, delay=KEY_DELAY) + qtbot.keyClick(completer, Qt.Key.Key_Down, delay=KEY_DELAY) + qtbot.keyClick(completer, Qt.Key.Key_Down, delay=KEY_DELAY) + qtbot.keyClick(completer, Qt.Key.Key_Return, delay=KEY_DELAY) + qtbot.keyClick(completer, ".", delay=KEY_DELAY) + assert len(completer.actions()) == 1 + qtbot.keyClick(completer, Qt.Key.Key_Down, delay=KEY_DELAY) + qtbot.keyClick(completer, Qt.Key.Key_Return, delay=KEY_DELAY) + qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY) + assert docEditor.getText() == ( + "### Scene One\n\n" + "@char: Jane\n" + "@focus: John\n" + "%Synopsis: \n" + "%Story.Resolution: \n" + "%Note.Consistency: \n" ) # qtbot.stop() From 47814e35cb1b5661148391ca4193dd9576be4875 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 18 May 2025 22:46:54 +0200 Subject: [PATCH 4/6] Add support for including notes in manuscript --- novelwriter/assets/i18n/project_en_GB.json | 1 + novelwriter/core/buildsettings.py | 2 ++ novelwriter/core/docbuild.py | 1 + novelwriter/formats/tokenizer.py | 3 ++- novelwriter/gui/outline.py | 7 +++++++ novelwriter/tools/manuscript.py | 2 +- novelwriter/tools/manussettings.py | 4 ++++ 7 files changed, 18 insertions(+), 2 deletions(-) diff --git a/novelwriter/assets/i18n/project_en_GB.json b/novelwriter/assets/i18n/project_en_GB.json index b5288d04..237096f5 100644 --- a/novelwriter/assets/i18n/project_en_GB.json +++ b/novelwriter/assets/i18n/project_en_GB.json @@ -4,6 +4,7 @@ "Footnotes": "Footnotes", "Comment": "Comment", "Story Structure": "Story Structure", + "Note": "Note", "Notes": "Notes", "Tag": "Tag", "Point of View": "Point of View", diff --git a/novelwriter/core/buildsettings.py b/novelwriter/core/buildsettings.py index 13429e2c..0972dd71 100644 --- a/novelwriter/core/buildsettings.py +++ b/novelwriter/core/buildsettings.py @@ -79,6 +79,7 @@ SETTINGS_TEMPLATE: dict[str, tuple[type, T_BuildValue]] = { "text.includeSynopsis": (bool, False), "text.includeComments": (bool, False), "text.includeStory": (bool, False), + "text.includeNotes": (bool, False), "text.includeKeywords": (bool, False), "text.includeBodyText": (bool, True), "text.ignoredKeywords": (str, ""), @@ -146,6 +147,7 @@ SETTINGS_LABELS = { "text.includeSynopsis": QT_TRANSLATE_NOOP("Builds", "Include Synopsis"), "text.includeComments": QT_TRANSLATE_NOOP("Builds", "Include Comments"), "text.includeStory": QT_TRANSLATE_NOOP("Builds", "Include Story Structure"), + "text.includeNotes": QT_TRANSLATE_NOOP("Builds", "Include Notes"), "text.includeKeywords": QT_TRANSLATE_NOOP("Builds", "Include Keywords"), "text.includeBodyText": QT_TRANSLATE_NOOP("Builds", "Include Body Text"), "text.ignoredKeywords": QT_TRANSLATE_NOOP("Builds", "Ignore These Keywords"), diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py index 4706e1d1..8e89095d 100644 --- a/novelwriter/core/docbuild.py +++ b/novelwriter/core/docbuild.py @@ -317,6 +317,7 @@ class NWBuildDocument: bldObj.setCommentType(nwComment.SYNOPSIS, self._build.getBool("text.includeSynopsis")) bldObj.setCommentType(nwComment.SHORT, self._build.getBool("text.includeSynopsis")) bldObj.setCommentType(nwComment.STORY, self._build.getBool("text.includeStory")) + bldObj.setCommentType(nwComment.NOTE, self._build.getBool("text.includeNotes")) if isinstance(bldObj, ToHtml): bldObj.setStyles(self._build.getBool("html.addStyles")) diff --git a/novelwriter/formats/tokenizer.py b/novelwriter/formats/tokenizer.py index 90300861..ba355a16 100644 --- a/novelwriter/formats/tokenizer.py +++ b/novelwriter/formats/tokenizer.py @@ -614,7 +614,8 @@ class Tokenizer(ABC): tStyle |= BlockFmt.JUSTIFY if cStyle in ( - nwComment.SYNOPSIS, nwComment.SHORT, nwComment.PLAIN, nwComment.STORY + nwComment.SYNOPSIS, nwComment.SHORT, nwComment.PLAIN, + nwComment.STORY, nwComment.NOTE, ): bStyle = COMMENT_STYLE[cStyle] tLine, tFmt = self._formatComment(bStyle, cKey, cText) diff --git a/novelwriter/gui/outline.py b/novelwriter/gui/outline.py index efe20aea..4ab18e50 100644 --- a/novelwriter/gui/outline.py +++ b/novelwriter/gui/outline.py @@ -751,9 +751,13 @@ class GuiOutlineTree(QTreeWidget): def _dumpNovelData(self, rootHandle: str | None) -> list[list[str | int]]: """Dump all novel data into a table.""" sLabel = SHARED.project.localLookup("Story Structure") + nLabel = SHARED.project.localLookup("Note") sKeys = sorted(SHARED.project.index.getStoryKeys()) + nKeys = sorted(SHARED.project.index.getNoteKeys()) sMatch = [f"story.{k}" for k in sKeys] + nMatch = [f"note.{k}" for k in nKeys] sHeaders = [f"{sLabel} ({k})" for k in sKeys] + nHeaders = [f"{nLabel} ({k})" for k in nKeys] data: list[list[str | int]] = [[ "H", @@ -777,6 +781,7 @@ class GuiOutlineTree(QTreeWidget): trConst(nwLabels.OUTLINE_COLS[nwOutline.MENTION]), trConst(nwLabels.OUTLINE_COLS[nwOutline.SYNOP]), *sHeaders, + *nHeaders, ]] for _, tHandle, sTitle, novIdx in SHARED.project.index.novelStructure( @@ -786,6 +791,7 @@ class GuiOutlineTree(QTreeWidget): refs = SHARED.project.index.getReferences(tHandle, sTitle) comments = dict(novIdx.comments.items()) story = [comments.get(k, "") for k in sMatch] + notes = [comments.get(k, "") for k in nMatch] data.append([ novIdx.level, novIdx.title, @@ -808,6 +814,7 @@ class GuiOutlineTree(QTreeWidget): ", ".join(refs[nwKeyWords.MENTION_KEY]), novIdx.synopsis, *story, + *notes, ]) return data diff --git a/novelwriter/tools/manuscript.py b/novelwriter/tools/manuscript.py index 8d4aca10..098a3f33 100644 --- a/novelwriter/tools/manuscript.py +++ b/novelwriter/tools/manuscript.py @@ -630,7 +630,7 @@ class _DetailsWidget(QWidget): self.listView.addTopLevelItem(item) for key in [ "text.includeSynopsis", "text.includeComments", "text.includeStory", - "text.includeKeywords", "text.includeBodyText", + "text.includeNotes", "text.includeKeywords", "text.includeBodyText", ]: sub = QTreeWidgetItem() sub.setText(0, build.getLabel(key)) diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py index 855f64ea..521e08a0 100644 --- a/novelwriter/tools/manussettings.py +++ b/novelwriter/tools/manussettings.py @@ -974,12 +974,14 @@ class _FormattingTab(NScrollableForm): self.incSynopsis = NSwitch(self, height=iPx) self.incComments = NSwitch(self, height=iPx) self.incStory = NSwitch(self, height=iPx) + self.incNotes = NSwitch(self, height=iPx) self.incKeywords = NSwitch(self, height=iPx) self.addRow(self._build.getLabel("text.includeBodyText"), self.incBodyText) self.addRow(self._build.getLabel("text.includeSynopsis"), self.incSynopsis) self.addRow(self._build.getLabel("text.includeComments"), self.incComments) self.addRow(self._build.getLabel("text.includeStory"), self.incStory) + self.addRow(self._build.getLabel("text.includeNotes"), self.incNotes) self.addRow(self._build.getLabel("text.includeKeywords"), self.incKeywords) # Ignored Keywords @@ -1288,6 +1290,7 @@ class _FormattingTab(NScrollableForm): self.incSynopsis.setChecked(self._build.getBool("text.includeSynopsis")) self.incComments.setChecked(self._build.getBool("text.includeComments")) self.incStory.setChecked(self._build.getBool("text.includeStory")) + self.incNotes.setChecked(self._build.getBool("text.includeNotes")) self.incKeywords.setChecked(self._build.getBool("text.includeKeywords")) self.ignoredKeywords.setText(self._build.getStr("text.ignoredKeywords")) self.addNoteHead.setChecked(self._build.getBool("text.addNoteHeadings")) @@ -1387,6 +1390,7 @@ class _FormattingTab(NScrollableForm): self._build.setValue("text.includeSynopsis", self.incSynopsis.isChecked()) self._build.setValue("text.includeComments", self.incComments.isChecked()) self._build.setValue("text.includeStory", self.incStory.isChecked()) + self._build.setValue("text.includeNotes", self.incNotes.isChecked()) self._build.setValue("text.includeKeywords", self.incKeywords.isChecked()) self._build.setValue("text.ignoredKeywords", self.ignoredKeywords.text()) self._build.setValue("text.addNoteHeadings", self.addNoteHead.isChecked()) From 70ae2bd595eaa9ed62380e82e53133b9eb4c7ce5 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 18 May 2025 22:47:01 +0200 Subject: [PATCH 5/6] Update tests --- tests/test_tools/test_tools_manussettings.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_tools/test_tools_manussettings.py b/tests/test_tools/test_tools_manussettings.py index e5756a18..b2d0479f 100644 --- a/tests/test_tools/test_tools_manussettings.py +++ b/tests/test_tools/test_tools_manussettings.py @@ -510,6 +510,8 @@ def testToolBuildSettings_FormatTextContent(qtbot, nwGUI): build.setValue("text.includeBodyText", False) build.setValue("text.includeSynopsis", False) build.setValue("text.includeComments", False) + build.setValue("text.includeStory", False) + build.setValue("text.includeNotes", False) build.setValue("text.includeKeywords", False) build.setValue("text.ignoredKeywords", "") @@ -530,6 +532,8 @@ def testToolBuildSettings_FormatTextContent(qtbot, nwGUI): assert fmtTab.incBodyText.isChecked() is False assert fmtTab.incSynopsis.isChecked() is False assert fmtTab.incComments.isChecked() is False + assert fmtTab.incStory.isChecked() is False + assert fmtTab.incNotes.isChecked() is False assert fmtTab.incKeywords.isChecked() is False assert fmtTab.ignoredKeywords.text() == "" @@ -539,6 +543,8 @@ def testToolBuildSettings_FormatTextContent(qtbot, nwGUI): fmtTab.incBodyText.setChecked(True) fmtTab.incSynopsis.setChecked(True) fmtTab.incComments.setChecked(True) + fmtTab.incStory.setChecked(True) + fmtTab.incNotes.setChecked(True) fmtTab.incKeywords.setChecked(True) fmtTab.addNoteHead.setChecked(True) @@ -554,6 +560,8 @@ def testToolBuildSettings_FormatTextContent(qtbot, nwGUI): assert build.getBool("text.includeBodyText") is True assert build.getBool("text.includeSynopsis") is True assert build.getBool("text.includeComments") is True + assert build.getBool("text.includeStory") is True + assert build.getBool("text.includeNotes") is True assert build.getBool("text.includeKeywords") is True assert build.getStr("text.ignoredKeywords") in ("@custom, @object", "@object, @custom") From 5fb45f7647a3855898fc287315dbb55755a5071c Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 18 May 2025 22:51:17 +0200 Subject: [PATCH 6/6] Rename notes to manuscript notes --- novelwriter/core/buildsettings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/novelwriter/core/buildsettings.py b/novelwriter/core/buildsettings.py index 0972dd71..6129de05 100644 --- a/novelwriter/core/buildsettings.py +++ b/novelwriter/core/buildsettings.py @@ -147,7 +147,7 @@ SETTINGS_LABELS = { "text.includeSynopsis": QT_TRANSLATE_NOOP("Builds", "Include Synopsis"), "text.includeComments": QT_TRANSLATE_NOOP("Builds", "Include Comments"), "text.includeStory": QT_TRANSLATE_NOOP("Builds", "Include Story Structure"), - "text.includeNotes": QT_TRANSLATE_NOOP("Builds", "Include Notes"), + "text.includeNotes": QT_TRANSLATE_NOOP("Builds", "Include Manuscript Notes"), "text.includeKeywords": QT_TRANSLATE_NOOP("Builds", "Include Keywords"), "text.includeBodyText": QT_TRANSLATE_NOOP("Builds", "Include Body Text"), "text.ignoredKeywords": QT_TRANSLATE_NOOP("Builds", "Ignore These Keywords"),