From dd09d3af2f2d7c5abfc3c62aaf2d10a59bf45dab Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 31 Oct 2024 18:28:58 +0100
Subject: [PATCH] Refactor outline details panel code
---
novelwriter/core/options.py | 3 +
novelwriter/gui/itemdetails.py | 12 +-
novelwriter/gui/outline.py | 229 +++++++++--------------------
tests/test_gui/test_gui_outline.py | 3 +-
4 files changed, 79 insertions(+), 168 deletions(-)
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"