Merge branch 'main' into primer
This commit is contained in:
@@ -44,7 +44,7 @@ from novelwriter.common import (
|
|||||||
NWConfigParser, checkInt, checkPath, describeFont, fontMatcher,
|
NWConfigParser, checkInt, checkPath, describeFont, fontMatcher,
|
||||||
formatTimeStamp, processDialogSymbols, simplified
|
formatTimeStamp, processDialogSymbols, simplified
|
||||||
)
|
)
|
||||||
from novelwriter.constants import nwFiles, nwHtmlUnicode, nwQuotes, nwUnicode
|
from novelwriter.constants import nwFiles, nwQuotes, nwUnicode
|
||||||
from novelwriter.enum import nwTheme
|
from novelwriter.enum import nwTheme
|
||||||
from novelwriter.error import formatException, logException
|
from novelwriter.error import formatException, logException
|
||||||
|
|
||||||
@@ -899,7 +899,7 @@ class Config:
|
|||||||
"""
|
"""
|
||||||
self.splashMessage(f"Initialising {kind} font: {font.family()}")
|
self.splashMessage(f"Initialising {kind} font: {font.family()}")
|
||||||
metrics = QFontMetrics(font)
|
metrics = QFontMetrics(font)
|
||||||
for char in nwHtmlUnicode.U_TO_H.keys():
|
for char in nwUnicode.UI_SYMBOLS:
|
||||||
if not metrics.inFont(char): # type: ignore
|
if not metrics.inFont(char): # type: ignore
|
||||||
logger.warning("No glyph U+%04x in font", ord(char)) # pragma: no cover
|
logger.warning("No glyph U+%04x in font", ord(char)) # pragma: no cover
|
||||||
return
|
return
|
||||||
|
|||||||
+12
-19
@@ -598,21 +598,14 @@ class nwUnicode:
|
|||||||
U_TIMES = "\u00d7" # Multiplication sign
|
U_TIMES = "\u00d7" # Multiplication sign
|
||||||
U_DIVIDE = "\u00f7" # Division sign
|
U_DIVIDE = "\u00f7" # Division sign
|
||||||
|
|
||||||
# Arrows
|
|
||||||
U_UTRI = "\u25b2" # Up-pointing triangle
|
|
||||||
U_UTRIS = "\u25b4" # Up-pointing triangle, small
|
|
||||||
U_RTRI = "\u25b6" # Right-pointing triangle
|
|
||||||
U_RTRIS = "\u25b8" # Right-pointing triangle, small
|
|
||||||
U_DTRI = "\u25bc" # Down-pointing triangle
|
|
||||||
U_DTRIS = "\u25be" # Down-pointing triangle, small
|
|
||||||
U_LTRI = "\u25c0" # Left-pointing triangle
|
|
||||||
U_LTRIS = "\u25c2" # Left-pointing triangle, small
|
|
||||||
|
|
||||||
# Special
|
# Special
|
||||||
U_UNKN = "\ufffd" # Unknown character
|
U_UNKN = "\ufffd" # Unknown character
|
||||||
U_NAC1 = "\ufffe" # Not a character
|
U_NAC1 = "\ufffe" # Not a character
|
||||||
U_NAC2 = "\uffff" # Not a character
|
U_NAC2 = "\uffff" # Not a character
|
||||||
|
|
||||||
|
# Placeholders
|
||||||
|
U_LBREAK = "\u21b2" # Downwards Arrow With Tip Leftwards
|
||||||
|
|
||||||
# HTML Equivalents
|
# HTML Equivalents
|
||||||
# ================
|
# ================
|
||||||
|
|
||||||
@@ -667,15 +660,15 @@ class nwUnicode:
|
|||||||
H_TIMES = "×"
|
H_TIMES = "×"
|
||||||
H_DIVIDE = "÷"
|
H_DIVIDE = "÷"
|
||||||
|
|
||||||
# Arrows
|
# Unicode symbols expected to be used on the UI
|
||||||
H_UTRI = "▲"
|
UI_SYMBOLS: Final[list[str]] = [
|
||||||
H_UTRIS = "▴"
|
U_QUOT, U_APOS, U_LAQUO, U_RAQUO, U_LSQUO, U_RSQUO, U_SBQUO, U_SUQUO,
|
||||||
H_RTRI = "▶"
|
U_LDQUO, U_RDQUO, U_BDQUO, U_UDQUO, U_LSAQUO, U_RSAQUO, U_BDRQUO,
|
||||||
H_RTRIS = "▸"
|
U_LCQUO, U_RCQUO, U_LWCQUO, U_RWCQUO, U_FGDASH, U_ENDASH, U_EMDASH,
|
||||||
H_DTRI = "▼"
|
U_HBAR, U_HELLIP, U_MAPOS, U_PRIME, U_DPRIME, U_NBSP, U_THSP, U_THNBSP,
|
||||||
H_DTRIS = "▾"
|
U_ENSP, U_EMSP, U_MMSP, U_CHECK, U_CROSS, U_BULL, U_TRBULL, U_HYBULL,
|
||||||
H_LTRI = "◀"
|
U_FLOWER, U_PERMIL, U_DEGREE, U_MINUS, U_TIMES, U_DIVIDE, U_LBREAK,
|
||||||
H_LTRIS = "◂"
|
]
|
||||||
|
|
||||||
|
|
||||||
class nwHtmlUnicode:
|
class nwHtmlUnicode:
|
||||||
|
|||||||
@@ -1169,12 +1169,18 @@ class Tokenizer(ABC):
|
|||||||
|
|
||||||
class HeadingFormatter:
|
class HeadingFormatter:
|
||||||
|
|
||||||
def __init__(self, project: NWProject) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
project: NWProject,
|
||||||
|
chapter: int = 0,
|
||||||
|
scene: int = 0,
|
||||||
|
absolute: int = 0,
|
||||||
|
) -> None:
|
||||||
self._project = project
|
self._project = project
|
||||||
self._handle = None
|
self._handle = None
|
||||||
self._chCount = 0
|
self._chapter = chapter
|
||||||
self._scChCount = 0
|
self._scene = scene
|
||||||
self._scAbsCount = 0
|
self._absolute = absolute
|
||||||
return
|
return
|
||||||
|
|
||||||
def setHandle(self, tHandle: str | None) -> None:
|
def setHandle(self, tHandle: str | None) -> None:
|
||||||
@@ -1184,42 +1190,42 @@ class HeadingFormatter:
|
|||||||
|
|
||||||
def incChapter(self) -> None:
|
def incChapter(self) -> None:
|
||||||
"""Increment the chapter counter."""
|
"""Increment the chapter counter."""
|
||||||
self._chCount += 1
|
self._chapter += 1
|
||||||
return
|
return
|
||||||
|
|
||||||
def incScene(self) -> None:
|
def incScene(self) -> None:
|
||||||
"""Increment the scene counters."""
|
"""Increment the scene counters."""
|
||||||
self._scChCount += 1
|
self._scene += 1
|
||||||
self._scAbsCount += 1
|
self._absolute += 1
|
||||||
return
|
return
|
||||||
|
|
||||||
def resetAll(self) -> None:
|
def resetAll(self) -> None:
|
||||||
"""Reset all counters."""
|
"""Reset all counters."""
|
||||||
self._chCount = 0
|
self._chapter = 0
|
||||||
self._scChCount = 0
|
self._scene = 0
|
||||||
self._scAbsCount = 0
|
self._absolute = 0
|
||||||
return
|
return
|
||||||
|
|
||||||
def resetScene(self) -> None:
|
def resetScene(self) -> None:
|
||||||
"""Reset the chapter scene counter."""
|
"""Reset the chapter scene counter."""
|
||||||
self._scChCount = 0
|
self._scene = 0
|
||||||
return
|
return
|
||||||
|
|
||||||
def apply(self, hFormat: str, text: str, nHead: int) -> str:
|
def apply(self, hFormat: str, text: str, nHead: int) -> str:
|
||||||
"""Apply formatting to a specific heading."""
|
"""Apply formatting to a specific heading."""
|
||||||
hFormat = hFormat.replace(nwHeadFmt.TITLE, text)
|
hFormat = hFormat.replace(nwHeadFmt.TITLE, text)
|
||||||
hFormat = hFormat.replace(nwHeadFmt.BR, "\n")
|
hFormat = hFormat.replace(nwHeadFmt.BR, "\n")
|
||||||
hFormat = hFormat.replace(nwHeadFmt.CH_NUM, str(self._chCount))
|
hFormat = hFormat.replace(nwHeadFmt.CH_NUM, str(self._chapter))
|
||||||
hFormat = hFormat.replace(nwHeadFmt.SC_NUM, str(self._scChCount))
|
hFormat = hFormat.replace(nwHeadFmt.SC_NUM, str(self._scene))
|
||||||
hFormat = hFormat.replace(nwHeadFmt.SC_ABS, str(self._scAbsCount))
|
hFormat = hFormat.replace(nwHeadFmt.SC_ABS, str(self._absolute))
|
||||||
if nwHeadFmt.CH_WORD in hFormat:
|
if nwHeadFmt.CH_WORD in hFormat:
|
||||||
chWord = self._project.localLookup(self._chCount)
|
chWord = self._project.localLookup(self._chapter)
|
||||||
hFormat = hFormat.replace(nwHeadFmt.CH_WORD, chWord)
|
hFormat = hFormat.replace(nwHeadFmt.CH_WORD, chWord)
|
||||||
if nwHeadFmt.CH_ROML in hFormat:
|
if nwHeadFmt.CH_ROML in hFormat:
|
||||||
chRom = numberToRoman(self._chCount, toLower=True)
|
chRom = numberToRoman(self._chapter, toLower=True)
|
||||||
hFormat = hFormat.replace(nwHeadFmt.CH_ROML, chRom)
|
hFormat = hFormat.replace(nwHeadFmt.CH_ROML, chRom)
|
||||||
if nwHeadFmt.CH_ROMU in hFormat:
|
if nwHeadFmt.CH_ROMU in hFormat:
|
||||||
chRom = numberToRoman(self._chCount, toLower=False)
|
chRom = numberToRoman(self._chapter, toLower=False)
|
||||||
hFormat = hFormat.replace(nwHeadFmt.CH_ROMU, chRom)
|
hFormat = hFormat.replace(nwHeadFmt.CH_ROMU, chRom)
|
||||||
|
|
||||||
if nwHeadFmt.CHAR_POV in hFormat or nwHeadFmt.CHAR_FOCUS in hFormat:
|
if nwHeadFmt.CHAR_POV in hFormat or nwHeadFmt.CHAR_FOCUS in hFormat:
|
||||||
|
|||||||
@@ -40,8 +40,9 @@ from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt, s
|
|||||||
from novelwriter.formats.tokenizer import HEADINGS, Tokenizer
|
from novelwriter.formats.tokenizer import HEADINGS, Tokenizer
|
||||||
from novelwriter.types import (
|
from novelwriter.types import (
|
||||||
QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight,
|
QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight,
|
||||||
QtKeepAnchor, QtMoveAnchor, QtPageBreakAfter, QtPageBreakBefore,
|
QtKeepAnchor, QtMoveAnchor, QtPageBreakAfter, QtPageBreakAuto,
|
||||||
QtPropLineHeight, QtTransparent, QtVAlignNormal, QtVAlignSub, QtVAlignSuper
|
QtPageBreakBefore, QtPropLineHeight, QtTransparent, QtVAlignNormal,
|
||||||
|
QtVAlignSub, QtVAlignSuper
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -253,8 +254,10 @@ class ToQTextDocument(Tokenizer):
|
|||||||
|
|
||||||
elif tType in HEADINGS:
|
elif tType in HEADINGS:
|
||||||
bFmt, cFmt = self._genHeadStyle(tType, tMeta, bFmt)
|
bFmt, cFmt = self._genHeadStyle(tType, tMeta, bFmt)
|
||||||
newBlock(cursor, bFmt)
|
for tPart in tText.split("\n"):
|
||||||
cursor.insertText(tText, cFmt)
|
newBlock(cursor, bFmt)
|
||||||
|
cursor.insertText(tPart, cFmt)
|
||||||
|
bFmt.setPageBreakPolicy(QtPageBreakAuto)
|
||||||
|
|
||||||
elif tType == BlockTyp.SEP:
|
elif tType == BlockTyp.SEP:
|
||||||
newBlock(cursor, bFmt)
|
newBlock(cursor, bFmt)
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ from PyQt6.QtWidgets import (
|
|||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.common import fuzzyTime, qtLambda
|
from novelwriter.common import fuzzyTime, qtLambda
|
||||||
from novelwriter.constants import nwLabels, nwStats, trStats
|
from novelwriter.constants import nwHeadFmt, nwLabels, nwStats, nwUnicode, trStats
|
||||||
from novelwriter.core.buildsettings import BuildCollection, BuildSettings
|
from novelwriter.core.buildsettings import BuildCollection, BuildSettings
|
||||||
from novelwriter.core.docbuild import NWBuildDocument
|
from novelwriter.core.docbuild import NWBuildDocument
|
||||||
from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton, NToolDialog
|
from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton, NToolDialog
|
||||||
@@ -595,11 +595,7 @@ class _DetailsWidget(QWidget):
|
|||||||
item.addChild(sub)
|
item.addChild(sub)
|
||||||
|
|
||||||
# Headings
|
# Headings
|
||||||
hFmt = HeadingFormatter(SHARED.project)
|
hFmt = HeadingFormatter(SHARED.project, 7, 5, 23)
|
||||||
hFmt.incChapter()
|
|
||||||
hFmt.incScene()
|
|
||||||
hFmt.resetScene()
|
|
||||||
hFmt.incScene()
|
|
||||||
title = self.tr("Title")
|
title = self.tr("Title")
|
||||||
hidden = self.tr("Hidden")
|
hidden = self.tr("Hidden")
|
||||||
|
|
||||||
@@ -620,7 +616,8 @@ class _DetailsWidget(QWidget):
|
|||||||
if build.getBool(hHide):
|
if build.getBool(hHide):
|
||||||
sub.setText(1, f"[{hidden}]")
|
sub.setText(1, f"[{hidden}]")
|
||||||
else:
|
else:
|
||||||
sub.setText(1, hFmt.apply(build.getStr(hFormat), title, 0))
|
preview = build.getStr(hFormat).replace(nwHeadFmt.BR, nwUnicode.U_LBREAK)
|
||||||
|
sub.setText(1, hFmt.apply(preview, title, 0))
|
||||||
item.addChild(sub)
|
item.addChild(sub)
|
||||||
|
|
||||||
# Text Content
|
# Text Content
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ from PyQt6.QtWidgets import (
|
|||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.common import describeFont, fontMatcher, qtAddAction, qtLambda
|
from novelwriter.common import describeFont, fontMatcher, qtAddAction, qtLambda
|
||||||
from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, trConst
|
from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwUnicode, trConst
|
||||||
from novelwriter.core.buildsettings import BuildSettings, FilterMode
|
from novelwriter.core.buildsettings import BuildSettings, FilterMode
|
||||||
from novelwriter.extensions.configlayout import (
|
from novelwriter.extensions.configlayout import (
|
||||||
NColorLabel, NFixedPage, NScrollableForm, NScrollablePage
|
NColorLabel, NFixedPage, NScrollableForm, NScrollablePage
|
||||||
@@ -795,12 +795,15 @@ class _HeadingsTab(NScrollablePage):
|
|||||||
|
|
||||||
def loadContent(self) -> None:
|
def loadContent(self) -> None:
|
||||||
"""Populate the widgets."""
|
"""Populate the widgets."""
|
||||||
self.fmtPart.setText(self._build.getStr("headings.fmtPart"))
|
def fmtBreak(text: str) -> str:
|
||||||
self.fmtChapter.setText(self._build.getStr("headings.fmtChapter"))
|
return text.replace(nwHeadFmt.BR, nwUnicode.U_LBREAK)
|
||||||
self.fmtUnnumbered.setText(self._build.getStr("headings.fmtUnnumbered"))
|
|
||||||
self.fmtScene.setText(self._build.getStr("headings.fmtScene"))
|
self.fmtPart.setText(fmtBreak(self._build.getStr("headings.fmtPart")))
|
||||||
self.fmtAScene.setText(self._build.getStr("headings.fmtAltScene"))
|
self.fmtChapter.setText(fmtBreak(self._build.getStr("headings.fmtChapter")))
|
||||||
self.fmtSection.setText(self._build.getStr("headings.fmtSection"))
|
self.fmtUnnumbered.setText(fmtBreak(self._build.getStr("headings.fmtUnnumbered")))
|
||||||
|
self.fmtScene.setText(fmtBreak(self._build.getStr("headings.fmtScene")))
|
||||||
|
self.fmtAScene.setText(fmtBreak(self._build.getStr("headings.fmtAltScene")))
|
||||||
|
self.fmtSection.setText(fmtBreak(self._build.getStr("headings.fmtSection")))
|
||||||
|
|
||||||
self.swtPart.setChecked(self._build.getBool("headings.hidePart"))
|
self.swtPart.setChecked(self._build.getBool("headings.hidePart"))
|
||||||
self.swtChapter.setChecked(self._build.getBool("headings.hideChapter"))
|
self.swtChapter.setChecked(self._build.getBool("headings.hideChapter"))
|
||||||
@@ -880,7 +883,7 @@ class _HeadingsTab(NScrollablePage):
|
|||||||
text = ""
|
text = ""
|
||||||
label = self.tr("None")
|
label = self.tr("None")
|
||||||
|
|
||||||
self.editTextBox.setPlainText(text.replace(nwHeadFmt.BR, "\n"))
|
self.editTextBox.setPlainText(text.replace(nwUnicode.U_LBREAK, "\n"))
|
||||||
self.lblEditForm.setText(self.tr("Editing: {0}").format(label))
|
self.lblEditForm.setText(self.tr("Editing: {0}").format(label))
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -893,25 +896,26 @@ class _HeadingsTab(NScrollablePage):
|
|||||||
def _saveFormat(self) -> None:
|
def _saveFormat(self) -> None:
|
||||||
"""Save the format from the edit text box."""
|
"""Save the format from the edit text box."""
|
||||||
heading = self._editing
|
heading = self._editing
|
||||||
text = self.editTextBox.toPlainText().strip().replace("\n", nwHeadFmt.BR)
|
text = self.editTextBox.toPlainText().strip().replace("\n", nwUnicode.U_LBREAK)
|
||||||
|
value = text.replace(nwUnicode.U_LBREAK, nwHeadFmt.BR)
|
||||||
if heading == self.EDIT_TITLE:
|
if heading == self.EDIT_TITLE:
|
||||||
self.fmtPart.setText(text)
|
self.fmtPart.setText(text)
|
||||||
self._build.setValue("headings.fmtPart", text)
|
self._build.setValue("headings.fmtPart", value)
|
||||||
elif heading == self.EDIT_CHAPTER:
|
elif heading == self.EDIT_CHAPTER:
|
||||||
self.fmtChapter.setText(text)
|
self.fmtChapter.setText(text)
|
||||||
self._build.setValue("headings.fmtChapter", text)
|
self._build.setValue("headings.fmtChapter", value)
|
||||||
elif heading == self.EDIT_UNNUM:
|
elif heading == self.EDIT_UNNUM:
|
||||||
self.fmtUnnumbered.setText(text)
|
self.fmtUnnumbered.setText(text)
|
||||||
self._build.setValue("headings.fmtUnnumbered", text)
|
self._build.setValue("headings.fmtUnnumbered", value)
|
||||||
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", value)
|
||||||
elif heading == self.EDIT_HSCENE:
|
elif heading == self.EDIT_HSCENE:
|
||||||
self.fmtAScene.setText(text)
|
self.fmtAScene.setText(text)
|
||||||
self._build.setValue("headings.fmtAltScene", text)
|
self._build.setValue("headings.fmtAltScene", value)
|
||||||
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", value)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ QtVAlignSuper = QTextCharFormat.VerticalAlignment.AlignSuperScript
|
|||||||
|
|
||||||
QtPageBreakBefore = QTextFormat.PageBreakFlag.PageBreak_AlwaysBefore
|
QtPageBreakBefore = QTextFormat.PageBreakFlag.PageBreak_AlwaysBefore
|
||||||
QtPageBreakAfter = QTextFormat.PageBreakFlag.PageBreak_AlwaysAfter
|
QtPageBreakAfter = QTextFormat.PageBreakFlag.PageBreak_AlwaysAfter
|
||||||
|
QtPageBreakAuto = QTextFormat.PageBreakFlag.PageBreak_Auto
|
||||||
|
|
||||||
QtPropLineHeight = 1 # QTextBlockFormat.LineHeightTypes.ProportionalHeight
|
QtPropLineHeight = 1 # QTextBlockFormat.LineHeightTypes.ProportionalHeight
|
||||||
|
|
||||||
|
|||||||
@@ -1659,7 +1659,7 @@ def testFmtToken_ProcessHeaders(mockGUI):
|
|||||||
# H2: Chapter Word Number
|
# H2: Chapter Word Number
|
||||||
tokens._text = "## Chapter\n"
|
tokens._text = "## Chapter\n"
|
||||||
tokens.setChapterFormat(f"Chapter {nwHeadFmt.CH_WORD}")
|
tokens.setChapterFormat(f"Chapter {nwHeadFmt.CH_WORD}")
|
||||||
tokens._hFormatter._chCount = 0
|
tokens._hFormatter._chapter = 0
|
||||||
tokens.tokenizeText()
|
tokens.tokenizeText()
|
||||||
assert tokens._blocks == [
|
assert tokens._blocks == [
|
||||||
(BlockTyp.HEAD1, TM1, "Chapter One", [], BlockFmt.PBB),
|
(BlockTyp.HEAD1, TM1, "Chapter One", [], BlockFmt.PBB),
|
||||||
@@ -1733,8 +1733,8 @@ def testFmtToken_ProcessHeaders(mockGUI):
|
|||||||
# H3: Scene w/Absolute Number
|
# H3: Scene w/Absolute Number
|
||||||
tokens._text = "### A Scene\n"
|
tokens._text = "### A Scene\n"
|
||||||
tokens.setSceneFormat(f"Scene {nwHeadFmt.SC_ABS}", False)
|
tokens.setSceneFormat(f"Scene {nwHeadFmt.SC_ABS}", False)
|
||||||
tokens._hFormatter._scAbsCount = 0
|
tokens._hFormatter._scene = 0
|
||||||
tokens._hFormatter._scChCount = 0
|
tokens._hFormatter._absolute = 0
|
||||||
tokens.tokenizeText()
|
tokens.tokenizeText()
|
||||||
assert tokens._blocks == [
|
assert tokens._blocks == [
|
||||||
(BlockTyp.HEAD2, TM1, "Scene 1", [], BlockFmt.NONE),
|
(BlockTyp.HEAD2, TM1, "Scene 1", [], BlockFmt.NONE),
|
||||||
@@ -1743,8 +1743,8 @@ def testFmtToken_ProcessHeaders(mockGUI):
|
|||||||
# H3: Scene w/Chapter Number
|
# H3: Scene w/Chapter Number
|
||||||
tokens._text = "### A Scene\n"
|
tokens._text = "### A Scene\n"
|
||||||
tokens.setSceneFormat(f"Scene {nwHeadFmt.CH_NUM}.{nwHeadFmt.SC_NUM}", False)
|
tokens.setSceneFormat(f"Scene {nwHeadFmt.CH_NUM}.{nwHeadFmt.SC_NUM}", False)
|
||||||
tokens._hFormatter._scAbsCount = 0
|
tokens._hFormatter._scene = 1
|
||||||
tokens._hFormatter._scChCount = 1
|
tokens._hFormatter._absolute = 0
|
||||||
tokens.tokenizeText()
|
tokens.tokenizeText()
|
||||||
assert tokens._blocks == [
|
assert tokens._blocks == [
|
||||||
(BlockTyp.HEAD2, TM1, "Scene 3.2", [], BlockFmt.NONE),
|
(BlockTyp.HEAD2, TM1, "Scene 3.2", [], BlockFmt.NONE),
|
||||||
@@ -2549,9 +2549,9 @@ def testFmtToken_HeadingFormatter(fncPath, mockGUI, mockRnd):
|
|||||||
|
|
||||||
# Special Formats
|
# Special Formats
|
||||||
# ===============
|
# ===============
|
||||||
formatter._chCount = 2
|
formatter._chapter = 2
|
||||||
formatter._scChCount = 3
|
formatter._scene = 3
|
||||||
formatter._scAbsCount = 8
|
formatter._absolute = 8
|
||||||
|
|
||||||
# Chapter Number Word
|
# Chapter Number Word
|
||||||
cFormat = f"Chapter {nwHeadFmt.CH_WORD}, Scene {nwHeadFmt.SC_NUM} - {nwHeadFmt.TITLE}"
|
cFormat = f"Chapter {nwHeadFmt.CH_WORD}, Scene {nwHeadFmt.SC_NUM} - {nwHeadFmt.TITLE}"
|
||||||
|
|||||||
@@ -25,14 +25,15 @@ import pytest
|
|||||||
from PyQt6.QtGui import QFont, QTextBlock, QTextCharFormat, QTextCursor
|
from PyQt6.QtGui import QFont, QTextBlock, QTextCharFormat, QTextCursor
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
from novelwriter.constants import nwUnicode
|
from novelwriter.constants import nwHeadFmt, nwUnicode
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.enum import nwComment
|
from novelwriter.enum import nwComment
|
||||||
from novelwriter.formats.shared import BlockFmt, BlockTyp, TextDocumentTheme
|
from novelwriter.formats.shared import BlockFmt, BlockTyp, TextDocumentTheme
|
||||||
from novelwriter.formats.toqdoc import ToQTextDocument
|
from novelwriter.formats.toqdoc import ToQTextDocument
|
||||||
from novelwriter.types import (
|
from novelwriter.types import (
|
||||||
QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight,
|
QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight,
|
||||||
QtPageBreakAfter, QtTransparent, QtVAlignNormal, QtVAlignSub, QtVAlignSuper
|
QtPageBreakAfter, QtPageBreakAuto, QtPageBreakBefore, QtTransparent,
|
||||||
|
QtVAlignNormal, QtVAlignSub, QtVAlignSuper
|
||||||
)
|
)
|
||||||
|
|
||||||
THEME = TextDocumentTheme()
|
THEME = TextDocumentTheme()
|
||||||
@@ -54,6 +55,11 @@ def testFmtToQTextDocument_ConvertHeaders(mockGUI):
|
|||||||
|
|
||||||
doc._isNovel = True
|
doc._isNovel = True
|
||||||
doc._isFirst = True
|
doc._isFirst = True
|
||||||
|
|
||||||
|
# Add a line break in chapter header format, see #2415
|
||||||
|
doc.setChapterFormat(f"{nwHeadFmt.CH_NUM}{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||||||
|
|
||||||
|
# Populate
|
||||||
doc._text = (
|
doc._text = (
|
||||||
"#! Title\n"
|
"#! Title\n"
|
||||||
"# Partition\n"
|
"# Partition\n"
|
||||||
@@ -63,12 +69,13 @@ def testFmtToQTextDocument_ConvertHeaders(mockGUI):
|
|||||||
)
|
)
|
||||||
doc.tokenizeText()
|
doc.tokenizeText()
|
||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
assert doc.document.blockCount() == 5
|
assert doc.document.blockCount() == 6
|
||||||
|
|
||||||
# Title
|
# Title
|
||||||
block = doc.document.findBlockByNumber(0)
|
block = doc.document.findBlockByNumber(0)
|
||||||
assert block.text() == "Title"
|
assert block.text() == "Title"
|
||||||
bFmt = block.blockFormat()
|
bFmt = block.blockFormat()
|
||||||
|
assert bFmt.pageBreakPolicy() == QtPageBreakAuto # Expected on first title
|
||||||
assert bFmt.topMargin() == doc._mHead[BlockTyp.TITLE][0]
|
assert bFmt.topMargin() == doc._mHead[BlockTyp.TITLE][0]
|
||||||
assert bFmt.bottomMargin() == doc._mHead[BlockTyp.TITLE][1]
|
assert bFmt.bottomMargin() == doc._mHead[BlockTyp.TITLE][1]
|
||||||
cFmt = charFmtInBlock(block, 1)
|
cFmt = charFmtInBlock(block, 1)
|
||||||
@@ -87,10 +94,23 @@ def testFmtToQTextDocument_ConvertHeaders(mockGUI):
|
|||||||
assert cFmt.fontPointSize() == doc._sHead[BlockTyp.PART]
|
assert cFmt.fontPointSize() == doc._sHead[BlockTyp.PART]
|
||||||
assert cFmt.foreground().color() == THEME.head
|
assert cFmt.foreground().color() == THEME.head
|
||||||
|
|
||||||
# Chapter
|
# Chapter, Line 1
|
||||||
block = doc.document.findBlockByNumber(2)
|
block = doc.document.findBlockByNumber(2)
|
||||||
|
assert block.text() == "1"
|
||||||
|
bFmt = block.blockFormat()
|
||||||
|
assert bFmt.pageBreakPolicy() == QtPageBreakBefore
|
||||||
|
assert bFmt.topMargin() == doc._mHead[BlockTyp.HEAD1][0]
|
||||||
|
assert bFmt.bottomMargin() == doc._mHead[BlockTyp.HEAD1][1]
|
||||||
|
cFmt = charFmtInBlock(block, 1)
|
||||||
|
assert cFmt.fontWeight() == QFont.Weight.Bold
|
||||||
|
assert cFmt.fontPointSize() == doc._sHead[BlockTyp.HEAD1]
|
||||||
|
assert cFmt.foreground().color() == THEME.head
|
||||||
|
|
||||||
|
# Chapter, Line 2
|
||||||
|
block = doc.document.findBlockByNumber(3)
|
||||||
assert block.text() == "Chapter"
|
assert block.text() == "Chapter"
|
||||||
bFmt = block.blockFormat()
|
bFmt = block.blockFormat()
|
||||||
|
assert bFmt.pageBreakPolicy() == QtPageBreakAuto # Important! See #2415
|
||||||
assert bFmt.topMargin() == doc._mHead[BlockTyp.HEAD1][0]
|
assert bFmt.topMargin() == doc._mHead[BlockTyp.HEAD1][0]
|
||||||
assert bFmt.bottomMargin() == doc._mHead[BlockTyp.HEAD1][1]
|
assert bFmt.bottomMargin() == doc._mHead[BlockTyp.HEAD1][1]
|
||||||
cFmt = charFmtInBlock(block, 1)
|
cFmt = charFmtInBlock(block, 1)
|
||||||
@@ -99,9 +119,10 @@ def testFmtToQTextDocument_ConvertHeaders(mockGUI):
|
|||||||
assert cFmt.foreground().color() == THEME.head
|
assert cFmt.foreground().color() == THEME.head
|
||||||
|
|
||||||
# Scene
|
# Scene
|
||||||
block = doc.document.findBlockByNumber(3)
|
block = doc.document.findBlockByNumber(4)
|
||||||
assert block.text() == "Scene"
|
assert block.text() == "Scene"
|
||||||
bFmt = block.blockFormat()
|
bFmt = block.blockFormat()
|
||||||
|
assert bFmt.pageBreakPolicy() == QtPageBreakAuto
|
||||||
assert bFmt.topMargin() == doc._mHead[BlockTyp.HEAD2][0]
|
assert bFmt.topMargin() == doc._mHead[BlockTyp.HEAD2][0]
|
||||||
assert bFmt.bottomMargin() == doc._mHead[BlockTyp.HEAD2][1]
|
assert bFmt.bottomMargin() == doc._mHead[BlockTyp.HEAD2][1]
|
||||||
cFmt = charFmtInBlock(block, 1)
|
cFmt = charFmtInBlock(block, 1)
|
||||||
@@ -110,9 +131,10 @@ def testFmtToQTextDocument_ConvertHeaders(mockGUI):
|
|||||||
assert cFmt.foreground().color() == THEME.head
|
assert cFmt.foreground().color() == THEME.head
|
||||||
|
|
||||||
# Section
|
# Section
|
||||||
block = doc.document.findBlockByNumber(4)
|
block = doc.document.findBlockByNumber(5)
|
||||||
assert block.text() == "Section"
|
assert block.text() == "Section"
|
||||||
bFmt = block.blockFormat()
|
bFmt = block.blockFormat()
|
||||||
|
assert bFmt.pageBreakPolicy() == QtPageBreakAuto
|
||||||
assert bFmt.topMargin() == doc._mHead[BlockTyp.HEAD3][0]
|
assert bFmt.topMargin() == doc._mHead[BlockTyp.HEAD3][0]
|
||||||
assert bFmt.bottomMargin() == doc._mHead[BlockTyp.HEAD3][1]
|
assert bFmt.bottomMargin() == doc._mHead[BlockTyp.HEAD3][1]
|
||||||
cFmt = charFmtInBlock(block, 1)
|
cFmt = charFmtInBlock(block, 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user