Display build details in Manuscript tool (#1516)

This commit is contained in:
Veronica Berglyd Olsen
2023-09-01 18:50:55 +01:00
committed by GitHub
6 changed files with 323 additions and 49 deletions
+44 -16
View File
@@ -1986,53 +1986,53 @@
<context> <context>
<name>GuiManuscript</name> <name>GuiManuscript</name>
<message> <message>
<location filename="../novelwriter/tools/manuscript.py" line="80" /> <location filename="../novelwriter/tools/manuscript.py" line="82" />
<source>Build Manuscript</source> <source>Build Manuscript</source>
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
<message> <message>
<location filename="../novelwriter/tools/manuscript.py" line="110" /> <location filename="../novelwriter/tools/manuscript.py" line="112" />
<source>Add New Build</source> <source>Add New Build</source>
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
<message> <message>
<location filename="../novelwriter/tools/manuscript.py" line="117" /> <location filename="../novelwriter/tools/manuscript.py" line="119" />
<source>Delete Selected Build</source> <source>Delete Selected Build</source>
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
<message> <message>
<location filename="../novelwriter/tools/manuscript.py" line="124" /> <location filename="../novelwriter/tools/manuscript.py" line="126" />
<source>Edit Selected Build</source> <source>Edit Selected Build</source>
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
<message> <message>
<location filename="../novelwriter/tools/manuscript.py" line="128" /> <location filename="../novelwriter/tools/manuscript.py" line="130" />
<source>Builds</source> <source>Builds</source>
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
<message> <message>
<location filename="../novelwriter/tools/manuscript.py" line="145" /> <location filename="../novelwriter/tools/manuscript.py" line="164" />
<source>Preview</source> <source>Preview</source>
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
<message> <message>
<location filename="../novelwriter/tools/manuscript.py" line="148" /> <location filename="../novelwriter/tools/manuscript.py" line="167" />
<source>Print</source> <source>Print</source>
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
<message> <message>
<location filename="../novelwriter/tools/manuscript.py" line="151" /> <location filename="../novelwriter/tools/manuscript.py" line="170" />
<source>Build</source> <source>Build</source>
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
<message> <message>
<location filename="../novelwriter/tools/manuscript.py" line="154" /> <location filename="../novelwriter/tools/manuscript.py" line="173" />
<source>Close</source> <source>Close</source>
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
<message> <message>
<location filename="../novelwriter/tools/manuscript.py" line="254" /> <location filename="../novelwriter/tools/manuscript.py" line="278" />
<location filename="../novelwriter/tools/manuscript.py" line="207" /> <location filename="../novelwriter/tools/manuscript.py" line="226" />
<source>My Manuscript</source> <source>My Manuscript</source>
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
@@ -4487,6 +4487,34 @@
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
</context> </context>
<context>
<name>_DetailsWidget</name>
<message>
<location filename="../novelwriter/tools/manuscript.py" line="504" />
<source>Setting</source>
<translation type="unfinished" />
</message>
<message>
<location filename="../novelwriter/tools/manuscript.py" line="504" />
<source>Value</source>
<translation type="unfinished" />
</message>
<message>
<location filename="../novelwriter/tools/manuscript.py" line="573" />
<source>Name</source>
<translation type="unfinished" />
</message>
<message>
<location filename="../novelwriter/tools/manuscript.py" line="579" />
<source>Selection</source>
<translation type="unfinished" />
</message>
<message>
<location filename="../novelwriter/tools/manuscript.py" line="595" />
<source>Title</source>
<translation type="unfinished" />
</message>
</context>
<context> <context>
<name>_FilterTab</name> <name>_FilterTab</name>
<message> <message>
@@ -4625,27 +4653,27 @@
<context> <context>
<name>_PreviewWidget</name> <name>_PreviewWidget</name>
<message> <message>
<location filename="../novelwriter/tools/manuscript.py" line="467" /> <location filename="../novelwriter/tools/manuscript.py" line="659" />
<source>Press the "Build Preview" button to generate ...</source> <source>Press the "Build Preview" button to generate ...</source>
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
<message> <message>
<location filename="../novelwriter/tools/manuscript.py" line="565" /> <location filename="../novelwriter/tools/manuscript.py" line="757" />
<source>Processing ...</source> <source>Processing ...</source>
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
<message> <message>
<location filename="../novelwriter/tools/manuscript.py" line="594" /> <location filename="../novelwriter/tools/manuscript.py" line="786" />
<source>Done</source> <source>Done</source>
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
<message> <message>
<location filename="../novelwriter/tools/manuscript.py" line="637" /> <location filename="../novelwriter/tools/manuscript.py" line="829" />
<source>Unknown</source> <source>Unknown</source>
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
<message> <message>
<location filename="../novelwriter/tools/manuscript.py" line="638" /> <location filename="../novelwriter/tools/manuscript.py" line="830" />
<source>Built</source> <source>Built</source>
<translation type="unfinished" /> <translation type="unfinished" />
</message> </message>
+50 -5
View File
@@ -319,7 +319,6 @@ class BuildSettings:
value = min(max(value, definition[2]), definition[3]) value = min(max(value, definition[2]), definition[3])
self._changed = value != self._settings[key] self._changed = value != self._settings[key]
self._settings[key] = value self._settings[key] = value
logger.debug(f"Build Setting '{key}' set to: {value}")
return True return True
## ##
@@ -457,6 +456,8 @@ class BuildCollection:
def __init__(self, project: NWProject) -> None: def __init__(self, project: NWProject) -> None:
self._project = project self._project = project
self._builds = {} self._builds = {}
self._lastBuild = ""
self._defaultBuild = ""
self._loadCollection() self._loadCollection()
return return
@@ -465,7 +466,21 @@ class BuildCollection:
return len(self._builds) return len(self._builds)
## ##
# Methods # Properties
##
@property
def lastBuild(self) -> str:
"""Return the last active build."""
return self._lastBuild
@property
def defaultBuild(self) -> str:
"""Return the default build."""
return self._defaultBuild
##
# Getters
## ##
def getBuild(self, buildID: str) -> BuildSettings | None: def getBuild(self, buildID: str) -> BuildSettings | None:
@@ -476,6 +491,24 @@ class BuildCollection:
build.unpack(self._builds[buildID]) build.unpack(self._builds[buildID])
return build return build
##
# Setters
##
def setLastBuild(self, buildID: str) -> None:
"""Set the last active build id."""
if buildID != self._lastBuild:
self._lastBuild = buildID
self._saveCollection()
return
def setDefaultBuild(self, buildID: str) -> None:
"""Set the default build id."""
if buildID != self._defaultBuild:
self._defaultBuild = buildID
self._saveCollection()
return
def setBuild(self, build: BuildSettings) -> None: def setBuild(self, build: BuildSettings) -> None:
"""Set build settings data in the collection.""" """Set build settings data in the collection."""
if isinstance(build, BuildSettings): if isinstance(build, BuildSettings):
@@ -484,6 +517,10 @@ class BuildCollection:
self._saveCollection() self._saveCollection()
return return
##
# Methods
##
def removeBuild(self, buildID: str) -> None: def removeBuild(self, buildID: str) -> None:
"""Remove the a build from the collection.""" """Remove the a build from the collection."""
self._builds.pop(buildID, None) self._builds.pop(buildID, None)
@@ -527,7 +564,11 @@ class BuildCollection:
return False return False
for key, entry in builds.items(): for key, entry in builds.items():
if isinstance(entry, dict): if key == "lastBuild":
self._lastBuild = str(entry)
elif key == "defaultBuild":
self._defaultBuild = str(entry)
elif isinstance(entry, dict):
self._builds[key] = entry self._builds[key] = entry
return True return True
@@ -540,9 +581,13 @@ class BuildCollection:
logger.debug("Saving builds file") logger.debug("Saving builds file")
try: try:
data = {"novelWriter.builds": self._builds} data = {
"lastBuild": self._lastBuild,
"defaultBuild": self._defaultBuild,
}
data.update(self._builds)
with open(buildsFile, mode="w+", encoding="utf-8") as outFile: with open(buildsFile, mode="w+", encoding="utf-8") as outFile:
outFile.write(jsonEncode(data, nmax=4)) outFile.write(jsonEncode({"novelWriter.builds": data}, nmax=4))
except Exception: except Exception:
logger.error("Failed to save builds file") logger.error("Failed to save builds file")
logException() logException()
+4 -3
View File
@@ -58,13 +58,14 @@ VALID_MAP = {
"GuiWordList": {"winWidth", "winHeight"}, "GuiWordList": {"winWidth", "winHeight"},
"GuiNovelView": {"lastCol", "lastColSize"}, "GuiNovelView": {"lastCol", "lastColSize"},
"GuiBuildSettings": { "GuiBuildSettings": {
"winWidth", "winHeight", "treeWidth", "filterWidth" "winWidth", "winHeight", "treeWidth", "filterWidth",
}, },
"GuiManuscript": { "GuiManuscript": {
"winWidth", "winHeight", "optsWidth", "viewWidth" "winWidth", "winHeight", "optsWidth", "viewWidth", "listHeight",
"detailsHeight", "detailsWidth", "detailsExpanded",
}, },
"GuiManuscriptBuild": { "GuiManuscriptBuild": {
"winWidth", "winHeight", "fmtWidth", "sumWidth" "winWidth", "winHeight", "fmtWidth", "sumWidth",
}, },
} }
+1 -1
View File
@@ -600,7 +600,7 @@ class NWProject:
self._langData = json.load(inFile) self._langData = json.load(inFile)
logger.debug("Loaded project language file: %s", langFile.name) logger.debug("Loaded project language file: %s", langFile.name)
except Exception: except Exception:
logger.error("Failed to project language file") logger.error("Failed to load project language file")
logException() logException()
return False return False
+211 -21
View File
@@ -33,8 +33,9 @@ from datetime import datetime
from PyQt5.QtGui import QCloseEvent, QColor, QCursor, QFont, QPalette, QResizeEvent from PyQt5.QtGui import QCloseEvent, QColor, QCursor, QFont, QPalette, QResizeEvent
from PyQt5.QtCore import QSize, QTimer, Qt, pyqtSlot from PyQt5.QtCore import QSize, QTimer, Qt, pyqtSlot
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QDialog, QGridLayout, QHBoxLayout, QLabel, QListWidget, QListWidgetItem, QPushButton, QAbstractItemView, QDialog, QGridLayout, QHBoxLayout, QLabel, QListWidget,
QSplitter, QTextBrowser, QToolButton, QVBoxLayout, QWidget, qApp QListWidgetItem, QPushButton, QSplitter, QTextBrowser, QToolButton,
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget, qApp
) )
from PyQt5.QtPrintSupport import QPrintPreviewDialog, QPrinter from PyQt5.QtPrintSupport import QPrintPreviewDialog, QPrinter
@@ -43,6 +44,7 @@ from novelwriter.error import logException
from novelwriter.common import checkInt, fuzzyTime from novelwriter.common import checkInt, fuzzyTime
from novelwriter.core.tohtml import ToHtml from novelwriter.core.tohtml import ToHtml
from novelwriter.core.docbuild import NWBuildDocument from novelwriter.core.docbuild import NWBuildDocument
from novelwriter.core.tokenizer import HeadingFormatter
from novelwriter.core.buildsettings import BuildCollection, BuildSettings from novelwriter.core.buildsettings import BuildCollection, BuildSettings
from novelwriter.tools.manusbuild import GuiManuscriptBuild from novelwriter.tools.manusbuild import GuiManuscriptBuild
from novelwriter.tools.manussettings import GuiBuildSettings from novelwriter.tools.manussettings import GuiBuildSettings
@@ -127,10 +129,6 @@ class GuiManuscript(QDialog):
self.lblBuilds = QLabel("<b>{0}</b>".format(self.tr("Builds"))) self.lblBuilds = QLabel("<b>{0}</b>".format(self.tr("Builds")))
self.buildList = QListWidget()
self.buildList.setIconSize(QSize(iPx, iPx))
self.buildList.doubleClicked.connect(self._editSelectedBuild)
self.listToolBox = QHBoxLayout() self.listToolBox = QHBoxLayout()
self.listToolBox.addWidget(self.lblBuilds) self.listToolBox.addWidget(self.lblBuilds)
self.listToolBox.addStretch(1) self.listToolBox.addStretch(1)
@@ -139,6 +137,27 @@ class GuiManuscript(QDialog):
self.listToolBox.addWidget(self.tbEdit) self.listToolBox.addWidget(self.tbEdit)
self.listToolBox.setSpacing(0) self.listToolBox.setSpacing(0)
# Builds
# ======
self.buildList = QListWidget()
self.buildList.setIconSize(QSize(iPx, iPx))
self.buildList.doubleClicked.connect(self._editSelectedBuild)
self.buildList.currentItemChanged.connect(self._updateBuildDetails)
self.buildDetails = _DetailsWidget(self)
self.buildDetails.setColumnWidth(
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "detailsWidth", 100)),
)
self.buildSplit = QSplitter(Qt.Orientation.Vertical, self)
self.buildSplit.addWidget(self.buildList)
self.buildSplit.addWidget(self.buildDetails)
self.buildSplit.setSizes([
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "listHeight", 50)),
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "detailsHeight", 50)),
])
# Process Controls # Process Controls
# ================ # ================
@@ -167,7 +186,7 @@ class GuiManuscript(QDialog):
self.controlBox = QVBoxLayout() self.controlBox = QVBoxLayout()
self.controlBox.addLayout(self.listToolBox, 0) self.controlBox.addLayout(self.listToolBox, 0)
self.controlBox.addWidget(self.buildList, 1) self.controlBox.addWidget(self.buildSplit, 1)
self.controlBox.addLayout(self.processBox, 0) self.controlBox.addLayout(self.processBox, 0)
self.controlBox.setContentsMargins(0, 0, 0, 0) self.controlBox.setContentsMargins(0, 0, 0, 0)
@@ -206,8 +225,13 @@ class GuiManuscript(QDialog):
build = BuildSettings() build = BuildSettings()
build.setName(self.tr("My Manuscript")) build.setName(self.tr("My Manuscript"))
self._builds.setBuild(build) self._builds.setBuild(build)
selected = build.buildID
else:
selected = self._builds.lastBuild
self._updateBuildsList() self._updateBuildsList()
if selected in self._buildMap:
self.buildList.setCurrentItem(self._buildMap[selected])
logger.debug("Loading build cache") logger.debug("Loading build cache")
cache = CONFIG.dataPath("cache") / f"build_{SHARED.project.data.uuid}.json" cache = CONFIG.dataPath("cache") / f"build_{SHARED.project.data.uuid}.json"
@@ -263,6 +287,15 @@ class GuiManuscript(QDialog):
self._openSettingsDialog(build) self._openSettingsDialog(build)
return return
@pyqtSlot("QListWidgetItem*", "QListWidgetItem*")
def _updateBuildDetails(self, current: QListWidgetItem, previous: QListWidgetItem) -> None:
"""Process change of build selection to update the details."""
if isinstance(current, QListWidgetItem):
build = self._builds.getBuild(current.data(self.D_KEY))
if build is not None:
self.buildDetails.updateInfo(build)
return
@pyqtSlot() @pyqtSlot()
def _deleteSelectedBuild(self): def _deleteSelectedBuild(self):
"""Delete the currently selected build settings entry.""" """Delete the currently selected build settings entry."""
@@ -278,6 +311,9 @@ class GuiManuscript(QDialog):
"""Process new build settings from the settings dialog.""" """Process new build settings from the settings dialog."""
self._builds.setBuild(build) self._builds.setBuild(build)
self._updateBuildItem(build) self._updateBuildItem(build)
current = self.buildList.currentItem()
if isinstance(current, QListWidgetItem) and current.data(self.D_KEY) == build.buildID:
self._updateBuildDetails(current, current)
return return
@pyqtSlot() @pyqtSlot()
@@ -363,8 +399,6 @@ class GuiManuscript(QDialog):
self.docPreview.setJustify( self.docPreview.setJustify(
build.getBool("format.justifyText") build.getBool("format.justifyText")
) )
if build.buildID and build.buildID in self._buildMap:
self._buildMap[build.buildID].setSelected(True)
return return
def _getSelectedBuild(self) -> BuildSettings | None: def _getSelectedBuild(self) -> BuildSettings | None:
@@ -383,6 +417,10 @@ class GuiManuscript(QDialog):
"""Save the user GUI settings.""" """Save the user GUI settings."""
logger.debug("Saving GuiManuscript settings") logger.debug("Saving GuiManuscript settings")
current = self.buildList.currentItem()
if isinstance(current, QListWidgetItem):
self._builds.setLastBuild(current.data(self.D_KEY))
winWidth = CONFIG.rpxInt(self.width()) winWidth = CONFIG.rpxInt(self.width())
winHeight = CONFIG.rpxInt(self.height()) winHeight = CONFIG.rpxInt(self.height())
@@ -390,11 +428,21 @@ class GuiManuscript(QDialog):
optsWidth = CONFIG.rpxInt(mainSplit[0]) optsWidth = CONFIG.rpxInt(mainSplit[0])
viewWidth = CONFIG.rpxInt(mainSplit[1]) viewWidth = CONFIG.rpxInt(mainSplit[1])
buildSplit = self.buildSplit.sizes()
listHeight = CONFIG.rpxInt(buildSplit[0])
detailsHeight = CONFIG.rpxInt(buildSplit[1])
detailsWidth = CONFIG.rpxInt(self.buildDetails.getColumnWidth())
detailsExpanded = self.buildDetails.getExpandedState()
pOptions = SHARED.project.options pOptions = SHARED.project.options
pOptions.setValue("GuiManuscript", "winWidth", winWidth) pOptions.setValue("GuiManuscript", "winWidth", winWidth)
pOptions.setValue("GuiManuscript", "winHeight", winHeight) pOptions.setValue("GuiManuscript", "winHeight", winHeight)
pOptions.setValue("GuiManuscript", "optsWidth", optsWidth) pOptions.setValue("GuiManuscript", "optsWidth", optsWidth)
pOptions.setValue("GuiManuscript", "viewWidth", viewWidth) pOptions.setValue("GuiManuscript", "viewWidth", viewWidth)
pOptions.setValue("GuiManuscript", "listHeight", listHeight)
pOptions.setValue("GuiManuscript", "detailsHeight", detailsHeight)
pOptions.setValue("GuiManuscript", "detailsWidth", detailsWidth)
pOptions.setValue("GuiManuscript", "detailsExpanded", detailsExpanded)
pOptions.saveSettings() pOptions.saveSettings()
return return
@@ -444,9 +492,151 @@ class GuiManuscript(QDialog):
# END Class GuiManuscript # END Class GuiManuscript
class _DetailsWidget(QWidget):
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
self._initExpanded = True
# Tree Vidget
self.listView = QTreeWidget(self)
self.listView.setHeaderLabels([self.tr("Setting"), self.tr("Value")])
self.listView.setIndentation(SHARED.theme.baseIconSize)
self.listView.setSelectionMode(QAbstractItemView.NoSelection)
# Assemble
self.outerBox = QVBoxLayout()
self.outerBox.addWidget(self.listView)
self.outerBox.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.outerBox)
return
##
# Getters
##
def getColumnWidth(self) -> int:
"""Get the width of the first column."""
return self.listView.columnWidth(0)
def getExpandedState(self) -> list[bool]:
"""Get the expanded state of each top level item."""
state = []
for i in range(self.listView.topLevelItemCount()):
item = self.listView.topLevelItem(i)
if isinstance(item, QTreeWidgetItem):
state.append(item.isExpanded())
return state
##
# Setters
##
def setColumnWidth(self, value: int) -> None:
"""Set the width of the first column."""
self.listView.setColumnWidth(0, value)
return
def setExpandedState(self, state: list[bool]) -> None:
"""Set the expanded state of each top level item."""
count = len(state)
for i in range(self.listView.topLevelItemCount()):
item = self.listView.topLevelItem(i)
if isinstance(item, QTreeWidgetItem):
item.setExpanded((state[i] if i < count else True) and item.childCount() > 0)
return
##
# Methods
##
def updateInfo(self, build: BuildSettings) -> None:
"""Load the build settings info into the table."""
if self._initExpanded:
previous = SHARED.project.options.getValue("GuiManuscript", "detailsExpanded", [])
expanded = [bool(s) for s in previous]
self._initExpanded = False
else:
expanded = self.getExpandedState()
self.listView.clear()
on = SHARED.theme.getIcon("bullet-on")
off = SHARED.theme.getIcon("bullet-off")
# Name
item = QTreeWidgetItem()
item.setText(0, self.tr("Name"))
item.setText(1, build.name)
self.listView.addTopLevelItem(item)
# Selection
item = QTreeWidgetItem()
item.setText(0, self.tr("Selection"))
item.setText(1, "")
self.listView.addTopLevelItem(item)
for tHandle, nwItem in SHARED.project.tree.iterRoots(None):
if not nwItem.isInactiveClass():
sub = QTreeWidgetItem()
sub.setText(0, nwItem.itemName)
sub.setIcon(1, on if build.isRootAllowed(tHandle) else off)
item.addChild(sub)
# Headings
hFmt = HeadingFormatter(SHARED.project)
hFmt.incChapter()
hFmt.incScene()
hFmt.resetScene()
hFmt.incScene()
title = self.tr("Title")
item = QTreeWidgetItem()
item.setText(0, build.getLabel("headings"))
item.setText(1, "")
self.listView.addTopLevelItem(item)
entries = [
"headings.fmtTitle", "headings.fmtChapter", "headings.fmtUnnumbered",
"headings.fmtScene", "headings.fmtSection"
]
for key in entries:
sub = QTreeWidgetItem()
sub.setText(0, build.getLabel(key))
sub.setText(1, hFmt.apply(build.getStr(key), title))
item.addChild(sub)
for key in ["headings.hideScene", "headings.hideSection"]:
sub = QTreeWidgetItem()
sub.setText(0, build.getLabel(key))
sub.setIcon(1, on if build.getBool(key) else off)
item.addChild(sub)
# Text Content
item = QTreeWidgetItem()
item.setText(0, build.getLabel("text.grpContent"))
item.setText(1, "")
self.listView.addTopLevelItem(item)
entries = [
"text.includeSynopsis", "text.includeComments",
"text.includeKeywords", "text.includeBodyText",
]
for key in entries:
sub = QTreeWidgetItem()
sub.setText(0, build.getLabel(key))
sub.setIcon(1, on if build.getBool(key) else off)
item.addChild(sub)
# Restore expanded state
self.setExpandedState(expanded)
return
# END Class _DetailsWidget
class _PreviewWidget(QTextBrowser): class _PreviewWidget(QTextBrowser):
def __init__(self, parent: QWidget): def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
self._docTime = 0 self._docTime = 0
@@ -512,13 +702,13 @@ class _PreviewWidget(QTextBrowser):
# Setters # Setters
## ##
def setBuildName(self, name: str): def setBuildName(self, name: str) -> None:
"""Set the build name for the document label.""" """Set the build name for the document label."""
self._buildName = name self._buildName = name
self._updateBuildAge() self._updateBuildAge()
return return
def setJustify(self, state: bool): def setJustify(self, state: bool) -> None:
"""Enable/disable the justify text option.""" """Enable/disable the justify text option."""
pOptions = self.document().defaultTextOption() pOptions = self.document().defaultTextOption()
if state: if state:
@@ -528,7 +718,7 @@ class _PreviewWidget(QTextBrowser):
self.document().setDefaultTextOption(pOptions) self.document().setDefaultTextOption(pOptions)
return return
def setTextFont(self, family: str, size: int): def setTextFont(self, family: str, size: int) -> None:
"""Set the text font properties.""" """Set the text font properties."""
if family: if family:
font = QFont() font = QFont()
@@ -541,7 +731,7 @@ class _PreviewWidget(QTextBrowser):
# Methods # Methods
## ##
def beginNewBuild(self, length: int): def beginNewBuild(self, length: int) -> None:
"""Clear the document and show the progress bar.""" """Clear the document and show the progress bar."""
self.buildProgress.setMaximum(length) self.buildProgress.setMaximum(length)
self.buildProgress.setValue(0) self.buildProgress.setValue(0)
@@ -551,13 +741,13 @@ class _PreviewWidget(QTextBrowser):
self.clear() self.clear()
return return
def buildStep(self, value: int): def buildStep(self, value: int) -> None:
"""Update the progress bar value.""" """Update the progress bar value."""
self.buildProgress.setValue(value) self.buildProgress.setValue(value)
qApp.processEvents() qApp.processEvents()
return return
def setContent(self, data: dict): def setContent(self, data: dict) -> None:
"""Set the content of the preview widget.""" """Set the content of the preview widget."""
sPos = self.verticalScrollBar().value() sPos = self.verticalScrollBar().value()
qApp.setOverrideCursor(QCursor(Qt.WaitCursor)) qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
@@ -602,7 +792,7 @@ class _PreviewWidget(QTextBrowser):
# Events # Events
## ##
def resizeEvent(self, event: QResizeEvent): def resizeEvent(self, event: QResizeEvent) -> None:
"""Capture resize and update the document margins.""" """Capture resize and update the document margins."""
super().resizeEvent(event) super().resizeEvent(event)
self._updateDocMargins() self._updateDocMargins()
@@ -613,7 +803,7 @@ class _PreviewWidget(QTextBrowser):
## ##
@pyqtSlot("QPrinter*") @pyqtSlot("QPrinter*")
def printPreview(self, printer: QPrinter): def printPreview(self, printer: QPrinter) -> None:
"""Connect the print preview painter to the document viewer.""" """Connect the print preview painter to the document viewer."""
qApp.setOverrideCursor(QCursor(Qt.WaitCursor)) qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
printer.setOrientation(QPrinter.Portrait) printer.setOrientation(QPrinter.Portrait)
@@ -626,7 +816,7 @@ class _PreviewWidget(QTextBrowser):
## ##
@pyqtSlot() @pyqtSlot()
def _updateBuildAge(self): def _updateBuildAge(self) -> None:
"""Update the build time and the fuzzy age.""" """Update the build time and the fuzzy age."""
if self._docTime > 0: if self._docTime > 0:
strBuildTime = "%s (%s)" % ( strBuildTime = "%s (%s)" % (
@@ -642,7 +832,7 @@ class _PreviewWidget(QTextBrowser):
return return
@pyqtSlot() @pyqtSlot()
def _hideProgress(self): def _hideProgress(self) -> None:
"""Clean up the build progress bar.""" """Clean up the build progress bar."""
self.buildProgress.setVisible(False) self.buildProgress.setVisible(False)
return return
@@ -651,7 +841,7 @@ class _PreviewWidget(QTextBrowser):
# Internal Functions # Internal Functions
## ##
def _updateDocMargins(self): def _updateDocMargins(self) -> None:
"""Automatically adjust the header to fill the top of the """Automatically adjust the header to fill the top of the
document within the viewport. document within the viewport.
""" """
+13 -3
View File
@@ -377,6 +377,8 @@ def testCoreBuildSettings_Collection(monkeypatch, mockGUI, fncPath: Path, mockRn
builds = BuildCollection(project) builds = BuildCollection(project)
assert len(builds) == 0 assert len(builds) == 0
assert not buildsFile.exists() assert not buildsFile.exists()
assert builds.lastBuild == ""
assert builds.defaultBuild == ""
# Create a default build # Create a default build
buildOne = BuildSettings() buildOne = BuildSettings()
@@ -412,7 +414,9 @@ def testCoreBuildSettings_Collection(monkeypatch, mockGUI, fncPath: Path, mockRn
# Check the file content # Check the file content
data = json.loads(buildsFile.read_text(encoding="utf-8")) data = json.loads(buildsFile.read_text(encoding="utf-8"))
assert list(data["novelWriter.builds"].keys()) == [buildIDOne, buildIDTwo] assert list(data["novelWriter.builds"].keys()) == [
"lastBuild", "defaultBuild", buildIDOne, buildIDTwo
]
# Remove a build # Remove a build
builds.removeBuild(buildIDOne) builds.removeBuild(buildIDOne)
@@ -423,12 +427,16 @@ def testCoreBuildSettings_Collection(monkeypatch, mockGUI, fncPath: Path, mockRn
# Check the file content # Check the file content
data = json.loads(buildsFile.read_text(encoding="utf-8")) data = json.loads(buildsFile.read_text(encoding="utf-8"))
assert list(data["novelWriter.builds"].keys()) == [buildIDTwo] assert list(data["novelWriter.builds"].keys()) == [
"lastBuild", "defaultBuild", buildIDTwo
]
builds.setBuild(buildOne) builds.setBuild(buildOne)
assert list(builds.builds()) == [ assert list(builds.builds()) == [
(buildIDTwo, "Build Two"), (buildIDTwo, "Build Two"),
(buildIDOne, "Build One"), (buildIDOne, "Build One"),
] ]
builds.setLastBuild(buildIDOne)
builds.setDefaultBuild(buildIDTwo)
# Check errors: No valid path # Check errors: No valid path
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
@@ -447,7 +455,7 @@ def testCoreBuildSettings_Collection(monkeypatch, mockGUI, fncPath: Path, mockRn
buildsFile.write_text("foobar") buildsFile.write_text("foobar")
assert builds._loadCollection() is False assert builds._loadCollection() is False
# Check errors: Valid jason file, but list instead of object # Check errors: Valid json file, but list instead of object
buildsFile.write_text("[]") buildsFile.write_text("[]")
assert builds._loadCollection() is False assert builds._loadCollection() is False
buildsFile.unlink() buildsFile.unlink()
@@ -459,5 +467,7 @@ def testCoreBuildSettings_Collection(monkeypatch, mockGUI, fncPath: Path, mockRn
(buildIDTwo, "Build Two"), (buildIDTwo, "Build Two"),
(buildIDOne, "Build One"), (buildIDOne, "Build One"),
] ]
assert another.lastBuild == buildIDOne
assert another.defaultBuild == buildIDTwo
# END Test testCoreBuildSettings_Collection # END Test testCoreBuildSettings_Collection