diff --git a/novelwriter/core/options.py b/novelwriter/core/options.py index 9f813abc..419e4ec2 100644 --- a/novelwriter/core/options.py +++ b/novelwriter/core/options.py @@ -73,6 +73,9 @@ VALID_MAP: dict[str, set[str]] = { "widthCol3", "widthCol4", "wordsPerPage", "countFrom", "clearDouble", "novelRoot", }, + "GuiOutlineDetails": { + "detailsWidth", "tagsWidth", + } } diff --git a/novelwriter/gui/itemdetails.py b/novelwriter/gui/itemdetails.py index 73542889..6d67d74c 100644 --- a/novelwriter/gui/itemdetails.py +++ b/novelwriter/gui/itemdetails.py @@ -30,7 +30,7 @@ from PyQt5.QtWidgets import QGridLayout, QLabel, QWidget from novelwriter import CONFIG, SHARED from novelwriter.common import elide -from novelwriter.constants import nwLabels, trConst +from novelwriter.constants import nwLabels, nwStats, trConst from novelwriter.types import ( QtAlignLeft, QtAlignLeftBase, QtAlignRight, QtAlignRightBase, QtAlignRightMiddle @@ -62,6 +62,10 @@ class GuiItemDetails(QWidget): fntValue = self.font() fntValue.setPointSizeF(0.9*fPt) + trStats1 = trConst(nwLabels.STATS_NAME[nwStats.CHARS_ALL]) + trStats2 = trConst(nwLabels.STATS_NAME[nwStats.WORDS_ALL]) + trStats3 = trConst(nwLabels.STATS_NAME[nwStats.PARAGRAPHS]) + # Label self.labelName = QLabel(self.tr("Label"), self) self.labelName.setFont(fntLabel) @@ -113,7 +117,7 @@ class GuiItemDetails(QWidget): self.usageData.setWordWrap(True) # Character Count - self.cCountName = QLabel(" "+self.tr("Characters"), self) + self.cCountName = QLabel(trStats1, self) self.cCountName.setFont(fntLabel) self.cCountName.setAlignment(QtAlignRight) @@ -122,7 +126,7 @@ class GuiItemDetails(QWidget): self.cCountData.setAlignment(QtAlignRight) # Word Count - self.wCountName = QLabel(" "+self.tr("Words"), self) + self.wCountName = QLabel(trStats2, self) self.wCountName.setFont(fntLabel) self.wCountName.setAlignment(QtAlignRight) @@ -131,7 +135,7 @@ class GuiItemDetails(QWidget): self.wCountData.setAlignment(QtAlignRight) # Paragraph Count - self.pCountName = QLabel(" "+self.tr("Paragraphs"), self) + self.pCountName = QLabel(trStats3, self) self.pCountName.setFont(fntLabel) self.pCountName.setAlignment(QtAlignRight) diff --git a/novelwriter/gui/outline.py b/novelwriter/gui/outline.py index 54b8102e..a55c61ce 100644 --- a/novelwriter/gui/outline.py +++ b/novelwriter/gui/outline.py @@ -42,7 +42,7 @@ from PyQt5.QtWidgets import ( from novelwriter import CONFIG, SHARED from novelwriter.common import checkInt, formatFileFilter, makeFileNameSafe -from novelwriter.constants import nwKeyWords, nwLabels, nwStyles, trConst +from novelwriter.constants import nwKeyWords, nwLabels, nwStats, nwStyles, trConst from novelwriter.enum import nwDocMode, nwItemClass, nwItemLayout, nwItemType, nwOutline from novelwriter.error import logException from novelwriter.extensions.configlayout import NColourLabel @@ -135,12 +135,14 @@ class GuiOutlineView(QWidget): self.outlineBar.populateNovelList() self.outlineBar.setCurrentRoot(lastOutline) self.outlineBar.setEnabled(True) + self.outlineData.loadGuiSettings() return def closeProjectTasks(self) -> None: """Run closing project tasks.""" self.outlineTree.closeProjectTasks() + self.outlineData.saveGuiSettings() self.outlineData.updateClasses() self.clearOutline() return @@ -799,6 +801,10 @@ class GuiOutlineDetails(QScrollArea): bFont = SHARED.theme.guiFontB + trStats1 = trConst(nwLabels.STATS_NAME[nwStats.CHARS_ALL]) + trStats2 = trConst(nwLabels.STATS_NAME[nwStats.WORDS_ALL]) + trStats3 = trConst(nwLabels.STATS_NAME[nwStats.PARAGRAPHS]) + # Details Area self.titleLabel = QLabel(self.tr("Title"), self) self.fileLabel = QLabel(self.tr("Document"), self) @@ -819,9 +825,9 @@ class GuiOutlineDetails(QScrollArea): self.itemValue.setMaximumWidth(maxTitle) # Stats Area - self.cCLabel = QLabel(self.tr("Characters"), self) - self.wCLabel = QLabel(self.tr("Words"), self) - self.pCLabel = QLabel(self.tr("Paragraphs"), self) + self.cCLabel = QLabel(trStats1, self) + self.wCLabel = QLabel(trStats2, self) + self.pCLabel = QLabel(trStats3, self) self.cCValue = QLabel("", self) self.wCValue = QLabel("", self) self.pCValue = QLabel("", self) @@ -848,94 +854,9 @@ class GuiOutlineDetails(QScrollArea): self.synopLWrap = QHBoxLayout() self.synopLWrap.addWidget(self.synopValue, 1) - # Tags - self.povKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY]), self) - self.focKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY]), self) - self.chrKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.CHAR_KEY]), self) - self.pltKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY]), self) - self.timKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.TIME_KEY]), self) - self.wldKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.WORLD_KEY]), self) - self.objKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.OBJECT_KEY]), self) - self.entKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.ENTITY_KEY]), self) - self.cstKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.CUSTOM_KEY]), self) - self.styKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.STORY_KEY]), self) - self.mntKeyLabel = QLabel(trConst(nwLabels.KEY_NAME[nwKeyWords.MENTION_KEY]), self) - - self.povKeyLabel.setFont(bFont) - self.focKeyLabel.setFont(bFont) - self.chrKeyLabel.setFont(bFont) - self.pltKeyLabel.setFont(bFont) - self.timKeyLabel.setFont(bFont) - self.wldKeyLabel.setFont(bFont) - self.objKeyLabel.setFont(bFont) - self.entKeyLabel.setFont(bFont) - self.cstKeyLabel.setFont(bFont) - self.styKeyLabel.setFont(bFont) - self.mntKeyLabel.setFont(bFont) - - self.povKeyLWrap = QHBoxLayout() - self.focKeyLWrap = QHBoxLayout() - self.chrKeyLWrap = QHBoxLayout() - self.pltKeyLWrap = QHBoxLayout() - self.timKeyLWrap = QHBoxLayout() - self.wldKeyLWrap = QHBoxLayout() - self.objKeyLWrap = QHBoxLayout() - self.entKeyLWrap = QHBoxLayout() - self.cstKeyLWrap = QHBoxLayout() - self.styKeyLWrap = QHBoxLayout() - self.mntKeyLWrap = QHBoxLayout() - - self.povKeyValue = QLabel("", self) - self.focKeyValue = QLabel("", self) - self.chrKeyValue = QLabel("", self) - self.pltKeyValue = QLabel("", self) - self.timKeyValue = QLabel("", self) - self.wldKeyValue = QLabel("", self) - self.objKeyValue = QLabel("", self) - self.entKeyValue = QLabel("", self) - self.cstKeyValue = QLabel("", self) - self.styKeyValue = QLabel("", self) - self.mntKeyValue = QLabel("", self) - - self.povKeyValue.setWordWrap(True) - self.focKeyValue.setWordWrap(True) - self.chrKeyValue.setWordWrap(True) - self.pltKeyValue.setWordWrap(True) - self.timKeyValue.setWordWrap(True) - self.wldKeyValue.setWordWrap(True) - self.objKeyValue.setWordWrap(True) - self.entKeyValue.setWordWrap(True) - self.cstKeyValue.setWordWrap(True) - self.styKeyValue.setWordWrap(True) - self.mntKeyValue.setWordWrap(True) - - self.povKeyValue.linkActivated.connect(lambda x: self.itemTagClicked.emit(x)) - self.focKeyValue.linkActivated.connect(lambda x: self.itemTagClicked.emit(x)) - self.chrKeyValue.linkActivated.connect(lambda x: self.itemTagClicked.emit(x)) - self.pltKeyValue.linkActivated.connect(lambda x: self.itemTagClicked.emit(x)) - self.timKeyValue.linkActivated.connect(lambda x: self.itemTagClicked.emit(x)) - self.wldKeyValue.linkActivated.connect(lambda x: self.itemTagClicked.emit(x)) - self.objKeyValue.linkActivated.connect(lambda x: self.itemTagClicked.emit(x)) - self.entKeyValue.linkActivated.connect(lambda x: self.itemTagClicked.emit(x)) - self.cstKeyValue.linkActivated.connect(lambda x: self.itemTagClicked.emit(x)) - self.styKeyValue.linkActivated.connect(lambda x: self.itemTagClicked.emit(x)) - self.mntKeyValue.linkActivated.connect(lambda x: self.itemTagClicked.emit(x)) - - self.povKeyLWrap.addWidget(self.povKeyValue, 1) - self.focKeyLWrap.addWidget(self.focKeyValue, 1) - self.chrKeyLWrap.addWidget(self.chrKeyValue, 1) - self.pltKeyLWrap.addWidget(self.pltKeyValue, 1) - self.timKeyLWrap.addWidget(self.timKeyValue, 1) - self.wldKeyLWrap.addWidget(self.wldKeyValue, 1) - self.objKeyLWrap.addWidget(self.objKeyValue, 1) - self.entKeyLWrap.addWidget(self.entKeyValue, 1) - self.cstKeyLWrap.addWidget(self.cstKeyValue, 1) - self.styKeyLWrap.addWidget(self.styKeyValue, 1) - self.mntKeyLWrap.addWidget(self.mntKeyValue, 1) - # Selected Item Details - self.mainGroup = QGroupBox(self.tr("Title Details"), self) self.mainForm = QGridLayout() + self.mainGroup = QGroupBox(self.tr("Title Details"), self) self.mainGroup.setLayout(self.mainForm) self.mainForm.addWidget(self.titleLabel, 0, 0, 1, 1, QtAlignLeftTop) @@ -959,46 +880,37 @@ class GuiOutlineDetails(QScrollArea): self.mainForm.setVerticalSpacing(vSpace) # Selected Item Tags - self.tagsGroup = QGroupBox(self.tr("Reference Tags"), self) self.tagsForm = QGridLayout() + self.tagsGroup = QGroupBox(self.tr("Reference Tags"), self) self.tagsGroup.setLayout(self.tagsForm) - self.tagsForm.addWidget(self.povKeyLabel, 0, 0, QtAlignLeftTop) - self.tagsForm.addLayout(self.povKeyLWrap, 0, 1, QtAlignLeftTop) - self.tagsForm.addWidget(self.focKeyLabel, 1, 0, QtAlignLeftTop) - self.tagsForm.addLayout(self.focKeyLWrap, 1, 1, QtAlignLeftTop) - self.tagsForm.addWidget(self.chrKeyLabel, 2, 0, QtAlignLeftTop) - self.tagsForm.addLayout(self.chrKeyLWrap, 2, 1, QtAlignLeftTop) - self.tagsForm.addWidget(self.pltKeyLabel, 3, 0, QtAlignLeftTop) - self.tagsForm.addLayout(self.pltKeyLWrap, 3, 1, QtAlignLeftTop) - self.tagsForm.addWidget(self.timKeyLabel, 4, 0, QtAlignLeftTop) - self.tagsForm.addLayout(self.timKeyLWrap, 4, 1, QtAlignLeftTop) - self.tagsForm.addWidget(self.wldKeyLabel, 5, 0, QtAlignLeftTop) - self.tagsForm.addLayout(self.wldKeyLWrap, 5, 1, QtAlignLeftTop) - self.tagsForm.addWidget(self.objKeyLabel, 6, 0, QtAlignLeftTop) - self.tagsForm.addLayout(self.objKeyLWrap, 6, 1, QtAlignLeftTop) - self.tagsForm.addWidget(self.entKeyLabel, 7, 0, QtAlignLeftTop) - self.tagsForm.addLayout(self.entKeyLWrap, 7, 1, QtAlignLeftTop) - self.tagsForm.addWidget(self.cstKeyLabel, 8, 0, QtAlignLeftTop) - self.tagsForm.addLayout(self.cstKeyLWrap, 8, 1, QtAlignLeftTop) - self.tagsForm.addWidget(self.styKeyLabel, 9, 0, QtAlignLeftTop) - self.tagsForm.addLayout(self.styKeyLWrap, 9, 1, QtAlignLeftTop) - self.tagsForm.addWidget(self.mntKeyLabel, 10, 0, QtAlignLeftTop) - self.tagsForm.addLayout(self.mntKeyLWrap, 10, 1, QtAlignLeftTop) + self.tagValues: dict[str, tuple[QLabel, QLabel]] = {} + for key in nwKeyWords.ALL_KEYS: + label = QLabel(trConst(nwLabels.KEY_NAME[key]), self) + label.setFont(bFont) + value = QLabel("", self) + value.setWordWrap(True) + value.linkActivated.connect(lambda x: self.itemTagClicked.emit(x)) + layout = QHBoxLayout() + layout.addWidget(value, 1) + n = len(self.tagValues) + self.tagsForm.addWidget(label, n, 0, QtAlignLeftTop) + self.tagsForm.addLayout(layout, n, 1, QtAlignLeftTop) + self.tagValues[key] = (label, value) self.tagsForm.setColumnStretch(1, 1) - self.tagsForm.setRowStretch(10, 1) + self.tagsForm.setRowStretch(len(self.tagValues), 1) self.tagsForm.setHorizontalSpacing(hSpace) self.tagsForm.setVerticalSpacing(vSpace) # Assemble - self.outerWidget = QWidget(self) - self.outerBox = QHBoxLayout() - self.outerBox.addWidget(self.mainGroup, 0) - self.outerBox.addWidget(self.tagsGroup, 1) + self.mainSplit = QSplitter(Qt.Orientation.Horizontal) + self.mainSplit.addWidget(self.mainGroup) + self.mainSplit.addWidget(self.tagsGroup) + self.mainSplit.setCollapsible(0, False) + self.mainSplit.setCollapsible(1, False) - self.outerWidget.setLayout(self.outerBox) - self.setWidget(self.outerWidget) + self.setWidget(self.mainSplit) self.setHorizontalScrollBarPolicy(QtScrollAsNeeded) self.setVerticalScrollBarPolicy(QtScrollAsNeeded) @@ -1024,6 +936,28 @@ class GuiOutlineDetails(QScrollArea): self.updateClasses() return + def loadGuiSettings(self) -> None: + """Run open project tasks.""" + half = self.width() // 2 + pOptions = SHARED.project.options + self.mainSplit.setSizes([ + CONFIG.pxInt(pOptions.getInt("GuiOutlineDetails", "detailsWidth", half)), + CONFIG.pxInt(pOptions.getInt("GuiOutlineDetails", "tagsWidth", half)) + ]) + return + + def saveGuiSettings(self) -> None: + """Run close project tasks.""" + mainSplit = self.mainSplit.sizes() + detailsWidth = CONFIG.rpxInt(mainSplit[0]) + tagsWidth = CONFIG.rpxInt(mainSplit[1]) + + logger.debug("Saving State: GuiOutlineDetails") + pOptions = SHARED.project.options + pOptions.setValue("GuiOutlineDetails", "detailsWidth", detailsWidth) + pOptions.setValue("GuiOutlineDetails", "tagsWidth", tagsWidth) + return + def clearDetails(self) -> None: """Clear all the data labels.""" self.titleLabel.setText(self.tr("Title")) @@ -1034,17 +968,10 @@ class GuiOutlineDetails(QScrollArea): self.wCValue.setText("") self.pCValue.setText("") self.synopValue.setText("") - self.povKeyValue.setText("") - self.focKeyValue.setText("") - self.chrKeyValue.setText("") - self.pltKeyValue.setText("") - self.timKeyValue.setText("") - self.wldKeyValue.setText("") - self.objKeyValue.setText("") - self.entKeyValue.setText("") - self.cstKeyValue.setText("") - self.styKeyValue.setText("") - self.mntKeyValue.setText("") + + for _, value in self.tagValues.values(): + value.setText("") + self.updateClasses() return @@ -1076,17 +1003,8 @@ class GuiOutlineDetails(QScrollArea): self.synopValue.setText(novIdx.synopsis) - self.povKeyValue.setText(self._formatTags(novRefs, nwKeyWords.POV_KEY)) - self.focKeyValue.setText(self._formatTags(novRefs, nwKeyWords.FOCUS_KEY)) - self.chrKeyValue.setText(self._formatTags(novRefs, nwKeyWords.CHAR_KEY)) - self.pltKeyValue.setText(self._formatTags(novRefs, nwKeyWords.PLOT_KEY)) - self.timKeyValue.setText(self._formatTags(novRefs, nwKeyWords.TIME_KEY)) - self.wldKeyValue.setText(self._formatTags(novRefs, nwKeyWords.WORLD_KEY)) - self.objKeyValue.setText(self._formatTags(novRefs, nwKeyWords.OBJECT_KEY)) - self.entKeyValue.setText(self._formatTags(novRefs, nwKeyWords.ENTITY_KEY)) - self.cstKeyValue.setText(self._formatTags(novRefs, nwKeyWords.CUSTOM_KEY)) - self.styKeyValue.setText(self._formatTags(novRefs, nwKeyWords.STORY_KEY)) - self.mntKeyValue.setText(self._formatTags(novRefs, nwKeyWords.MENTION_KEY)) + for key, (_, value) in self.tagValues.items(): + value.setText(self._formatTags(novRefs, key)) return @@ -1094,27 +1012,12 @@ class GuiOutlineDetails(QScrollArea): def updateClasses(self) -> None: """Update the visibility status of class details.""" usedClasses = SHARED.project.tree.rootClasses() - - pltVisible = nwItemClass.PLOT in usedClasses - timVisible = nwItemClass.TIMELINE in usedClasses - wldVisible = nwItemClass.WORLD in usedClasses - objVisible = nwItemClass.OBJECT in usedClasses - entVisible = nwItemClass.ENTITY in usedClasses - cstVisible = nwItemClass.CUSTOM in usedClasses - - self.pltKeyLabel.setVisible(pltVisible) - self.pltKeyValue.setVisible(pltVisible) - self.timKeyLabel.setVisible(timVisible) - self.timKeyValue.setVisible(timVisible) - self.wldKeyLabel.setVisible(wldVisible) - self.wldKeyValue.setVisible(wldVisible) - self.objKeyLabel.setVisible(objVisible) - self.objKeyValue.setVisible(objVisible) - self.entKeyLabel.setVisible(entVisible) - self.entKeyValue.setVisible(entVisible) - self.cstKeyLabel.setVisible(cstVisible) - self.cstKeyValue.setVisible(cstVisible) - + for key, itemClass in nwKeyWords.KEY_CLASS.items(): + visible = itemClass in usedClasses + if key in self.tagValues: + label, value = self.tagValues[key] + label.setVisible(visible) + value.setVisible(visible) return @staticmethod diff --git a/tests/test_gui/test_gui_outline.py b/tests/test_gui/test_gui_outline.py index 3dc6b32b..dabd9cf0 100644 --- a/tests/test_gui/test_gui_outline.py +++ b/tests/test_gui/test_gui_outline.py @@ -29,6 +29,7 @@ import pytest from PyQt5.QtWidgets import QAction, QFileDialog, QWidget from novelwriter import CONFIG, SHARED +from novelwriter.constants import nwKeyWords from novelwriter.enum import nwItemClass, nwOutline, nwView from novelwriter.types import QtScrollAlwaysOff, QtScrollAsNeeded @@ -261,7 +262,7 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, prjLipsum, fncPath, tstPat assert outlineData.itemValue.text() == "Finished" # Click POV Link - assert outlineData.povKeyValue.text() == "Bod" + assert outlineData.tagValues[nwKeyWords.POV_KEY][1].text() == "Bod" outlineView._tagClicked("Bod") assert nwGUI.docViewer.docHandle == "4c4f28287af27"