Use label for template title (#2477)

This commit is contained in:
Veronica Berglyd Olsen
2025-07-17 15:47:45 +02:00
committed by GitHub
9 changed files with 47 additions and 25 deletions
+3
View File
@@ -166,6 +166,9 @@ a character note template, you can add a **Templates** root folder to your proje
added to this root folder will show up in the **Add Item** menu in the project tree toolbar. When
selected, a new document is created with its content copied from the chosen template.
If the first line of the template file is a title line, the title text will be replaced with the
text of the document label when it is first created.
.. versionadded:: 2.3
+9 -1
View File
@@ -224,7 +224,7 @@ class NWProject:
return True
def copyFileContent(self, tHandle: str, sHandle: str) -> bool:
def copyFileContent(self, tHandle: str, sHandle: str, newTitle: str | None = None) -> bool:
"""Copy content to a new document after it is created. This
will not run if the file exists and is not empty.
"""
@@ -239,6 +239,14 @@ class NWProject:
logger.debug("Populating '%s' with text from '%s'", tHandle, sHandle)
text = self._storage.getDocumentText(sHandle)
if (
newTitle and (lines := text.split("\n")) and lines
and lines[0].startswith(("# ", "## ", "### ", "#### ", "#! ", "##! ", "###! "))
):
prefix, _, _ = lines[0].partition(" ")
lines[0] = f"{prefix} {newTitle}"
text = "\n".join(lines)
self._storage.getDocument(tHandle).writeDocument(text)
sItem.setLayout(tItem.itemLayout)
self._index.reIndexHandle(tHandle)
+1 -1
View File
@@ -667,7 +667,7 @@ class GuiProjectTree(QTreeView):
if itemType == nwItemType.FILE:
if tHandle := SHARED.project.newFile(newLabel, sHandle, sPos):
if copyDoc:
SHARED.project.copyFileContent(tHandle, copyDoc)
SHARED.project.copyFileContent(tHandle, copyDoc, newLabel)
elif hLevel > 0:
SHARED.project.writeNewFile(tHandle, hLevel, not nNote)
SHARED.project.index.reIndexHandle(tHandle)
+2 -2
View File
@@ -1,7 +1,7 @@
%%~name: Scene
%%~path: 09f22ae8369f7/5aec885635c85
%%~path: f4ed1ae756a1f/5aec885635c85
%%~kind: TEMPLATE/DOCUMENT
%%~hash: 05c9750b00020f8cf9a7bc7362bf19681022cc03
%%~hash: 537fb0132a80101060b40b42ad8875ebbcb40a5b
%%~date: 2024-02-02 10:58:47/2024-02-02 10:59:32
### Scene
+2 -2
View File
@@ -1,7 +1,7 @@
%%~name: Character Note
%%~path: 09f22ae8369f7/5ee8aebcdebc9
%%~path: f4ed1ae756a1f/5ee8aebcdebc9
%%~kind: TEMPLATE/NOTE
%%~hash: 6230bb6f796e708a72f4c29dc79d63dc90a47ba5
%%~hash: 0d33e49259d13a6be92b493d2122dcacc677eb0b
%%~date: 2024-02-02 10:54:52/2024-02-02 10:56:05
# Character Name
+15 -15
View File
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.8a2" hexVersion="0x020800a2" fileVersion="1.5" fileRevision="6" timeStamp="2025-07-16 16:35:53">
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="2283" autoCount="304" editTime="100296">
<novelWriterXML appVersion="2.8a2" hexVersion="0x020800a2" fileVersion="1.5" fileRevision="6" timeStamp="2025-07-17 15:43:44">
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="2284" autoCount="304" editTime="100347">
<name>Sample Project</name>
<author>Jane Smith</author>
</project>
@@ -42,19 +42,19 @@
<name status="sc24b8f" import="ia857f0">Novel</name>
</item>
<item handle="53b69b83cdafc" parent="7031beac91f75" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="no" heading="H1" charCount="148" wordCount="29" paraCount="4" cursorPos="178" />
<meta expanded="no" heading="H1" charCount="148" wordCount="29" paraCount="4" cursorPos="80" />
<name status="sc24b8f" import="ia857f0" active="yes">Title Page</name>
</item>
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="no" heading="H0" charCount="233" wordCount="47" paraCount="2" cursorPos="194" />
<meta expanded="no" heading="H0" charCount="233" wordCount="47" paraCount="2" cursorPos="21" />
<name status="sf12341" import="ia857f0" active="yes">Page</name>
</item>
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="no" heading="H1" charCount="26" wordCount="6" paraCount="1" cursorPos="36" />
<meta expanded="no" heading="H1" charCount="26" wordCount="6" paraCount="1" cursorPos="11" />
<name status="s90e6c9" import="ia857f0" active="yes">Part One</name>
</item>
<item handle="6a2d6d5f4f401" parent="7031beac91f75" root="7031beac91f75" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="yes" heading="H2" charCount="95" wordCount="18" paraCount="1" cursorPos="47" />
<meta expanded="yes" heading="H2" charCount="95" wordCount="18" paraCount="1" cursorPos="16" />
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
</item>
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
@@ -62,19 +62,19 @@
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
</item>
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="no" heading="H3" charCount="548" wordCount="108" paraCount="3" cursorPos="650" />
<meta expanded="no" heading="H3" charCount="548" wordCount="108" paraCount="3" cursorPos="18" />
<name status="s90e6c9" import="ia857f0" active="yes">Another Scene</name>
</item>
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="no" heading="H2" charCount="617" wordCount="101" paraCount="3" cursorPos="1182" />
<meta expanded="no" heading="H2" charCount="617" wordCount="101" paraCount="3" cursorPos="413" />
<name status="s78ea90" import="ia857f0" active="yes">Interlude</name>
</item>
<item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE">
<meta expanded="no" heading="H1" charCount="1909" wordCount="346" paraCount="7" cursorPos="1940" />
<meta expanded="no" heading="H1" charCount="1909" wordCount="346" paraCount="7" cursorPos="22" />
<name status="sf24ce6" import="ia857f0" active="no">A Note on Structure</name>
</item>
<item handle="88706ddc78b1b" parent="7031beac91f75" root="7031beac91f75" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="yes" heading="H2" charCount="139" wordCount="28" paraCount="1" cursorPos="356" />
<meta expanded="yes" heading="H2" charCount="139" wordCount="28" paraCount="1" cursorPos="238" />
<name status="s90e6c9" import="ia857f0" active="yes">Chapter Two</name>
</item>
<item handle="ae7339df26ded" parent="88706ddc78b1b" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
@@ -86,11 +86,11 @@
<name status="sf12341" import="ia857f0">Sequel</name>
</item>
<item handle="bacb7059e3083" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="no" heading="H1" charCount="27" wordCount="5" paraCount="1" cursorPos="41" />
<meta expanded="no" heading="H1" charCount="27" wordCount="5" paraCount="1" cursorPos="99" />
<name status="sc24b8f" import="ia857f0" active="yes">Title Page</name>
</item>
<item handle="a520879ca0b45" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="no" heading="H2" charCount="299" wordCount="55" paraCount="2" cursorPos="29" />
<meta expanded="no" heading="H2" charCount="299" wordCount="55" paraCount="2" cursorPos="385" />
<name status="s8ae72a" import="ia857f0" active="yes">Chapter One</name>
</item>
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="2" type="ROOT" class="CHARACTER">
@@ -102,11 +102,11 @@
<name status="sf12341" import="ia857f0">Main Characters</name>
</item>
<item handle="14298de4d9524" parent="f7e2d9f330615" root="f6622b4617424" order="0" type="FILE" class="CHARACTER" layout="NOTE">
<meta expanded="no" heading="H1" charCount="49" wordCount="9" paraCount="1" cursorPos="40" />
<meta expanded="no" heading="H1" charCount="49" wordCount="9" paraCount="1" cursorPos="101" />
<name status="sf12341" import="i2d7a54" active="yes">John Smith</name>
</item>
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="f6622b4617424" order="1" type="FILE" class="CHARACTER" layout="NOTE">
<meta expanded="no" heading="H1" charCount="55" wordCount="9" paraCount="1" cursorPos="40" />
<meta expanded="no" heading="H1" charCount="55" wordCount="9" paraCount="1" cursorPos="106" />
<name status="sf12341" import="i56be10" active="yes">Jane Smith</name>
</item>
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
@@ -118,7 +118,7 @@
<name status="sf12341" import="i2d7a54" active="yes">Earth</name>
</item>
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="15c4492bd5107" order="1" type="FILE" class="WORLD" layout="NOTE">
<meta expanded="no" heading="H1" charCount="115" wordCount="24" paraCount="1" cursorPos="0" />
<meta expanded="no" heading="H1" charCount="115" wordCount="24" paraCount="1" cursorPos="133" />
<name status="sf12341" import="i4a1d39" active="yes">Space</name>
</item>
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="15c4492bd5107" order="2" type="FILE" class="WORLD" layout="NOTE">
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.8a0" hexVersion="0x020800a0" fileVersion="1.5" fileRevision="6" timeStamp="2025-06-12 18:02:57">
<novelWriterXML appVersion="2.8a2" hexVersion="0x020800a2" fileVersion="1.5" fileRevision="6" timeStamp="2025-07-17 15:27:23">
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
<name>New Project</name>
<author>Jane Doe</author>
@@ -22,13 +22,13 @@
<entry key="s000003" count="0" color="green" shape="STAR">Finished</entry>
</status>
<importance>
<entry key="i000004" count="5" color="purple" shape="SQUARE">New</entry>
<entry key="i000004" count="6" color="purple" shape="SQUARE">New</entry>
<entry key="i000005" count="0" color="purple" shape="BLOCK_2">Minor</entry>
<entry key="i000006" count="0" color="purple" shape="BLOCK_3">Major</entry>
<entry key="i000007" count="0" color="purple" shape="BLOCK_4">Main</entry>
</importance>
</settings>
<content items="12" novelWords="10" notesWords="6" novelChars="45" notesChars="22">
<content items="13" novelWords="10" notesWords="9" novelChars="45" notesChars="33">
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
<meta expanded="no" />
<name status="s000000" import="i000004">Novel</name>
@@ -73,6 +73,10 @@
<meta expanded="no" heading="H1" charCount="11" wordCount="3" paraCount="1" cursorPos="0" />
<name status="s000000" import="i000004" active="yes">John</name>
</item>
<item handle="0000000000014" parent="000000000000a" root="000000000000a" order="2" type="FILE" class="CHARACTER" layout="NOTE">
<meta expanded="no" heading="H1" charCount="11" wordCount="3" paraCount="1" cursorPos="0" />
<name status="s000000" import="i000004" active="yes">Mike</name>
</item>
<item handle="000000000000b" parent="None" root="000000000000b" order="3" type="ROOT" class="WORLD">
<meta expanded="no" />
<name status="s000000" import="i000004">Locations</name>
+7
View File
@@ -100,6 +100,7 @@ def testCoreProject_NewFileFolder(monkeypatch, fncPath, tstPaths, mockGUI, mockR
bHandle = "0000000000011"
cHandle = "0000000000012"
dHandle = "0000000000013"
eHandle = "0000000000014"
xHandle = "1234567890abc"
# Invalid call
@@ -111,11 +112,13 @@ def testCoreProject_NewFileFolder(monkeypatch, fncPath, tstPaths, mockGUI, mockR
assert project.newFile("Hello", aHandle) == bHandle
assert project.newFile("Jane", C.hCharRoot) == cHandle
assert project.newFile("John", C.hCharRoot) == dHandle
assert project.newFile("Mike", C.hCharRoot) == eHandle
assert aHandle in project.tree
assert bHandle in project.tree
assert cHandle in project.tree
assert dHandle in project.tree
assert eHandle in project.tree
# Write to file, failed
assert project.writeNewFile("blabla", 1, True) is False # Not a handle
@@ -151,6 +154,10 @@ def testCoreProject_NewFileFolder(monkeypatch, fncPath, tstPaths, mockGUI, mockR
assert dText == cText
# Copy file content, and update title
assert project.copyFileContent(eHandle, cHandle, "Lars") is True
assert project.storage.getDocument(eHandle).readDocument() == "# Lars\n\nHi Jane\n\n"
# Save, close and check
assert project.projChanged is True
assert project.saveProject() is True
+1 -1
View File
@@ -1454,7 +1454,7 @@ def testGuiProjTree_Templates(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
nwNewCharacter = project.tree[hNewCharacter]
assert nwNewCharacter is not None
assert nwNewCharacter.itemName == "Note"
assert project.storage.getDocument(hNewCharacter).readDocument() == "# Jane\n\n@tag: Jane\n\n"
assert project.storage.getDocument(hNewCharacter).readDocument() == "# Note\n\n@tag: Jane\n\n"
# Clearing the menu and rebuilding it should work
projBar.mTemplates.clearMenu()