Add hard scene break format (#1753)

This commit is contained in:
Veronica Berglyd Olsen
2024-03-13 11:11:59 +01:00
committed by GitHub
22 changed files with 270 additions and 125 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 70 KiB

+4
View File
@@ -75,6 +75,10 @@ Organising Your Text
Keep in mind that this is not what the section heading is intended for, so the app will not Keep in mind that this is not what the section heading is intended for, so the app will not
understand the section heading as a scene, but it will be formatted correctly in the manuscript. understand the section heading as a scene, but it will be formatted correctly in the manuscript.
.. versionadded:: 2.4
You can now distinguish between soft and hard scene breaks with a modified scene header. See
:ref:`a_manuscript_settings_head_hard` for more details.
Other Tools Other Tools
=========== ===========
+16
View File
@@ -63,6 +63,8 @@ In the figure, the green arrow icon and the blue pin icon indicates which docume
and the red forbidden icon indicates that a document is explicitly excluded. and the red forbidden icon indicates that a document is explicitly excluded.
.. _a_manuscript_settings_head:
Formatting Headings Formatting Headings
------------------- -------------------
@@ -86,6 +88,7 @@ Any text you add that isn't highlighted in colours will remain in your formatted
You can preview the result of these format strings by clicking :guilabel:`Apply`, and then clicking You can preview the result of these format strings by clicking :guilabel:`Apply`, and then clicking
:guilabel:`Preview` in the :guilabel:`Manuscript Build` tool main window. :guilabel:`Preview` in the :guilabel:`Manuscript Build` tool main window.
Scene Separators Scene Separators
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
@@ -99,6 +102,19 @@ the desired separator text as the format. If the format is any piece of static t
be treated as a separator. be treated as a separator.
.. _a_manuscript_settings_head_hard:
Hard and Soft Scenes
^^^^^^^^^^^^^^^^^^^^
If you wish to distinguish between so-called soft and hard scene breaks, where a hard scene break
is understood as a scene break that changes point-of-view character, you can use the modified scene
heading format in your text. You can then give these headings a different formatting in the
:guilabel:`Headings` settings.
See :ref:`a_fmt_head` for more info on how to format headings in your text.
Output Settings Output Settings
--------------- ---------------
+6 -1
View File
@@ -68,7 +68,7 @@ correctly to produce the intended result. See :ref:`a_struct_heads` for more det
Heading level four. For novel documents, the level indicates the start of a new section. Section Heading level four. For novel documents, the level indicates the start of a new section. Section
titles can be replaced by separators or ignored completely when building the manuscript. titles can be replaced by separators or ignored completely when building the manuscript.
For headings level one and two, adding a ``!`` modifies its meaning: For headings level one through three, adding a ``!`` modifies its meaning:
``#! Title Text`` ``#! Title Text``
This tells the build tool that the level one heading is intended to be used for the novel's or This tells the build tool that the level one heading is intended to be used for the novel's or
@@ -81,6 +81,11 @@ For headings level one and two, adding a ``!`` modifies its meaning:
chapter numbers are being used. Such titles are useful for a prologue for instance. See chapter numbers are being used. Such titles are useful for a prologue for instance. See
:ref:`a_struct_heads_unnum` for more details. :ref:`a_struct_heads_unnum` for more details.
``###! Title Text``
This is an alternative scene heading that can be formatted differently in the
:guilabel:`Manuscript Build` tool. It is intended for separating "soft" and "hard" scene breaks.
Otherwise, it behaves identically to a regular scene heading.
.. note:: .. note::
The space after the ``#`` or ``!`` character is mandatory. The syntax highlighter will change The space after the ``#`` or ``!`` character is mandatory. The syntax highlighter will change
colour and font size when the heading is correctly formatted. colour and font size when the heading is correctly formatted.
+11 -7
View File
@@ -56,8 +56,10 @@ SETTINGS_TEMPLATE = {
"headings.fmtChapter": (str, nwHeadFmt.TITLE), "headings.fmtChapter": (str, nwHeadFmt.TITLE),
"headings.fmtUnnumbered": (str, nwHeadFmt.TITLE), "headings.fmtUnnumbered": (str, nwHeadFmt.TITLE),
"headings.fmtScene": (str, "* * *"), "headings.fmtScene": (str, "* * *"),
"headings.fmtHardScene": (str, ""),
"headings.fmtSection": (str, ""), "headings.fmtSection": (str, ""),
"headings.hideScene": (bool, False), "headings.hideScene": (bool, False),
"headings.hideHardScene": (bool, False),
"headings.hideSection": (bool, True), "headings.hideSection": (bool, True),
"headings.centerTitle": (bool, True), "headings.centerTitle": (bool, True),
"headings.centerChapter": (bool, False), "headings.centerChapter": (bool, False),
@@ -100,13 +102,15 @@ SETTINGS_LABELS = {
"filter.includeInactive": QT_TRANSLATE_NOOP("Builds", "Inactive Documents"), "filter.includeInactive": QT_TRANSLATE_NOOP("Builds", "Inactive Documents"),
"headings": QT_TRANSLATE_NOOP("Builds", "Headings"), "headings": QT_TRANSLATE_NOOP("Builds", "Headings"),
"headings.fmtTitle": QT_TRANSLATE_NOOP("Builds", "Title Headings"), "headings.fmtTitle": QT_TRANSLATE_NOOP("Builds", "Partition Format"),
"headings.fmtChapter": QT_TRANSLATE_NOOP("Builds", "Chapter Headings"), "headings.fmtChapter": QT_TRANSLATE_NOOP("Builds", "Chapter Format"),
"headings.fmtUnnumbered": QT_TRANSLATE_NOOP("Builds", "Unnumbered Headings"), "headings.fmtUnnumbered": QT_TRANSLATE_NOOP("Builds", "Unnumbered Format"),
"headings.fmtScene": QT_TRANSLATE_NOOP("Builds", "Scene Headings"), "headings.fmtScene": QT_TRANSLATE_NOOP("Builds", "Scene Format"),
"headings.fmtSection": QT_TRANSLATE_NOOP("Builds", "Section Headings"), "headings.fmtHardScene": QT_TRANSLATE_NOOP("Builds", "Hard Scene Format"),
"headings.hideScene": QT_TRANSLATE_NOOP("Builds", "Hide Scene Headings"), "headings.fmtSection": QT_TRANSLATE_NOOP("Builds", "Section Format"),
"headings.hideSection": QT_TRANSLATE_NOOP("Builds", "Hide Section Headings"), "headings.hideScene": QT_TRANSLATE_NOOP("Builds", "Hide Scene Breaks"),
"headings.hideHardScene": QT_TRANSLATE_NOOP("Builds", "Hide Hard Scene Breaks"),
"headings.hideSection": QT_TRANSLATE_NOOP("Builds", "Hide Section Breaks"),
"text.grpContent": QT_TRANSLATE_NOOP("Builds", "Text Content"), "text.grpContent": QT_TRANSLATE_NOOP("Builds", "Text Content"),
"text.includeSynopsis": QT_TRANSLATE_NOOP("Builds", "Include Synopsis"), "text.includeSynopsis": QT_TRANSLATE_NOOP("Builds", "Include Synopsis"),
+4
View File
@@ -282,6 +282,10 @@ class NWBuildDocument:
self._build.getStr("headings.fmtScene"), self._build.getStr("headings.fmtScene"),
self._build.getBool("headings.hideScene") self._build.getBool("headings.hideScene")
) )
bldObj.setHardSceneFormat(
self._build.getStr("headings.fmtHardScene"),
self._build.getBool("headings.hideHardScene")
)
bldObj.setSectionFormat( bldObj.setSectionFormat(
self._build.getStr("headings.fmtSection"), self._build.getStr("headings.fmtSection"),
self._build.getBool("headings.hideSection") self._build.getBool("headings.hideSection")
+2
View File
@@ -396,6 +396,8 @@ class NWIndex:
return "H1", line[3:].strip() return "H1", line[3:].strip()
elif line.startswith("##! "): elif line.startswith("##! "):
return "H2", line[4:].strip() return "H2", line[4:].strip()
elif line.startswith("###! "):
return "H3", line[5:].strip()
return "H0", "" return "H0", ""
def _indexWordCounts(self, tHandle: str, text: str, sTitle: str) -> None: def _indexWordCounts(self, tHandle: str, text: str, sTitle: str) -> None:
+51 -66
View File
@@ -156,9 +156,11 @@ class Tokenizer(ABC):
self._fmtChapter = nwHeadFmt.TITLE # Formatting for numbered chapters self._fmtChapter = nwHeadFmt.TITLE # Formatting for numbered chapters
self._fmtUnNum = nwHeadFmt.TITLE # Formatting for unnumbered chapters self._fmtUnNum = nwHeadFmt.TITLE # Formatting for unnumbered chapters
self._fmtScene = nwHeadFmt.TITLE # Formatting for scenes self._fmtScene = nwHeadFmt.TITLE # Formatting for scenes
self._fmtHScene = nwHeadFmt.TITLE # Formatting for hard scenes
self._fmtSection = nwHeadFmt.TITLE # Formatting for sections self._fmtSection = nwHeadFmt.TITLE # Formatting for sections
self._hideScene = False # Do not include scene headings self._hideScene = False # Do not include scene headings
self._hideHScene = False # Do not include hard scene headings
self._hideSection = False # Do not include section headings self._hideSection = False # Do not include section headings
self._linkHeaders = False # Add an anchor before headings self._linkHeaders = False # Add an anchor before headings
@@ -254,6 +256,12 @@ class Tokenizer(ABC):
self._hideScene = hide self._hideScene = hide
return return
def setHardSceneFormat(self, hFormat: str, hide: bool) -> None:
"""Set the hard scene format pattern and hidden status."""
self._fmtHScene = hFormat.strip()
self._hideHScene = hide
return
def setSectionFormat(self, hFormat: str, hide: bool) -> None: def setSectionFormat(self, hFormat: str, hide: bool) -> None:
"""Set the section format pattern and hidden status.""" """Set the section format pattern and hidden status."""
self._fmtSection = hFormat.strip() self._fmtSection = hFormat.strip()
@@ -484,7 +492,7 @@ class Tokenizer(ABC):
# Check Line Format # Check Line Format
# ================= # =================
if aLine[0] == "[": if aLine.startswith("["):
# Special Formats # Special Formats
# =============== # ===============
# Parse special formatting line. This must be a separate if # Parse special formatting line. This must be a separate if
@@ -513,13 +521,13 @@ class Tokenizer(ABC):
] ]
continue continue
if aLine[0] == "%": if aLine.startswith("%"):
# Comments # Comments
# ======== # ========
# All style comments are processed and the exact type exact # All style comments are processed and the exact type exact
# style extracted. Ignored comments on the '%~' format are # style extracted. Ignored comments on the '%~' format are
# skipped completely. # skipped completely.
if aLine[1] == "~": if aLine.startswith("%~"):
continue continue
cStyle, cText, _ = processComment(aLine) cStyle, cText, _ = processComment(aLine)
@@ -542,7 +550,7 @@ class Tokenizer(ABC):
if self._doComments and self._keepMarkdown: if self._doComments and self._keepMarkdown:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine[0] == "@": elif aLine.startswith("@"):
# Keywords # Keywords
# ======== # ========
# Only valid keyword lines are parsed, and any ignored keywords # Only valid keyword lines are parsed, and any ignored keywords
@@ -556,56 +564,70 @@ class Tokenizer(ABC):
if self._doKeywords and self._keepMarkdown: if self._doKeywords and self._keepMarkdown:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine[:2] == "# ": elif aLine.startswith(("# ", "#! ")):
# Partition Headings # Title or Partition Headings
# ================== # ===========================
# Main titles are allowed in any document, and they are always
# centred and start on a new page. For novel documents, we also
# reset all counters when such a title is encountered.
# Partition headings are only formatted in novel documents, and # Partition headings are only formatted in novel documents, and
# otherwise unchanged. Scene separators are disabled # otherwise unchanged. Scene separators are disabled
# immediately after partitions, and scene numbers are reset. # immediately after partitions, and scene numbers are reset.
isPlain = aLine.startswith("# ")
nHead += 1 nHead += 1
tText = aLine[2:].strip() tText = aLine[2:].strip()
tStyle = self.A_NONE tType = self.T_HEAD1 if isPlain else self.T_TITLE
tStyle = self.A_NONE if isPlain else (self.A_PBB | self.A_CENTRE)
if self._isNovel: if self._isNovel:
tText = self._hFormatter.apply(self._fmtTitle, tText, nHead) if isPlain:
tStyle = self._titleStyle tText = self._hFormatter.apply(self._fmtTitle, tText, nHead)
tStyle = self._titleStyle
self._hFormatter.resetScene()
else:
self._hFormatter.resetAll()
self._noSep = True self._noSep = True
self._hFormatter.resetScene()
self._tokens.append(( self._tokens.append((
self.T_HEAD1, nHead, tText, [], tStyle tType, nHead, tText, [], tStyle
)) ))
if self._keepMarkdown: if self._keepMarkdown:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine[:3] == "## ": elif aLine.startswith(("## ", "##! ")):
# Chapter Headings # (Unnumbered) Chapter Headings
# ================ # =============================
# Chapter headings are only formatted in novel documents, and # Chapter headings are only formatted in novel documents, and
# otherwise unchanged. Chapter numbers are bumped before the # otherwise unchanged. Chapter numbers are bumped before the
# heading is formatted. Scene separators are disabled # heading is formatted. Scene separators are disabled
# immediately after chapter headings, and scene numbers are # immediately after chapter headings, and scene numbers are
# reset. # reset. Unnumbered chapters are only meaningful in Novel docs,
# so if we're in a note, we keep them as level 2 headings.
isPlain = aLine.startswith("## ")
nHead += 1 nHead += 1
tText = aLine[3:].strip() tText = aLine[3:].strip()
tType = self.T_HEAD2
tStyle = self.A_NONE tStyle = self.A_NONE
tFormat = self._fmtChapter if isPlain else self._fmtUnNum
if self._isNovel: if self._isNovel:
self._hFormatter.incChapter() if isPlain:
tText = self._hFormatter.apply(self._fmtChapter, tText, nHead) self._hFormatter.incChapter()
tText = self._hFormatter.apply(tFormat, tText, nHead)
tType = self.T_HEAD2 if isPlain else self.T_UNNUM
tStyle = self._chapterStyle tStyle = self._chapterStyle
self._noSep = True self._noSep = True
self._hFormatter.resetScene() self._hFormatter.resetScene()
self._tokens.append(( self._tokens.append((
self.T_HEAD2, nHead, tText, [], tStyle tType, nHead, tText, [], tStyle
)) ))
if self._keepMarkdown: if self._keepMarkdown:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine[:4] == "### ": elif aLine.startswith(("### ", "###! ")):
# Scene Headings # (Hard) Scene Headings
# ============== # =====================
# Scene headings in novel documents are treated as centred # Scene headings in novel documents are treated as centred
# separators if the formatting does not change the text. If the # separators if the formatting does not change the text. If the
# format is empty, the scene can be hidden or a blank paragraph # format is empty, the scene can be hidden or a blank paragraph
@@ -614,19 +636,22 @@ class Tokenizer(ABC):
# separators immediately after other titles. Scene numbers are # separators immediately after other titles. Scene numbers are
# always incremented before formatting. For notes, the heading # always incremented before formatting. For notes, the heading
# is unchanged. # is unchanged.
isPlain = aLine.startswith("### ")
nHead += 1 nHead += 1
tText = aLine[4:].strip() tText = aLine[4:].strip()
tType = self.T_HEAD3 tType = self.T_HEAD3
tStyle = self.A_NONE tStyle = self.A_NONE
sHide = self._hideScene if isPlain else self._hideHScene
tFormat = self._fmtScene if isPlain else self._fmtHScene
if self._isNovel: if self._isNovel:
self._hFormatter.incScene() self._hFormatter.incScene()
tText = self._hFormatter.apply(self._fmtScene, tText, nHead) tText = self._hFormatter.apply(tFormat, tText, nHead)
tStyle = self._sceneStyle tStyle = self._sceneStyle
if tText == "": if tText == "":
tType = self.T_EMPTY if self._noSep or self._hideScene else self.T_SKIP tType = self.T_EMPTY if self._noSep or sHide else self.T_SKIP
tStyle = self.A_NONE tStyle = self.A_NONE
elif tText == self._fmtScene: elif tText == tFormat:
tText = "" if self._noSep else tText tText = "" if self._noSep else tText
tType = self.T_EMPTY if self._noSep else self.T_SEP tType = self.T_EMPTY if self._noSep else self.T_SEP
tStyle = self.A_NONE if self._noSep else self.A_CENTRE tStyle = self.A_NONE if self._noSep else self.A_CENTRE
@@ -638,7 +663,7 @@ class Tokenizer(ABC):
if self._keepMarkdown: if self._keepMarkdown:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine[:5] == "#### ": elif aLine.startswith("#### "):
# Section Headings # Section Headings
# ================= # =================
# Section headings in novel docs are treated as centred # Section headings in novel docs are treated as centred
@@ -664,46 +689,6 @@ class Tokenizer(ABC):
if self._keepMarkdown: if self._keepMarkdown:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine[:3] == "#! ":
# Main Title
# ==========
# Main titles are allowed in any document, and they are always
# centred and start on a new page. For novel documents, we also
# reset all counters when such a title is encountered.
nHead += 1
self._tokens.append((
self.T_TITLE, nHead, aLine[3:].strip(), [], self.A_PBB | self.A_CENTRE
))
if self._isNovel:
self._noSep = True
self._hFormatter.resetAll()
if self._keepMarkdown:
tmpMarkdown.append(f"{aLine}\n")
elif aLine[:4] == "##! ":
# Unnumbered Chapter Headings
# ===========================
# Unnumbered chapters are only meaningful in Novel docs, so if
# we're in a note, we convert them to a plain level 2 heading.
nHead += 1
tText = aLine[4:].strip()
tType = self.T_HEAD2
tStyle = self.A_NONE
if self._isNovel:
tText = self._hFormatter.apply(self._fmtUnNum, tText, nHead)
tType = self.T_UNNUM
tStyle = self._chapterStyle
self._noSep = True
self._hFormatter.resetScene()
self._tokens.append((
tType, nHead, tText, [], tStyle
))
if self._keepMarkdown:
tmpMarkdown.append(f"{aLine}\n")
else: else:
# Text Lines # Text Lines
# ========== # ==========
+16 -15
View File
@@ -112,21 +112,22 @@ class nwDocAction(Enum):
BLOCK_TXT = 19 BLOCK_TXT = 19
BLOCK_TTL = 20 BLOCK_TTL = 20
BLOCK_UNN = 21 BLOCK_UNN = 21
REPL_SNG = 22 BLOCK_HSC = 22
REPL_DBL = 23 REPL_SNG = 23
RM_BREAKS = 24 REPL_DBL = 24
ALIGN_L = 25 RM_BREAKS = 25
ALIGN_C = 26 ALIGN_L = 26
ALIGN_R = 27 ALIGN_C = 27
INDENT_L = 28 ALIGN_R = 28
INDENT_R = 29 INDENT_L = 29
SC_ITALIC = 30 INDENT_R = 30
SC_BOLD = 31 SC_ITALIC = 31
SC_STRIKE = 32 SC_BOLD = 32
SC_ULINE = 33 SC_STRIKE = 33
SC_MARK = 34 SC_ULINE = 34
SC_SUP = 35 SC_MARK = 35
SC_SUB = 36 SC_SUP = 36
SC_SUB = 37
# END Enum nwDocAction # END Enum nwDocAction
+8
View File
@@ -744,6 +744,8 @@ class GuiDocEditor(QPlainTextEdit):
self._formatBlock(nwDocAction.BLOCK_TTL) self._formatBlock(nwDocAction.BLOCK_TTL)
elif action == nwDocAction.BLOCK_UNN: elif action == nwDocAction.BLOCK_UNN:
self._formatBlock(nwDocAction.BLOCK_UNN) self._formatBlock(nwDocAction.BLOCK_UNN)
elif action == nwDocAction.BLOCK_HSC:
self._formatBlock(nwDocAction.BLOCK_HSC)
elif action == nwDocAction.REPL_SNG: elif action == nwDocAction.REPL_SNG:
self._replaceQuotes("'", self._typSQuoteO, self._typSQuoteC) self._replaceQuotes("'", self._typSQuoteO, self._typSQuoteC)
elif action == nwDocAction.REPL_DBL: elif action == nwDocAction.REPL_DBL:
@@ -1651,6 +1653,9 @@ class GuiDocEditor(QPlainTextEdit):
elif text.startswith("##! "): elif text.startswith("##! "):
temp = text[4:] temp = text[4:]
offset = 4 offset = 4
elif text.startswith("###! "):
temp = text[5:]
offset = 5
elif text.startswith(">> "): elif text.startswith(">> "):
temp = text[3:] temp = text[3:]
offset = 3 offset = 3
@@ -1702,6 +1707,9 @@ class GuiDocEditor(QPlainTextEdit):
elif action == nwDocAction.BLOCK_UNN: elif action == nwDocAction.BLOCK_UNN:
text = f"##! {temp}" text = f"##! {temp}"
offset -= 4 offset -= 4
elif action == nwDocAction.BLOCK_HSC:
text = f"###! {temp}"
offset -= 5
elif action == nwDocAction.ALIGN_L: elif action == nwDocAction.ALIGN_L:
text = f"{temp} <<" text = f"{temp} <<"
elif action == nwDocAction.ALIGN_C: elif action == nwDocAction.ALIGN_C:
+5 -1
View File
@@ -300,7 +300,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
# so we force a return here # so we force a return here
return return
elif text.startswith(("# ", "#! ", "## ", "##! ", "### ", "#### ")): elif text.startswith(("# ", "#! ", "## ", "##! ", "### ", "###! ", "#### ")):
self.setCurrentBlockState(self.BLOCK_TITLE) self.setCurrentBlockState(self.BLOCK_TITLE)
if text.startswith("# "): # Heading 1 if text.startswith("# "): # Heading 1
@@ -327,6 +327,10 @@ class GuiDocHighlighter(QSyntaxHighlighter):
self.setFormat(0, 3, self._hStyles["head2h"]) self.setFormat(0, 3, self._hStyles["head2h"])
self.setFormat(3, len(text), self._hStyles["header2"]) self.setFormat(3, len(text), self._hStyles["header2"])
elif text.startswith("###! "): # Hard Scene
self.setFormat(0, 4, self._hStyles["head3h"])
self.setFormat(4, len(text), self._hStyles["header3"])
elif text.startswith("%"): # Comments elif text.startswith("%"): # Comments
self.setCurrentBlockState(self.BLOCK_TEXT) self.setCurrentBlockState(self.BLOCK_TEXT)
cStyle, _, cPos = processComment(text) cStyle, _, cPos = processComment(text)
+6
View File
@@ -735,6 +735,12 @@ class GuiMainMenu(QMenuBar):
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_UNN) lambda: self.requestDocAction.emit(nwDocAction.BLOCK_UNN)
) )
# Format > Hard Scene
self.aFmtHardSc = self.fmtMenu.addAction(self.tr("Hard Scene"))
self.aFmtHardSc.triggered.connect(
lambda: self.requestDocAction.emit(nwDocAction.BLOCK_HSC)
)
# Format > Separator # Format > Separator
self.fmtMenu.addSeparator() self.fmtMenu.addSeparator()
+3
View File
@@ -106,6 +106,9 @@ def standardCounter(text: str) -> tuple[int, int, int]:
elif line[:4] == "##! ": elif line[:4] == "##! ":
line = line[4:] line = line[4:]
countPara = False countPara = False
elif line[:5] == "###! ":
line = line[5:]
countPara = False
wCount += len(line.split()) wCount += len(line.split())
cCount += len(line) cCount += len(line)
+6 -8
View File
@@ -610,16 +610,15 @@ class _DetailsWidget(QWidget):
item.setText(0, build.getLabel("headings")) item.setText(0, build.getLabel("headings"))
item.setText(1, "") item.setText(1, "")
self.listView.addTopLevelItem(item) self.listView.addTopLevelItem(item)
entries = [ for key in [
"headings.fmtTitle", "headings.fmtChapter", "headings.fmtUnnumbered", "headings.fmtTitle", "headings.fmtChapter", "headings.fmtUnnumbered",
"headings.fmtScene", "headings.fmtSection" "headings.fmtScene", "headings.fmtHardScene", "headings.fmtSection"
] ]:
for key in entries:
sub = QTreeWidgetItem() sub = QTreeWidgetItem()
sub.setText(0, build.getLabel(key)) sub.setText(0, build.getLabel(key))
sub.setText(1, hFmt.apply(build.getStr(key), title, 0)) sub.setText(1, hFmt.apply(build.getStr(key), title, 0))
item.addChild(sub) item.addChild(sub)
for key in ["headings.hideScene", "headings.hideSection"]: for key in ["headings.hideScene", "headings.hideHardScene", "headings.hideSection"]:
sub = QTreeWidgetItem() sub = QTreeWidgetItem()
sub.setText(0, build.getLabel(key)) sub.setText(0, build.getLabel(key))
sub.setIcon(1, on if build.getBool(key) else off) sub.setIcon(1, on if build.getBool(key) else off)
@@ -630,11 +629,10 @@ class _DetailsWidget(QWidget):
item.setText(0, build.getLabel("text.grpContent")) item.setText(0, build.getLabel("text.grpContent"))
item.setText(1, "") item.setText(1, "")
self.listView.addTopLevelItem(item) self.listView.addTopLevelItem(item)
entries = [ for key in [
"text.includeSynopsis", "text.includeComments", "text.includeSynopsis", "text.includeComments",
"text.includeKeywords", "text.includeBodyText", "text.includeKeywords", "text.includeBodyText",
] ]:
for key in entries:
sub = QTreeWidgetItem() sub = QTreeWidgetItem()
sub.setText(0, build.getLabel(key)) sub.setText(0, build.getLabel(key))
sub.setIcon(1, on if build.getBool(key) else off) sub.setIcon(1, on if build.getBool(key) else off)
+46 -9
View File
@@ -584,7 +584,8 @@ class _HeadingsTab(NScrollablePage):
EDIT_CHAPTER = 2 EDIT_CHAPTER = 2
EDIT_UNNUM = 3 EDIT_UNNUM = 3
EDIT_SCENE = 4 EDIT_SCENE = 4
EDIT_SECTION = 5 EDIT_HSCENE = 5
EDIT_SECTION = 6
def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None: def __init__(self, buildMain: GuiBuildSettings, build: BuildSettings) -> None:
super().__init__(parent=buildMain) super().__init__(parent=buildMain)
@@ -603,7 +604,7 @@ class _HeadingsTab(NScrollablePage):
self.formatBox.setHorizontalSpacing(vSp) self.formatBox.setHorizontalSpacing(vSp)
# Title Heading # Title Heading
self.lblTitle = QLabel(self._build.getLabel("headings.fmtTitle")) self.lblTitle = QLabel(self._build.getLabel("headings.fmtTitle"), self)
self.fmtTitle = QLineEdit("", self) self.fmtTitle = QLineEdit("", self)
self.fmtTitle.setReadOnly(True) self.fmtTitle.setReadOnly(True)
self.btnTitle = QToolButton(self) self.btnTitle = QToolButton(self)
@@ -619,7 +620,7 @@ class _HeadingsTab(NScrollablePage):
self.formatBox.addLayout(wrapTitle, 0, 1) self.formatBox.addLayout(wrapTitle, 0, 1)
# Chapter Heading # Chapter Heading
self.lblChapter = QLabel(self._build.getLabel("headings.fmtChapter")) self.lblChapter = QLabel(self._build.getLabel("headings.fmtChapter"), self)
self.fmtChapter = QLineEdit("", self) self.fmtChapter = QLineEdit("", self)
self.fmtChapter.setReadOnly(True) self.fmtChapter.setReadOnly(True)
self.btnChapter = QToolButton(self) self.btnChapter = QToolButton(self)
@@ -635,7 +636,7 @@ class _HeadingsTab(NScrollablePage):
self.formatBox.addLayout(wrapChapter, 1, 1) self.formatBox.addLayout(wrapChapter, 1, 1)
# Unnumbered Chapter Heading # Unnumbered Chapter Heading
self.lblUnnumbered = QLabel(self._build.getLabel("headings.fmtUnnumbered")) self.lblUnnumbered = QLabel(self._build.getLabel("headings.fmtUnnumbered"), self)
self.fmtUnnumbered = QLineEdit("", self) self.fmtUnnumbered = QLineEdit("", self)
self.fmtUnnumbered.setReadOnly(True) self.fmtUnnumbered.setReadOnly(True)
self.btnUnnumbered = QToolButton(self) self.btnUnnumbered = QToolButton(self)
@@ -652,7 +653,7 @@ class _HeadingsTab(NScrollablePage):
# Scene Heading # Scene Heading
sceneHideTip = self._build.getLabel("headings.hideScene") sceneHideTip = self._build.getLabel("headings.hideScene")
self.lblScene = QLabel(self._build.getLabel("headings.fmtScene")) self.lblScene = QLabel(self._build.getLabel("headings.fmtScene"), self)
self.fmtScene = QLineEdit("", self) self.fmtScene = QLineEdit("", self)
self.fmtScene.setReadOnly(True) self.fmtScene.setReadOnly(True)
self.btnScene = QToolButton(self) self.btnScene = QToolButton(self)
@@ -677,9 +678,36 @@ class _HeadingsTab(NScrollablePage):
self.formatBox.addLayout(wrapScene, 3, 1) self.formatBox.addLayout(wrapScene, 3, 1)
self.formatBox.addLayout(wrapSceneHide, 3, 2) self.formatBox.addLayout(wrapSceneHide, 3, 2)
# Alt Scene Heading
hardSceneHideTip = self._build.getLabel("headings.hideHardScene")
self.lblHScene = QLabel(self._build.getLabel("headings.fmtHardScene"), self)
self.fmtHScene = QLineEdit("", self)
self.fmtHScene.setReadOnly(True)
self.btnHScene = QToolButton(self)
self.btnHScene.setIcon(SHARED.theme.getIcon("edit"))
self.btnHScene.clicked.connect(lambda: self._editHeading(self.EDIT_HSCENE))
self.hdeHScene = QLabel(self.tr("Hide"))
self.hdeHScene.setToolTip(hardSceneHideTip)
self.swtHScene = NSwitch(self, height=iPx)
self.swtHScene.setToolTip(hardSceneHideTip)
wrapHScene = QHBoxLayout()
wrapHScene.addWidget(self.fmtHScene)
wrapHScene.addWidget(self.btnHScene)
wrapHScene.setSpacing(bSp)
wrapHSceneHide = QHBoxLayout()
wrapHSceneHide.addWidget(self.hdeHScene)
wrapHSceneHide.addWidget(self.swtHScene)
wrapHSceneHide.setSpacing(bSp)
self.formatBox.addWidget(self.lblHScene, 4, 0)
self.formatBox.addLayout(wrapHScene, 4, 1)
self.formatBox.addLayout(wrapHSceneHide, 4, 2)
# Section Heading # Section Heading
sectionHideTip = self._build.getLabel("headings.hideSection") sectionHideTip = self._build.getLabel("headings.hideSection")
self.lblSection = QLabel(self._build.getLabel("headings.fmtSection")) self.lblSection = QLabel(self._build.getLabel("headings.fmtSection"), self)
self.fmtSection = QLineEdit("", self) self.fmtSection = QLineEdit("", self)
self.fmtSection.setReadOnly(True) self.fmtSection.setReadOnly(True)
self.btnSection = QToolButton(self) self.btnSection = QToolButton(self)
@@ -700,9 +728,9 @@ class _HeadingsTab(NScrollablePage):
wrapSectionHide.addWidget(self.swtSection) wrapSectionHide.addWidget(self.swtSection)
wrapSectionHide.setSpacing(bSp) wrapSectionHide.setSpacing(bSp)
self.formatBox.addWidget(self.lblSection, 4, 0) self.formatBox.addWidget(self.lblSection, 5, 0)
self.formatBox.addLayout(wrapSection, 4, 1) self.formatBox.addLayout(wrapSection, 5, 1)
self.formatBox.addLayout(wrapSectionHide, 4, 2) self.formatBox.addLayout(wrapSectionHide, 5, 2)
# Edit Form # Edit Form
# ========= # =========
@@ -830,8 +858,10 @@ class _HeadingsTab(NScrollablePage):
self.fmtChapter.setText(self._build.getStr("headings.fmtChapter")) self.fmtChapter.setText(self._build.getStr("headings.fmtChapter"))
self.fmtUnnumbered.setText(self._build.getStr("headings.fmtUnnumbered")) self.fmtUnnumbered.setText(self._build.getStr("headings.fmtUnnumbered"))
self.fmtScene.setText(self._build.getStr("headings.fmtScene")) self.fmtScene.setText(self._build.getStr("headings.fmtScene"))
self.fmtHScene.setText(self._build.getStr("headings.fmtHardScene"))
self.fmtSection.setText(self._build.getStr("headings.fmtSection")) self.fmtSection.setText(self._build.getStr("headings.fmtSection"))
self.swtScene.setChecked(self._build.getBool("headings.hideScene")) self.swtScene.setChecked(self._build.getBool("headings.hideScene"))
self.swtHScene.setChecked(self._build.getBool("headings.hideHardScene"))
self.swtSection.setChecked(self._build.getBool("headings.hideSection")) self.swtSection.setChecked(self._build.getBool("headings.hideSection"))
self.centerTitle.setChecked(self._build.getBool("headings.centerTitle")) self.centerTitle.setChecked(self._build.getBool("headings.centerTitle"))
self.centerChapter.setChecked(self._build.getBool("headings.centerChapter")) self.centerChapter.setChecked(self._build.getBool("headings.centerChapter"))
@@ -844,6 +874,7 @@ class _HeadingsTab(NScrollablePage):
def saveContent(self) -> None: def saveContent(self) -> None:
"""Save choices back into build object.""" """Save choices back into build object."""
self._build.setValue("headings.hideScene", self.swtScene.isChecked()) self._build.setValue("headings.hideScene", self.swtScene.isChecked())
self._build.setValue("headings.hideHardScene", self.swtHScene.isChecked())
self._build.setValue("headings.hideSection", self.swtSection.isChecked()) self._build.setValue("headings.hideSection", self.swtSection.isChecked())
self._build.setValue("headings.centerTitle", self.centerTitle.isChecked()) self._build.setValue("headings.centerTitle", self.centerTitle.isChecked())
self._build.setValue("headings.centerChapter", self.centerChapter.isChecked()) self._build.setValue("headings.centerChapter", self.centerChapter.isChecked())
@@ -881,6 +912,9 @@ class _HeadingsTab(NScrollablePage):
elif heading == self.EDIT_SCENE: elif heading == self.EDIT_SCENE:
text = self.fmtScene.text() text = self.fmtScene.text()
label = self._build.getLabel("headings.fmtScene") label = self._build.getLabel("headings.fmtScene")
elif heading == self.EDIT_HSCENE:
text = self.fmtHScene.text()
label = self._build.getLabel("headings.fmtHardScene")
elif heading == self.EDIT_SECTION: elif heading == self.EDIT_SECTION:
text = self.fmtSection.text() text = self.fmtSection.text()
label = self._build.getLabel("headings.fmtSection") label = self._build.getLabel("headings.fmtSection")
@@ -916,6 +950,9 @@ class _HeadingsTab(NScrollablePage):
elif heading == self.EDIT_SCENE: elif heading == self.EDIT_SCENE:
self.fmtScene.setText(text) self.fmtScene.setText(text)
self._build.setValue("headings.fmtScene", text) self._build.setValue("headings.fmtScene", text)
elif heading == self.EDIT_HSCENE:
self.fmtHScene.setText(text)
self._build.setValue("headings.fmtHardScene", text)
elif heading == self.EDIT_SECTION: elif heading == self.EDIT_SECTION:
self.fmtSection.setText(text) self.fmtSection.setText(text)
self._build.setValue("headings.fmtSection", text) self._build.setValue("headings.fmtSection", text)
+3 -3
View File
@@ -1,8 +1,8 @@
%%~name: Another Scene %%~name: Another Scene
%%~path: 6a2d6d5f4f401/bc0cbd2a407f3 %%~path: 6a2d6d5f4f401/bc0cbd2a407f3
%%~kind: NOVEL/DOCUMENT %%~kind: NOVEL/DOCUMENT
%%~hash: b3f9e4b097f7041c77489876577014c435a8be0c %%~hash: d35e2629876e627b1a2418affa40d477022984f8
%%~date: Unknown/2023-08-25 16:51:57 %%~date: Unknown/2024-03-11 22:56:28
### Another Scene ### Another Scene
@pov: John @pov: John
@@ -13,7 +13,7 @@ Adding more scenes to a chapter is as easy as adding more scene files with a lev
In fact, if you wish, you can add all the scenes in the chapter file too. All novelWriter cares about is the level of the headings and the order in which they appear. In fact, if you wish, you can add all the scenes in the chapter file too. All novelWriter cares about is the level of the headings and the order in which they appear.
### More Scenes ###! More Scenes
@pov: Jane @pov: Jane
@focus: John @focus: John
+2 -2
View File
@@ -196,8 +196,8 @@ def testCoreBuildSettings_BuildValues():
assert build.getFloat(floatSetting) == 2.5 assert build.getFloat(floatSetting) == 2.5
# Check labels # Check labels
assert build.getLabel(strSetting) == "Title Headings" assert build.getLabel(strSetting) == "Partition Format"
assert BuildSettings.getLabel(strSetting) == "Title Headings" assert BuildSettings.getLabel(strSetting) == "Partition Format"
# Pack the values # Pack the values
data = build.pack() data = build.pack()
+13
View File
@@ -502,6 +502,19 @@ def testCoreIndex_ScanText(mockGUI, fncPath, mockRnd):
assert index._itemIndex[tHandle]["T0001"].paraCount == 1 # type: ignore assert index._itemIndex[tHandle]["T0001"].paraCount == 1 # type: ignore
assert index._itemIndex[tHandle]["T0001"].synopsis == "" # type: ignore assert index._itemIndex[tHandle]["T0001"].synopsis == "" # type: ignore
assert index.scanText(tHandle, (
"###! Hard Scene\n\n"
"We're no longer following Jane, but John!\n\n"
))
assert index._itemIndex[cHandle]["T0001"].references == {} # type: ignore
assert index._itemIndex[tHandle]["T0001"].level == "H3" # type: ignore
assert index._itemIndex[tHandle]["T0001"].line == 1 # type: ignore
assert index._itemIndex[tHandle]["T0001"].title == "Hard Scene" # type: ignore
assert index._itemIndex[tHandle]["T0001"].charCount == 51 # type: ignore
assert index._itemIndex[tHandle]["T0001"].wordCount == 9 # type: ignore
assert index._itemIndex[tHandle]["T0001"].paraCount == 1 # type: ignore
assert index._itemIndex[tHandle]["T0001"].synopsis == "" # type: ignore
# Page wo/Title # Page wo/Title
# ============= # =============
+32 -12
View File
@@ -47,6 +47,7 @@ def testCoreToken_Setters(mockGUI):
assert tokens._fmtChapter == nwHeadFmt.TITLE assert tokens._fmtChapter == nwHeadFmt.TITLE
assert tokens._fmtUnNum == nwHeadFmt.TITLE assert tokens._fmtUnNum == nwHeadFmt.TITLE
assert tokens._fmtScene == nwHeadFmt.TITLE assert tokens._fmtScene == nwHeadFmt.TITLE
assert tokens._fmtHScene == nwHeadFmt.TITLE
assert tokens._fmtSection == nwHeadFmt.TITLE assert tokens._fmtSection == nwHeadFmt.TITLE
assert tokens._textFont == "Serif" assert tokens._textFont == "Serif"
assert tokens._textSize == 11 assert tokens._textSize == 11
@@ -74,6 +75,7 @@ def testCoreToken_Setters(mockGUI):
tokens.setChapterFormat(f"C: {nwHeadFmt.TITLE}") tokens.setChapterFormat(f"C: {nwHeadFmt.TITLE}")
tokens.setUnNumberedFormat(f"U: {nwHeadFmt.TITLE}") tokens.setUnNumberedFormat(f"U: {nwHeadFmt.TITLE}")
tokens.setSceneFormat(f"S: {nwHeadFmt.TITLE}", True) tokens.setSceneFormat(f"S: {nwHeadFmt.TITLE}", True)
tokens.setHardSceneFormat(f"H: {nwHeadFmt.TITLE}", True)
tokens.setSectionFormat(f"X: {nwHeadFmt.TITLE}", True) tokens.setSectionFormat(f"X: {nwHeadFmt.TITLE}", True)
tokens.setFont("Monospace", 10, True) tokens.setFont("Monospace", 10, True)
tokens.setLineHeight(2.0) tokens.setLineHeight(2.0)
@@ -97,6 +99,7 @@ def testCoreToken_Setters(mockGUI):
assert tokens._fmtChapter == f"C: {nwHeadFmt.TITLE}" assert tokens._fmtChapter == f"C: {nwHeadFmt.TITLE}"
assert tokens._fmtUnNum == f"U: {nwHeadFmt.TITLE}" assert tokens._fmtUnNum == f"U: {nwHeadFmt.TITLE}"
assert tokens._fmtScene == f"S: {nwHeadFmt.TITLE}" assert tokens._fmtScene == f"S: {nwHeadFmt.TITLE}"
assert tokens._fmtHScene == f"H: {nwHeadFmt.TITLE}"
assert tokens._fmtSection == f"X: {nwHeadFmt.TITLE}" assert tokens._fmtSection == f"X: {nwHeadFmt.TITLE}"
assert tokens._textFont == "Monospace" assert tokens._textFont == "Monospace"
assert tokens._textSize == 10 assert tokens._textSize == 10
@@ -1741,7 +1744,7 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
"# Title Two\n\n" "# Title Two\n\n"
"### Scene Three\n\n" "### Scene Three\n\n"
"Text\n\n" "Text\n\n"
"### Scene Four\n\n" "###! Scene Four\n\n"
"Text\n\n" "Text\n\n"
) )
md.setTitleFormat(f"T: {nwHeadFmt.TITLE}") md.setTitleFormat(f"T: {nwHeadFmt.TITLE}")
@@ -1749,13 +1752,14 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
md.setSectionFormat("", True) md.setSectionFormat("", True)
# Static Separator # Static Separator
md.setSceneFormat("* * *", False) md.setSceneFormat("~", False)
md.setHardSceneFormat("* * *", False)
md.tokenizeText() md.tokenizeText()
md.doConvert() md.doConvert()
assert md.result == ( assert md.result == (
"# T: Title One\n\n" "# T: Title One\n\n"
"Text\n\n" "Text\n\n"
"* * *\n\n" "~\n\n"
"Text\n\n" "Text\n\n"
"# T: Title Two\n\n" "# T: Title Two\n\n"
"Text\n\n" "Text\n\n"
@@ -1765,6 +1769,7 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
# Scene Title Formatted # Scene Title Formatted
md.setSceneFormat(f"S: {nwHeadFmt.TITLE}", False) md.setSceneFormat(f"S: {nwHeadFmt.TITLE}", False)
md.setHardSceneFormat(f"H: {nwHeadFmt.TITLE}", False)
md.tokenizeText() md.tokenizeText()
md.doConvert() md.doConvert()
assert md.result == ( assert md.result == (
@@ -1776,7 +1781,7 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
"# T: Title Two\n\n" "# T: Title Two\n\n"
"### S: Scene Three\n\n" "### S: Scene Three\n\n"
"Text\n\n" "Text\n\n"
"### S: Scene Four\n\n" "### H: Scene Four\n\n"
"Text\n\n" "Text\n\n"
) )
@@ -1793,7 +1798,7 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
"## Chapter Two\n\n" "## Chapter Two\n\n"
"### Scene Three\n\n" "### Scene Three\n\n"
"Text\n\n" "Text\n\n"
"### Scene Four\n\n" "###! Scene Four\n\n"
"Text\n\n" "Text\n\n"
) )
md.setTitleFormat(f"T: {nwHeadFmt.TITLE}") md.setTitleFormat(f"T: {nwHeadFmt.TITLE}")
@@ -1802,6 +1807,7 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
# Static Separator # Static Separator
md.setSceneFormat("* * *", False) md.setSceneFormat("* * *", False)
md.setHardSceneFormat("* * *", False)
md.tokenizeText() md.tokenizeText()
md.doConvert() md.doConvert()
assert md.result == ( assert md.result == (
@@ -1818,6 +1824,7 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
# Scene Title Formatted # Scene Title Formatted
md.setSceneFormat(f"S: {nwHeadFmt.TITLE}", False) md.setSceneFormat(f"S: {nwHeadFmt.TITLE}", False)
md.setHardSceneFormat(f"H: {nwHeadFmt.TITLE}", False)
md.tokenizeText() md.tokenizeText()
md.doConvert() md.doConvert()
assert md.result == ( assert md.result == (
@@ -1830,15 +1837,18 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
"## C: Chapter Two\n\n" "## C: Chapter Two\n\n"
"### S: Scene Three\n\n" "### S: Scene Three\n\n"
"Text\n\n" "Text\n\n"
"### S: Scene Four\n\n" "### H: Scene Four\n\n"
"Text\n\n" "Text\n\n"
) )
# Counter Handling, Novel Titles # Counter Handling, Novel Titles
# ============================== # ==============================
# This also checks that only numbered chapters bump the counter
md._text = ( md._text = (
"#! Novel One\n\n" "#! Novel One\n\n"
"##! Prologue\n\n"
"Text\n\n"
"## Chapter One\n\n" "## Chapter One\n\n"
"### Scene One\n\n" "### Scene One\n\n"
"Text\n\n" "Text\n\n"
@@ -1847,9 +1857,11 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
"## Chapter Two\n\n" "## Chapter Two\n\n"
"### Scene Three\n\n" "### Scene Three\n\n"
"Text\n\n" "Text\n\n"
"### Scene Four\n\n" "###! Scene Four\n\n"
"Text\n\n" "Text\n\n"
"#! Novel Two\n\n" "#! Novel Two\n\n"
"##! Prologue\n\n"
"Text\n\n"
"## Chapter One\n\n" "## Chapter One\n\n"
"### Scene One\n\n" "### Scene One\n\n"
"Text\n\n" "Text\n\n"
@@ -1858,13 +1870,17 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
"## Chapter Two\n\n" "## Chapter Two\n\n"
"### Scene Three\n\n" "### Scene Three\n\n"
"Text\n\n" "Text\n\n"
"### Scene Four\n\n" "###! Scene Four\n\n"
"Text\n\n" "Text\n\n"
) )
md.setTitleFormat(f"T: {nwHeadFmt.TITLE}") md.setTitleFormat(f"T: {nwHeadFmt.TITLE}")
md.setChapterFormat(f"C {nwHeadFmt.CH_NUM}: {nwHeadFmt.TITLE}") md.setChapterFormat(f"C {nwHeadFmt.CH_NUM}: {nwHeadFmt.TITLE}")
md.setSceneFormat(f"S {nwHeadFmt.CH_NUM}.{nwHeadFmt.SC_NUM} ({nwHeadFmt.SC_ABS}): " md.setSceneFormat(
f"{nwHeadFmt.TITLE}", False) f"S {nwHeadFmt.CH_NUM}.{nwHeadFmt.SC_NUM} ({nwHeadFmt.SC_ABS}): {nwHeadFmt.TITLE}", False
)
md.setHardSceneFormat(
f"H {nwHeadFmt.CH_NUM}.{nwHeadFmt.SC_NUM} ({nwHeadFmt.SC_ABS}): {nwHeadFmt.TITLE}", False
)
md.setSectionFormat("", True) md.setSectionFormat("", True)
# Two Novel Format # Two Novel Format
@@ -1872,6 +1888,8 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
md.doConvert() md.doConvert()
assert md.result == ( assert md.result == (
"# Novel One\n\n" "# Novel One\n\n"
"## Prologue\n\n"
"Text\n\n"
"## C 1: Chapter One\n\n" "## C 1: Chapter One\n\n"
"### S 1.1 (1): Scene One\n\n" "### S 1.1 (1): Scene One\n\n"
"Text\n\n" "Text\n\n"
@@ -1880,9 +1898,11 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
"## C 2: Chapter Two\n\n" "## C 2: Chapter Two\n\n"
"### S 2.1 (3): Scene Three\n\n" "### S 2.1 (3): Scene Three\n\n"
"Text\n\n" "Text\n\n"
"### S 2.2 (4): Scene Four\n\n" "### H 2.2 (4): Scene Four\n\n"
"Text\n\n" "Text\n\n"
"# Novel Two\n\n" "# Novel Two\n\n"
"## Prologue\n\n"
"Text\n\n"
"## C 1: Chapter One\n\n" "## C 1: Chapter One\n\n"
"### S 1.1 (1): Scene One\n\n" "### S 1.1 (1): Scene One\n\n"
"Text\n\n" "Text\n\n"
@@ -1891,7 +1911,7 @@ def testCoreToken_HeaderCounterAndVisibility(mockGUI):
"## C 2: Chapter Two\n\n" "## C 2: Chapter Two\n\n"
"### S 2.1 (3): Scene Three\n\n" "### S 2.1 (3): Scene Three\n\n"
"Text\n\n" "Text\n\n"
"### S 2.2 (4): Scene Four\n\n" "### H 2.2 (4): Scene Four\n\n"
"Text\n\n" "Text\n\n"
) )
+15 -1
View File
@@ -1134,13 +1134,20 @@ def testGuiEditor_BlockFormatting(qtbot, monkeypatch, nwGUI, projPath, ipsumText
assert nwGUI.docEditor.getText() == "Title\n\n" assert nwGUI.docEditor.getText() == "Title\n\n"
assert nwGUI.docEditor.getCursorPosition() == 2 assert nwGUI.docEditor.getCursorPosition() == 2
# Strip Unnumbered CHapter # Strip Unnumbered Chapter
nwGUI.docEditor.replaceText("##! Title\n\n") nwGUI.docEditor.replaceText("##! Title\n\n")
nwGUI.docEditor.setCursorPosition(5) nwGUI.docEditor.setCursorPosition(5)
assert nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_TXT) is True assert nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_TXT) is True
assert nwGUI.docEditor.getText() == "Title\n\n" assert nwGUI.docEditor.getText() == "Title\n\n"
assert nwGUI.docEditor.getCursorPosition() == 1 assert nwGUI.docEditor.getCursorPosition() == 1
# Strip Hard Scene
nwGUI.docEditor.replaceText("###! Title\n\n")
nwGUI.docEditor.setCursorPosition(6)
assert nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_TXT) is True
assert nwGUI.docEditor.getText() == "Title\n\n"
assert nwGUI.docEditor.getCursorPosition() == 1
# Strip Text # Strip Text
nwGUI.docEditor.replaceText("Generic text\n\n") nwGUI.docEditor.replaceText("Generic text\n\n")
nwGUI.docEditor.setCursorPosition(5) nwGUI.docEditor.setCursorPosition(5)
@@ -1302,6 +1309,13 @@ def testGuiEditor_BlockFormatting(qtbot, monkeypatch, nwGUI, projPath, ipsumText
assert nwGUI.docEditor.getText() == "##! Some text\n\n" assert nwGUI.docEditor.getText() == "##! Some text\n\n"
assert nwGUI.docEditor.getCursorPosition() == 9 assert nwGUI.docEditor.getCursorPosition() == 9
# Hard Scene
nwGUI.docEditor.replaceText("Some text\n\n")
nwGUI.docEditor.setCursorPosition(5)
assert nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_HSC) is True
assert nwGUI.docEditor.getText() == "###! Some text\n\n"
assert nwGUI.docEditor.getCursorPosition() == 10
# Left Indent # Left Indent
nwGUI.docEditor.replaceText("Some text\n\n") nwGUI.docEditor.replaceText("Some text\n\n")
nwGUI.docEditor.setCursorPosition(5) nwGUI.docEditor.setCursorPosition(5)
+5
View File
@@ -126,6 +126,11 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, prjLipsum):
fmtStr = "##! Pellentesque nec erat ut nulla posuere commodo." fmtStr = "##! Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[54:105] == fmtStr assert nwGUI.docEditor.getText()[54:105] == fmtStr
# Hard Scene
nwGUI.mainMenu.aFmtHardSc.activate(QAction.Trigger)
fmtStr = "###! Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[54:106] == fmtStr
# Clear Format # Clear Format
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[54:101] == cleanText assert nwGUI.docEditor.getText()[54:101] == cleanText
@@ -327,12 +327,14 @@ def testBuildSettings_Headings(qtbot: QtBot, nwGUI: GuiMain):
chTitle = f"Chapter: {nwHeadFmt.TITLE}" chTitle = f"Chapter: {nwHeadFmt.TITLE}"
unTitle = f"Interlude: {nwHeadFmt.TITLE}" unTitle = f"Interlude: {nwHeadFmt.TITLE}"
scTitle = f"Scene: {nwHeadFmt.TITLE}" scTitle = f"Scene: {nwHeadFmt.TITLE}"
shTitle = f"Hard Scene: {nwHeadFmt.TITLE}"
sxTitle = f"Section: {nwHeadFmt.TITLE}" sxTitle = f"Section: {nwHeadFmt.TITLE}"
build.setValue("headings.fmtTitle", ttTitle) build.setValue("headings.fmtTitle", ttTitle)
build.setValue("headings.fmtChapter", chTitle) build.setValue("headings.fmtChapter", chTitle)
build.setValue("headings.fmtUnnumbered", unTitle) build.setValue("headings.fmtUnnumbered", unTitle)
build.setValue("headings.fmtScene", scTitle) build.setValue("headings.fmtScene", scTitle)
build.setValue("headings.fmtHardScene", shTitle)
build.setValue("headings.fmtSection", sxTitle) build.setValue("headings.fmtSection", sxTitle)
build.setValue("headings.hideScene", False) build.setValue("headings.hideScene", False)
build.setValue("headings.hideSection", False) build.setValue("headings.hideSection", False)
@@ -351,6 +353,7 @@ def testBuildSettings_Headings(qtbot: QtBot, nwGUI: GuiMain):
assert headTab.fmtChapter.text() == chTitle assert headTab.fmtChapter.text() == chTitle
assert headTab.fmtUnnumbered.text() == unTitle assert headTab.fmtUnnumbered.text() == unTitle
assert headTab.fmtScene.text() == scTitle assert headTab.fmtScene.text() == scTitle
assert headTab.fmtHScene.text() == shTitle
assert headTab.fmtSection.text() == sxTitle assert headTab.fmtSection.text() == sxTitle
assert headTab.swtScene.isChecked() is False assert headTab.swtScene.isChecked() is False
assert headTab.swtSection.isChecked() is False assert headTab.swtSection.isChecked() is False
@@ -396,6 +399,14 @@ def testBuildSettings_Headings(qtbot: QtBot, nwGUI: GuiMain):
headTab.btnApply.click() headTab.btnApply.click()
assert headTab.editTextBox.isEnabled() is False assert headTab.editTextBox.isEnabled() is False
# Hard Scene
headTab.btnHScene.click()
assert headTab._editing == headTab.EDIT_HSCENE
assert headTab.editTextBox.isEnabled() is True
assert headTab.editTextBox.toPlainText() == shTitle
headTab.btnApply.click()
assert headTab.editTextBox.isEnabled() is False
# Section # Section
headTab.btnSection.click() headTab.btnSection.click()
assert headTab._editing == headTab.EDIT_SECTION assert headTab._editing == headTab.EDIT_SECTION
@@ -455,6 +466,11 @@ def testBuildSettings_Headings(qtbot: QtBot, nwGUI: GuiMain):
headTab.btnApply.click() headTab.btnApply.click()
assert build.getStr("headings.fmtScene") == nwHeadFmt.TITLE assert build.getStr("headings.fmtScene") == nwHeadFmt.TITLE
headTab.btnHScene.click()
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
headTab.btnApply.click()
assert build.getStr("headings.fmtHardScene") == nwHeadFmt.TITLE
headTab.btnSection.click() headTab.btnSection.click()
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE) headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
headTab.btnApply.click() headTab.btnApply.click()