Refactor and clean up GUI code (#2078)

This commit is contained in:
Veronica Berglyd Olsen
2024-10-31 18:42:32 +01:00
committed by GitHub
14 changed files with 264 additions and 363 deletions
+1 -1
View File
@@ -436,7 +436,7 @@ def describeFont(font: QFont) -> str:
def qtLambda(func: Callable, *args: Any, **kwargs: Any) -> Callable: def qtLambda(func: Callable, *args: Any, **kwargs: Any) -> Callable:
"""A replacement for Python lambdas that works for Qt slots.""" """A replacement for Python lambdas that works for Qt slots."""
def wrapper(*args_: Any, **kwargs_: Any) -> None: def wrapper(*a_: Any) -> None:
func(*args, **kwargs) func(*args, **kwargs)
return wrapper return wrapper
+3
View File
@@ -73,6 +73,9 @@ VALID_MAP: dict[str, set[str]] = {
"widthCol3", "widthCol4", "wordsPerPage", "countFrom", "clearDouble", "widthCol3", "widthCol4", "wordsPerPage", "countFrom", "clearDouble",
"novelRoot", "novelRoot",
}, },
"GuiOutlineDetails": {
"detailsWidth", "tagsWidth",
}
} }
+3 -3
View File
@@ -35,7 +35,7 @@ from PyQt5.QtWidgets import (
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import simplified from novelwriter.common import qtLambda, simplified
from novelwriter.constants import nwLabels, trConst from novelwriter.constants import nwLabels, trConst
from novelwriter.core.status import NWStatus, StatusEntry from novelwriter.core.status import NWStatus, StatusEntry
from novelwriter.enum import nwStatusShape from novelwriter.enum import nwStatusShape
@@ -365,10 +365,10 @@ class _StatusPage(NFixedPage):
self.delButton.clicked.connect(self._delItem) self.delButton.clicked.connect(self._delItem)
self.upButton = NIconToolButton(self, iSz, "up") self.upButton = NIconToolButton(self, iSz, "up")
self.upButton.clicked.connect(lambda: self._moveItem(-1)) self.upButton.clicked.connect(qtLambda(self._moveItem, -1))
self.dnButton = NIconToolButton(self, iSz, "down") self.dnButton = NIconToolButton(self, iSz, "down")
self.dnButton.clicked.connect(lambda: self._moveItem(1)) self.dnButton.clicked.connect(qtLambda(self._moveItem, 1))
# Edit Form # Edit Form
self.editName = QLineEdit(self) self.editName = QLineEdit(self)
+9 -8
View File
@@ -38,6 +38,7 @@ from PyQt5.QtWidgets import (
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import qtLambda
from novelwriter.constants import nwStyles, nwUnicode from novelwriter.constants import nwStyles, nwUnicode
from novelwriter.enum import nwDocAction, nwDocMode, nwItemType from novelwriter.enum import nwDocAction, nwDocMode, nwItemType
from novelwriter.error import logException from novelwriter.error import logException
@@ -396,25 +397,25 @@ class GuiDocViewer(QTextBrowser):
if userSelection: if userSelection:
mnuCopy = QAction(self.tr("Copy"), ctxMenu) mnuCopy = QAction(self.tr("Copy"), ctxMenu)
mnuCopy.triggered.connect(lambda: self.docAction(nwDocAction.COPY)) mnuCopy.triggered.connect(qtLambda(self.docAction, nwDocAction.COPY))
ctxMenu.addAction(mnuCopy) ctxMenu.addAction(mnuCopy)
ctxMenu.addSeparator() ctxMenu.addSeparator()
mnuSelAll = QAction(self.tr("Select All"), ctxMenu) mnuSelAll = QAction(self.tr("Select All"), ctxMenu)
mnuSelAll.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL)) mnuSelAll.triggered.connect(qtLambda(self.docAction, nwDocAction.SEL_ALL))
ctxMenu.addAction(mnuSelAll) ctxMenu.addAction(mnuSelAll)
mnuSelWord = QAction(self.tr("Select Word"), ctxMenu) mnuSelWord = QAction(self.tr("Select Word"), ctxMenu)
mnuSelWord.triggered.connect( mnuSelWord.triggered.connect(qtLambda(
lambda: self._makePosSelection(QTextCursor.SelectionType.WordUnderCursor, point) self._makePosSelection, QTextCursor.SelectionType.WordUnderCursor, point
) ))
ctxMenu.addAction(mnuSelWord) ctxMenu.addAction(mnuSelWord)
mnuSelPara = QAction(self.tr("Select Paragraph"), ctxMenu) mnuSelPara = QAction(self.tr("Select Paragraph"), ctxMenu)
mnuSelPara.triggered.connect( mnuSelPara.triggered.connect(qtLambda(
lambda: self._makePosSelection(QTextCursor.SelectionType.BlockUnderCursor, point) self._makePosSelection, QTextCursor.SelectionType.BlockUnderCursor, point
) ))
ctxMenu.addAction(mnuSelPara) ctxMenu.addAction(mnuSelPara)
# Open the context menu # Open the context menu
+8 -4
View File
@@ -30,7 +30,7 @@ from PyQt5.QtWidgets import QGridLayout, QLabel, QWidget
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import elide from novelwriter.common import elide
from novelwriter.constants import nwLabels, trConst from novelwriter.constants import nwLabels, nwStats, trConst
from novelwriter.types import ( from novelwriter.types import (
QtAlignLeft, QtAlignLeftBase, QtAlignRight, QtAlignRightBase, QtAlignLeft, QtAlignLeftBase, QtAlignRight, QtAlignRightBase,
QtAlignRightMiddle QtAlignRightMiddle
@@ -62,6 +62,10 @@ class GuiItemDetails(QWidget):
fntValue = self.font() fntValue = self.font()
fntValue.setPointSizeF(0.9*fPt) 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 # Label
self.labelName = QLabel(self.tr("Label"), self) self.labelName = QLabel(self.tr("Label"), self)
self.labelName.setFont(fntLabel) self.labelName.setFont(fntLabel)
@@ -113,7 +117,7 @@ class GuiItemDetails(QWidget):
self.usageData.setWordWrap(True) self.usageData.setWordWrap(True)
# Character Count # Character Count
self.cCountName = QLabel(" "+self.tr("Characters"), self) self.cCountName = QLabel(trStats1, self)
self.cCountName.setFont(fntLabel) self.cCountName.setFont(fntLabel)
self.cCountName.setAlignment(QtAlignRight) self.cCountName.setAlignment(QtAlignRight)
@@ -122,7 +126,7 @@ class GuiItemDetails(QWidget):
self.cCountData.setAlignment(QtAlignRight) self.cCountData.setAlignment(QtAlignRight)
# Word Count # Word Count
self.wCountName = QLabel(" "+self.tr("Words"), self) self.wCountName = QLabel(trStats2, self)
self.wCountName.setFont(fntLabel) self.wCountName.setFont(fntLabel)
self.wCountName.setAlignment(QtAlignRight) self.wCountName.setAlignment(QtAlignRight)
@@ -131,7 +135,7 @@ class GuiItemDetails(QWidget):
self.wCountData.setAlignment(QtAlignRight) self.wCountData.setAlignment(QtAlignRight)
# Paragraph Count # Paragraph Count
self.pCountName = QLabel(" "+self.tr("Paragraphs"), self) self.pCountName = QLabel(trStats3, self)
self.pCountName.setFont(fntLabel) self.pCountName.setFont(fntLabel)
self.pCountName.setAlignment(QtAlignRight) self.pCountName.setAlignment(QtAlignRight)
+4 -11
View File
@@ -39,7 +39,7 @@ from PyQt5.QtWidgets import (
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import minmax from novelwriter.common import minmax, qtLambda
from novelwriter.constants import nwKeyWords, nwLabels, nwStyles, trConst from novelwriter.constants import nwKeyWords, nwLabels, nwStyles, trConst
from novelwriter.core.index import IndexHeading from novelwriter.core.index import IndexHeading
from novelwriter.enum import nwDocMode, nwItemClass, nwOutline from novelwriter.enum import nwDocMode, nwItemClass, nwOutline
@@ -346,7 +346,7 @@ class GuiNovelToolBar(QWidget):
aLast = self.mLastCol.addAction(actionLabel) aLast = self.mLastCol.addAction(actionLabel)
aLast.setCheckable(True) aLast.setCheckable(True)
aLast.setActionGroup(self.gLastCol) aLast.setActionGroup(self.gLastCol)
aLast.triggered.connect(lambda: self.setLastColType(colType)) aLast.triggered.connect(qtLambda(self.setLastColType, colType))
self.aLastCol[colType] = aLast self.aLastCol[colType] = aLast
return return
@@ -484,16 +484,9 @@ class GuiNovelTree(QTreeWidget):
if rootHandle is None: if rootHandle is None:
rootHandle = SHARED.project.tree.findRoot(nwItemClass.NOVEL) rootHandle = SHARED.project.tree.findRoot(nwItemClass.NOVEL)
treeChanged = SHARED.mainGui.projView.changedSince(self._lastBuild)
indexChanged = SHARED.project.index.rootChangedSince(rootHandle, self._lastBuild)
if not (treeChanged or indexChanged or overRide):
logger.debug("No changes have been made to the novel index")
return
selItem = self.selectedItems()
titleKey = None titleKey = None
if selItem: if selItems := self.selectedItems():
titleKey = selItem[0].data(self.C_DATA, self.D_KEY) titleKey = selItems[0].data(self.C_DATA, self.D_KEY)
self._populateTree(rootHandle) self._populateTree(rootHandle)
SHARED.project.data.setLastHandle(rootHandle, "novelTree") SHARED.project.data.setLastHandle(rootHandle, "novelTree")
+73 -170
View File
@@ -42,7 +42,7 @@ from PyQt5.QtWidgets import (
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import checkInt, formatFileFilter, makeFileNameSafe 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.enum import nwDocMode, nwItemClass, nwItemLayout, nwItemType, nwOutline
from novelwriter.error import logException from novelwriter.error import logException
from novelwriter.extensions.configlayout import NColourLabel from novelwriter.extensions.configlayout import NColourLabel
@@ -135,12 +135,14 @@ class GuiOutlineView(QWidget):
self.outlineBar.populateNovelList() self.outlineBar.populateNovelList()
self.outlineBar.setCurrentRoot(lastOutline) self.outlineBar.setCurrentRoot(lastOutline)
self.outlineBar.setEnabled(True) self.outlineBar.setEnabled(True)
self.outlineData.loadGuiSettings()
return return
def closeProjectTasks(self) -> None: def closeProjectTasks(self) -> None:
"""Run closing project tasks.""" """Run closing project tasks."""
self.outlineTree.closeProjectTasks() self.outlineTree.closeProjectTasks()
self.outlineData.saveGuiSettings()
self.outlineData.updateClasses() self.outlineData.updateClasses()
self.clearOutline() self.clearOutline()
return return
@@ -799,6 +801,10 @@ class GuiOutlineDetails(QScrollArea):
bFont = SHARED.theme.guiFontB 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 # Details Area
self.titleLabel = QLabel(self.tr("Title"), self) self.titleLabel = QLabel(self.tr("Title"), self)
self.fileLabel = QLabel(self.tr("Document"), self) self.fileLabel = QLabel(self.tr("Document"), self)
@@ -819,9 +825,9 @@ class GuiOutlineDetails(QScrollArea):
self.itemValue.setMaximumWidth(maxTitle) self.itemValue.setMaximumWidth(maxTitle)
# Stats Area # Stats Area
self.cCLabel = QLabel(self.tr("Characters"), self) self.cCLabel = QLabel(trStats1, self)
self.wCLabel = QLabel(self.tr("Words"), self) self.wCLabel = QLabel(trStats2, self)
self.pCLabel = QLabel(self.tr("Paragraphs"), self) self.pCLabel = QLabel(trStats3, self)
self.cCValue = QLabel("", self) self.cCValue = QLabel("", self)
self.wCValue = QLabel("", self) self.wCValue = QLabel("", self)
self.pCValue = QLabel("", self) self.pCValue = QLabel("", self)
@@ -848,94 +854,9 @@ class GuiOutlineDetails(QScrollArea):
self.synopLWrap = QHBoxLayout() self.synopLWrap = QHBoxLayout()
self.synopLWrap.addWidget(self.synopValue, 1) 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 # Selected Item Details
self.mainGroup = QGroupBox(self.tr("Title Details"), self)
self.mainForm = QGridLayout() self.mainForm = QGridLayout()
self.mainGroup = QGroupBox(self.tr("Title Details"), self)
self.mainGroup.setLayout(self.mainForm) self.mainGroup.setLayout(self.mainForm)
self.mainForm.addWidget(self.titleLabel, 0, 0, 1, 1, QtAlignLeftTop) self.mainForm.addWidget(self.titleLabel, 0, 0, 1, 1, QtAlignLeftTop)
@@ -959,46 +880,37 @@ class GuiOutlineDetails(QScrollArea):
self.mainForm.setVerticalSpacing(vSpace) self.mainForm.setVerticalSpacing(vSpace)
# Selected Item Tags # Selected Item Tags
self.tagsGroup = QGroupBox(self.tr("Reference Tags"), self)
self.tagsForm = QGridLayout() self.tagsForm = QGridLayout()
self.tagsGroup = QGroupBox(self.tr("Reference Tags"), self)
self.tagsGroup.setLayout(self.tagsForm) self.tagsGroup.setLayout(self.tagsForm)
self.tagsForm.addWidget(self.povKeyLabel, 0, 0, QtAlignLeftTop) self.tagValues: dict[str, tuple[QLabel, QLabel]] = {}
self.tagsForm.addLayout(self.povKeyLWrap, 0, 1, QtAlignLeftTop) for key in nwKeyWords.ALL_KEYS:
self.tagsForm.addWidget(self.focKeyLabel, 1, 0, QtAlignLeftTop) label = QLabel(trConst(nwLabels.KEY_NAME[key]), self)
self.tagsForm.addLayout(self.focKeyLWrap, 1, 1, QtAlignLeftTop) label.setFont(bFont)
self.tagsForm.addWidget(self.chrKeyLabel, 2, 0, QtAlignLeftTop) value = QLabel("", self)
self.tagsForm.addLayout(self.chrKeyLWrap, 2, 1, QtAlignLeftTop) value.setWordWrap(True)
self.tagsForm.addWidget(self.pltKeyLabel, 3, 0, QtAlignLeftTop) value.linkActivated.connect(lambda x: self.itemTagClicked.emit(x))
self.tagsForm.addLayout(self.pltKeyLWrap, 3, 1, QtAlignLeftTop) layout = QHBoxLayout()
self.tagsForm.addWidget(self.timKeyLabel, 4, 0, QtAlignLeftTop) layout.addWidget(value, 1)
self.tagsForm.addLayout(self.timKeyLWrap, 4, 1, QtAlignLeftTop) n = len(self.tagValues)
self.tagsForm.addWidget(self.wldKeyLabel, 5, 0, QtAlignLeftTop) self.tagsForm.addWidget(label, n, 0, QtAlignLeftTop)
self.tagsForm.addLayout(self.wldKeyLWrap, 5, 1, QtAlignLeftTop) self.tagsForm.addLayout(layout, n, 1, QtAlignLeftTop)
self.tagsForm.addWidget(self.objKeyLabel, 6, 0, QtAlignLeftTop) self.tagValues[key] = (label, value)
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.tagsForm.setColumnStretch(1, 1) self.tagsForm.setColumnStretch(1, 1)
self.tagsForm.setRowStretch(10, 1) self.tagsForm.setRowStretch(len(self.tagValues), 1)
self.tagsForm.setHorizontalSpacing(hSpace) self.tagsForm.setHorizontalSpacing(hSpace)
self.tagsForm.setVerticalSpacing(vSpace) self.tagsForm.setVerticalSpacing(vSpace)
# Assemble # Assemble
self.outerWidget = QWidget(self) self.mainSplit = QSplitter(Qt.Orientation.Horizontal)
self.outerBox = QHBoxLayout() self.mainSplit.addWidget(self.mainGroup)
self.outerBox.addWidget(self.mainGroup, 0) self.mainSplit.addWidget(self.tagsGroup)
self.outerBox.addWidget(self.tagsGroup, 1) self.mainSplit.setCollapsible(0, False)
self.mainSplit.setCollapsible(1, False)
self.outerWidget.setLayout(self.outerBox) self.setWidget(self.mainSplit)
self.setWidget(self.outerWidget)
self.setHorizontalScrollBarPolicy(QtScrollAsNeeded) self.setHorizontalScrollBarPolicy(QtScrollAsNeeded)
self.setVerticalScrollBarPolicy(QtScrollAsNeeded) self.setVerticalScrollBarPolicy(QtScrollAsNeeded)
@@ -1024,27 +936,42 @@ class GuiOutlineDetails(QScrollArea):
self.updateClasses() self.updateClasses()
return 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: def clearDetails(self) -> None:
"""Clear all the data labels.""" """Clear all the data labels."""
self.titleLabel.setText(self.tr("Title")) self.titleLabel.setText(self.tr("Title"))
self.titleValue.setText("") self.titleValue.clear()
self.fileValue.setText("") self.fileValue.clear()
self.itemValue.setText("") self.itemValue.clear()
self.cCValue.setText("") self.cCValue.clear()
self.wCValue.setText("") self.wCValue.clear()
self.pCValue.setText("") self.pCValue.clear()
self.synopValue.setText("") self.synopValue.clear()
self.povKeyValue.setText("")
self.focKeyValue.setText("") for _, value in self.tagValues.values():
self.chrKeyValue.setText("") value.clear()
self.pltKeyValue.setText("")
self.timKeyValue.setText("")
self.wldKeyValue.setText("")
self.objKeyValue.setText("")
self.entKeyValue.setText("")
self.cstKeyValue.setText("")
self.styKeyValue.setText("")
self.mntKeyValue.setText("")
self.updateClasses() self.updateClasses()
return return
@@ -1076,17 +1003,8 @@ class GuiOutlineDetails(QScrollArea):
self.synopValue.setText(novIdx.synopsis) self.synopValue.setText(novIdx.synopsis)
self.povKeyValue.setText(self._formatTags(novRefs, nwKeyWords.POV_KEY)) for key, (_, value) in self.tagValues.items():
self.focKeyValue.setText(self._formatTags(novRefs, nwKeyWords.FOCUS_KEY)) value.setText(self._formatTags(novRefs, 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))
return return
@@ -1094,27 +1012,12 @@ class GuiOutlineDetails(QScrollArea):
def updateClasses(self) -> None: def updateClasses(self) -> None:
"""Update the visibility status of class details.""" """Update the visibility status of class details."""
usedClasses = SHARED.project.tree.rootClasses() usedClasses = SHARED.project.tree.rootClasses()
for key, itemClass in nwKeyWords.KEY_CLASS.items():
pltVisible = nwItemClass.PLOT in usedClasses visible = itemClass in usedClasses
timVisible = nwItemClass.TIMELINE in usedClasses if key in self.tagValues:
wldVisible = nwItemClass.WORLD in usedClasses label, value = self.tagValues[key]
objVisible = nwItemClass.OBJECT in usedClasses label.setVisible(visible)
entVisible = nwItemClass.ENTITY in usedClasses value.setVisible(visible)
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)
return return
@staticmethod @staticmethod
+115 -124
View File
@@ -29,7 +29,6 @@ from __future__ import annotations
import logging import logging
from enum import Enum from enum import Enum
from time import time
from PyQt5.QtCore import QPoint, Qt, QTimer, pyqtSignal, pyqtSlot from PyQt5.QtCore import QPoint, Qt, QTimer, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QDragEnterEvent, QDragMoveEvent, QDropEvent, QIcon, QMouseEvent, QPalette from PyQt5.QtGui import QDragEnterEvent, QDragMoveEvent, QDropEvent, QIcon, QMouseEvent, QPalette
@@ -39,7 +38,7 @@ from PyQt5.QtWidgets import (
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import minmax from novelwriter.common import minmax, qtLambda
from novelwriter.constants import nwLabels, nwStyles, nwUnicode, trConst from novelwriter.constants import nwLabels, nwStyles, nwUnicode, trConst
from novelwriter.core.coretools import DocDuplicator, DocMerger, DocSplitter from novelwriter.core.coretools import DocDuplicator, DocMerger, DocSplitter
from novelwriter.core.item import NWItem from novelwriter.core.item import NWItem
@@ -97,37 +96,37 @@ class GuiProjectView(QWidget):
self.keyMoveUp = QShortcut(self.projTree) self.keyMoveUp = QShortcut(self.projTree)
self.keyMoveUp.setKey("Ctrl+Up") self.keyMoveUp.setKey("Ctrl+Up")
self.keyMoveUp.setContext(Qt.ShortcutContext.WidgetShortcut) self.keyMoveUp.setContext(Qt.ShortcutContext.WidgetShortcut)
self.keyMoveUp.activated.connect(lambda: self.projTree.moveTreeItem(-1)) self.keyMoveUp.activated.connect(qtLambda(self.projTree.moveTreeItem, -1))
self.keyMoveDn = QShortcut(self.projTree) self.keyMoveDn = QShortcut(self.projTree)
self.keyMoveDn.setKey("Ctrl+Down") self.keyMoveDn.setKey("Ctrl+Down")
self.keyMoveDn.setContext(Qt.ShortcutContext.WidgetShortcut) self.keyMoveDn.setContext(Qt.ShortcutContext.WidgetShortcut)
self.keyMoveDn.activated.connect(lambda: self.projTree.moveTreeItem(1)) self.keyMoveDn.activated.connect(qtLambda(self.projTree.moveTreeItem, 1))
self.keyGoPrev = QShortcut(self.projTree) self.keyGoPrev = QShortcut(self.projTree)
self.keyGoPrev.setKey("Alt+Up") self.keyGoPrev.setKey("Alt+Up")
self.keyGoPrev.setContext(Qt.ShortcutContext.WidgetShortcut) self.keyGoPrev.setContext(Qt.ShortcutContext.WidgetShortcut)
self.keyGoPrev.activated.connect(lambda: self.projTree.moveToNextItem(-1)) self.keyGoPrev.activated.connect(qtLambda(self.projTree.moveToNextItem, -1))
self.keyGoNext = QShortcut(self.projTree) self.keyGoNext = QShortcut(self.projTree)
self.keyGoNext.setKey("Alt+Down") self.keyGoNext.setKey("Alt+Down")
self.keyGoNext.setContext(Qt.ShortcutContext.WidgetShortcut) self.keyGoNext.setContext(Qt.ShortcutContext.WidgetShortcut)
self.keyGoNext.activated.connect(lambda: self.projTree.moveToNextItem(1)) self.keyGoNext.activated.connect(qtLambda(self.projTree.moveToNextItem, 1))
self.keyGoUp = QShortcut(self.projTree) self.keyGoUp = QShortcut(self.projTree)
self.keyGoUp.setKey("Alt+Left") self.keyGoUp.setKey("Alt+Left")
self.keyGoUp.setContext(Qt.ShortcutContext.WidgetShortcut) self.keyGoUp.setContext(Qt.ShortcutContext.WidgetShortcut)
self.keyGoUp.activated.connect(lambda: self.projTree.moveToLevel(-1)) self.keyGoUp.activated.connect(qtLambda(self.projTree.moveToLevel, -1))
self.keyGoDown = QShortcut(self.projTree) self.keyGoDown = QShortcut(self.projTree)
self.keyGoDown.setKey("Alt+Right") self.keyGoDown.setKey("Alt+Right")
self.keyGoDown.setContext(Qt.ShortcutContext.WidgetShortcut) self.keyGoDown.setContext(Qt.ShortcutContext.WidgetShortcut)
self.keyGoDown.activated.connect(lambda: self.projTree.moveToLevel(1)) self.keyGoDown.activated.connect(qtLambda(self.projTree.moveToLevel, 1))
self.keyContext = QShortcut(self.projTree) self.keyContext = QShortcut(self.projTree)
self.keyContext.setKey("Ctrl+.") self.keyContext.setKey("Ctrl+.")
self.keyContext.setContext(Qt.ShortcutContext.WidgetShortcut) self.keyContext.setContext(Qt.ShortcutContext.WidgetShortcut)
self.keyContext.activated.connect(lambda: self.projTree.openContextOnSelected()) self.keyContext.activated.connect(self.projTree.openContextMenu)
# Signals # Signals
self.selectedItemChanged.connect(self.projBar.treeSelectionChanged) self.selectedItemChanged.connect(self.projBar.treeSelectionChanged)
@@ -138,7 +137,6 @@ class GuiProjectView(QWidget):
self.emptyTrash = self.projTree.emptyTrash self.emptyTrash = self.projTree.emptyTrash
self.requestDeleteItem = self.projTree.requestDeleteItem self.requestDeleteItem = self.projTree.requestDeleteItem
self.getSelectedHandle = self.projTree.getSelectedHandle self.getSelectedHandle = self.projTree.getSelectedHandle
self.changedSince = self.projTree.changedSince
return return
@@ -292,38 +290,38 @@ class GuiProjectToolBar(QWidget):
# Move Buttons # Move Buttons
self.tbMoveU = NIconToolButton(self, iSz) self.tbMoveU = NIconToolButton(self, iSz)
self.tbMoveU.setToolTip("%s [Ctrl+Up]" % self.tr("Move Up")) self.tbMoveU.setToolTip("%s [Ctrl+Up]" % self.tr("Move Up"))
self.tbMoveU.clicked.connect(lambda: self.projTree.moveTreeItem(-1)) self.tbMoveU.clicked.connect(qtLambda(self.projTree.moveTreeItem, -1))
self.tbMoveD = NIconToolButton(self, iSz) self.tbMoveD = NIconToolButton(self, iSz)
self.tbMoveD.setToolTip("%s [Ctrl+Down]" % self.tr("Move Down")) self.tbMoveD.setToolTip("%s [Ctrl+Down]" % self.tr("Move Down"))
self.tbMoveD.clicked.connect(lambda: self.projTree.moveTreeItem(1)) self.tbMoveD.clicked.connect(qtLambda(self.projTree.moveTreeItem, 1))
# Add Item Menu # Add Item Menu
self.mAdd = QMenu(self) self.mAdd = QMenu(self)
self.aAddEmpty = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["document"])) self.aAddEmpty = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["document"]))
self.aAddEmpty.triggered.connect( self.aAddEmpty.triggered.connect(
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=0, isNote=False) qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=0, isNote=False)
) )
self.aAddChap = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h2"])) self.aAddChap = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h2"]))
self.aAddChap.triggered.connect( self.aAddChap.triggered.connect(
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=2, isNote=False) qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=2, isNote=False)
) )
self.aAddScene = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"])) self.aAddScene = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"]))
self.aAddScene.triggered.connect( self.aAddScene.triggered.connect(
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=3, isNote=False) qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=3, isNote=False)
) )
self.aAddNote = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["note"])) self.aAddNote = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["note"]))
self.aAddNote.triggered.connect( self.aAddNote.triggered.connect(
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True) qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=1, isNote=True)
) )
self.aAddFolder = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["folder"])) self.aAddFolder = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["folder"]))
self.aAddFolder.triggered.connect( self.aAddFolder.triggered.connect(
lambda: self.projTree.newTreeItem(nwItemType.FOLDER) qtLambda(self.projTree.newTreeItem, nwItemType.FOLDER)
) )
self.mTemplates = _UpdatableMenu(self.mAdd) self.mTemplates = _UpdatableMenu(self.mAdd)
@@ -343,13 +341,17 @@ class GuiProjectToolBar(QWidget):
self.mMore = QMenu(self) self.mMore = QMenu(self)
self.aExpand = self.mMore.addAction(self.tr("Expand All")) self.aExpand = self.mMore.addAction(self.tr("Expand All"))
self.aExpand.triggered.connect(lambda: self.projTree.setExpandedFromHandle(None, True)) self.aExpand.triggered.connect(
qtLambda(self.projTree.setExpandedFromHandle, None, True)
)
self.aCollapse = self.mMore.addAction(self.tr("Collapse All")) self.aCollapse = self.mMore.addAction(self.tr("Collapse All"))
self.aCollapse.triggered.connect(lambda: self.projTree.setExpandedFromHandle(None, False)) self.aCollapse.triggered.connect(
qtLambda(self.projTree.setExpandedFromHandle, None, False)
)
self.aEmptyTrash = self.mMore.addAction(self.tr("Empty Trash")) self.aEmptyTrash = self.mMore.addAction(self.tr("Empty Trash"))
self.aEmptyTrash.triggered.connect(lambda: self.projTree.emptyTrash()) self.aEmptyTrash.triggered.connect(qtLambda(self.projTree.emptyTrash))
self.tbMore = NIconToolButton(self, iSz) self.tbMore = NIconToolButton(self, iSz)
self.tbMore.setToolTip(self.tr("More Options")) self.tbMore.setToolTip(self.tr("More Options"))
@@ -422,7 +424,7 @@ class GuiProjectToolBar(QWidget):
action.setData(tHandle) action.setData(tHandle)
action.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass])) action.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass]))
action.triggered.connect( action.triggered.connect(
lambda _, tHandle=tHandle: self.projView.setSelectedHandle(tHandle, doScroll=True) qtLambda(self.projView.setSelectedHandle, tHandle, doScroll=True)
) )
return return
@@ -461,7 +463,9 @@ class GuiProjectToolBar(QWidget):
def addClass(itemClass: nwItemClass) -> None: def addClass(itemClass: nwItemClass) -> None:
aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass])) aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass]))
aNew.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[itemClass])) aNew.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[itemClass]))
aNew.triggered.connect(lambda: self.projTree.newTreeItem(nwItemType.ROOT, itemClass)) aNew.triggered.connect(
qtLambda(self.projTree.newTreeItem, nwItemType.ROOT, itemClass)
)
self.mAddRoot.addAction(aNew) self.mAddRoot.addAction(aNew)
return return
@@ -504,7 +508,6 @@ class GuiProjectTree(QTreeWidget):
# Internal Variables # Internal Variables
self._treeMap: dict[str, QTreeWidgetItem] = {} self._treeMap: dict[str, QTreeWidgetItem] = {}
self._timeChanged = 0.0
self._popAlert = None self._popAlert = None
self._actHandle = None self._actHandle = None
@@ -518,7 +521,7 @@ class GuiProjectTree(QTreeWidget):
# Context Menu # Context Menu
self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.customContextMenuRequested.connect(self._openContextMenu) self.customContextMenuRequested.connect(self.openContextMenu)
# Tree Settings # Tree Settings
iPx = SHARED.theme.baseIconHeight iPx = SHARED.theme.baseIconHeight
@@ -602,7 +605,6 @@ class GuiProjectTree(QTreeWidget):
"""Clear the GUI content and the related map.""" """Clear the GUI content and the related map."""
self.clear() self.clear()
self._treeMap = {} self._treeMap = {}
self._timeChanged = 0.0
return return
def createNewNote(self, tag: str, itemClass: nwItemClass) -> None: def createNewNote(self, tag: str, itemClass: nwItemClass) -> None:
@@ -1121,12 +1123,10 @@ class GuiProjectTree(QTreeWidget):
""" """
logger.debug("Building the project tree ...") logger.debug("Building the project tree ...")
self.clearTree() self.clearTree()
count = 0
for nwItem in SHARED.project.iterProjectItems(): for nwItem in SHARED.project.iterProjectItems():
count += 1
self._addTreeItem(nwItem) self._addTreeItem(nwItem)
if count > 0: self.setActiveHandle(self._actHandle)
logger.info("%d item(s) added to the project tree", count) logger.info("%d item(s) added to the project tree", len(self._treeMap))
return return
def getSelectedHandle(self) -> str | None: def getSelectedHandle(self) -> str | None:
@@ -1137,19 +1137,13 @@ class GuiProjectTree(QTreeWidget):
return items[0].data(self.C_DATA, self.D_HANDLE) return items[0].data(self.C_DATA, self.D_HANDLE)
return None return None
def setSelectedHandle(self, tHandle: str | None, doScroll: bool = False) -> bool: def setSelectedHandle(self, tHandle: str | None, doScroll: bool = False) -> None:
"""Set a specific handle as the selected item.""" """Set a specific handle as the selected item."""
tItem = self._getTreeItem(tHandle)
if tItem is None:
return False
if tHandle in self._treeMap: if tHandle in self._treeMap:
self.setCurrentItem(self._treeMap[tHandle]) self.setCurrentItem(self._treeMap[tHandle])
if (indexes := self.selectedIndexes()) and doScroll:
if (indexes := self.selectedIndexes()) and doScroll: self.scrollTo(indexes[0], QAbstractItemView.ScrollHint.PositionAtCenter)
self.scrollTo(indexes[0], QAbstractItemView.ScrollHint.PositionAtCenter) return
return True
def setActiveHandle(self, tHandle: str | None) -> None: def setActiveHandle(self, tHandle: str | None) -> None:
"""Highlight the rows associated with a given handle.""" """Highlight the rows associated with a given handle."""
@@ -1169,19 +1163,52 @@ class GuiProjectTree(QTreeWidget):
status for all child items. If tHandle is None, it affects the status for all child items. If tHandle is None, it affects the
entire tree. entire tree.
""" """
trItem = self._getTreeItem(tHandle) or self.invisibleRootItem() item = self._getTreeItem(tHandle) or self.invisibleRootItem()
self._recursiveSetExpanded(trItem, isExpanded) self._recursiveSetExpanded(item, isExpanded)
return return
def openContextOnSelected(self) -> bool: ##
"""Open the context menu on the current selected item.""" # Public Slots
if items := self.selectedItems(): ##
return self._openContextMenu(self.visualItemRect(items[0]).center())
return False
def changedSince(self, checkTime: float) -> bool: @pyqtSlot()
"""Check if the tree has changed since a given time.""" @pyqtSlot("QPoint")
return self._timeChanged > checkTime def openContextMenu(self, clickPos: QPoint | None = None) -> None:
"""The user right clicked an element in the project tree, so we
open a context menu in-place.
"""
if clickPos is None and (items := self.selectedItems()):
clickPos = self.visualItemRect(items[0]).center()
if clickPos is not None:
tItem = None
tHandle = None
hasChild = False
sItem = self.itemAt(clickPos)
sItems = self.selectedItems()
if isinstance(sItem, QTreeWidgetItem):
tHandle = sItem.data(self.C_DATA, self.D_HANDLE)
tItem = SHARED.project.tree[tHandle]
hasChild = sItem.childCount() > 0
if tItem is None or tHandle is None:
logger.debug("No item found")
return
ctxMenu = _TreeContextMenu(self, tItem)
trashHandle = SHARED.project.tree.trashRoot
if trashHandle and tHandle == trashHandle:
ctxMenu.buildTrashMenu()
elif len(sItems) > 1:
handles = [str(x.data(self.C_DATA, self.D_HANDLE)) for x in sItems]
ctxMenu.buildMultiSelectMenu(handles)
else:
ctxMenu.buildSingleSelectMenu(hasChild)
ctxMenu.exec(self.viewport().mapToGlobal(clickPos))
ctxMenu.deleteLater()
return
## ##
# Private Slots # Private Slots
@@ -1224,40 +1251,6 @@ class GuiProjectTree(QTreeWidget):
return return
@pyqtSlot("QPoint")
def _openContextMenu(self, clickPos: QPoint) -> bool:
"""The user right clicked an element in the project tree, so we
open a context menu in-place.
"""
tItem = None
tHandle = None
hasChild = False
sItem = self.itemAt(clickPos)
sItems = self.selectedItems()
if isinstance(sItem, QTreeWidgetItem):
tHandle = sItem.data(self.C_DATA, self.D_HANDLE)
tItem = SHARED.project.tree[tHandle]
hasChild = sItem.childCount() > 0
if tItem is None or tHandle is None:
logger.debug("No item found")
return False
ctxMenu = _TreeContextMenu(self, tItem)
trashHandle = SHARED.project.tree.trashRoot
if trashHandle and tHandle == trashHandle:
ctxMenu.buildTrashMenu()
elif len(sItems) > 1:
handles = [str(x.data(self.C_DATA, self.D_HANDLE)) for x in sItems]
ctxMenu.buildMultiSelectMenu(handles)
else:
ctxMenu.buildSingleSelectMenu(hasChild)
ctxMenu.exec(self.viewport().mapToGlobal(clickPos))
ctxMenu.deleteLater()
return True
@pyqtSlot() @pyqtSlot()
def _doAutoScroll(self) -> None: def _doAutoScroll(self) -> None:
"""Scroll one item up or down based on direction value.""" """Scroll one item up or down based on direction value."""
@@ -1397,11 +1390,6 @@ class GuiProjectTree(QTreeWidget):
return return
def _getItemWordCount(self, tHandle: str) -> int:
"""Return the word count of a given item handle."""
tItem = self._getTreeItem(tHandle)
return int(tItem.data(self.C_DATA, self.D_WORDS)) if tItem else 0
def _getTreeItem(self, tHandle: str | None) -> QTreeWidgetItem | None: def _getTreeItem(self, tHandle: str | None) -> QTreeWidgetItem | None:
"""Return the QTreeWidgetItem of a given item handle.""" """Return the QTreeWidgetItem of a given item handle."""
return self._treeMap.get(tHandle, None) if tHandle else None return self._treeMap.get(tHandle, None) if tHandle else None
@@ -1658,7 +1646,6 @@ class GuiProjectTree(QTreeWidget):
signals. A flush is only needed if an item is moved, created or signals. A flush is only needed if an item is moved, created or
deleted. deleted.
""" """
self._timeChanged = time()
SHARED.project.setProjectChanged(True) SHARED.project.setProjectChanged(True)
if flush: if flush:
self.saveTreeOrder() self.saveTreeOrder()
@@ -1785,7 +1772,7 @@ class _TreeContextMenu(QMenu):
# Edit Item Settings # Edit Item Settings
action = self.addAction(self.tr("Rename")) action = self.addAction(self.tr("Rename"))
action.triggered.connect(lambda: self.projTree.renameTreeItem(self._handle)) action.triggered.connect(qtLambda(self.projTree.renameTreeItem, self._handle))
if isFile: if isFile:
self._itemHeader() self._itemHeader()
self._itemActive(False) self._itemActive(False)
@@ -1821,13 +1808,15 @@ class _TreeContextMenu(QMenu):
def _docActions(self) -> None: def _docActions(self) -> None:
"""Add document actions.""" """Add document actions."""
action = self.addAction(self.tr("Open Document")) action = self.addAction(self.tr("Open Document"))
action.triggered.connect( action.triggered.connect(qtLambda(
lambda: self.projView.openDocumentRequest.emit(self._handle, nwDocMode.EDIT, "", True) self.projView.openDocumentRequest.emit,
) self._handle, nwDocMode.EDIT, "", True
))
action = self.addAction(self.tr("View Document")) action = self.addAction(self.tr("View Document"))
action.triggered.connect( action.triggered.connect(qtLambda(
lambda: self.projView.openDocumentRequest.emit(self._handle, nwDocMode.VIEW, "", False) self.projView.openDocumentRequest.emit,
) self._handle, nwDocMode.VIEW, "", False
))
return return
def _itemCreation(self) -> None: def _itemCreation(self) -> None:
@@ -1846,7 +1835,7 @@ class _TreeContextMenu(QMenu):
if hItem := SHARED.project.index.getItemHeading(self._handle, "T0001"): if hItem := SHARED.project.index.getItemHeading(self._handle, "T0001"):
action = self.addAction(self.tr("Rename to Heading")) action = self.addAction(self.tr("Rename to Heading"))
action.triggered.connect( action.triggered.connect(
lambda: self.projTree.renameTreeItem(self._handle, hItem.title) qtLambda(self.projTree.renameTreeItem, self._handle, hItem.title)
) )
return return
@@ -1855,9 +1844,9 @@ class _TreeContextMenu(QMenu):
if multi: if multi:
mSub = self.addMenu(self.tr("Set Active to ...")) mSub = self.addMenu(self.tr("Set Active to ..."))
aOne = mSub.addAction(SHARED.theme.getIcon("checked"), self.projTree.trActive) aOne = mSub.addAction(SHARED.theme.getIcon("checked"), self.projTree.trActive)
aOne.triggered.connect(lambda: self._iterItemActive(True)) aOne.triggered.connect(qtLambda(self._iterItemActive, True))
aTwo = mSub.addAction(SHARED.theme.getIcon("unchecked"), self.projTree.trInactive) aTwo = mSub.addAction(SHARED.theme.getIcon("unchecked"), self.projTree.trInactive)
aTwo.triggered.connect(lambda: self._iterItemActive(False)) aTwo.triggered.connect(qtLambda(self._iterItemActive, False))
else: else:
action = self.addAction(self.tr("Toggle Active")) action = self.addAction(self.tr("Toggle Active"))
action.triggered.connect(self._toggleItemActive) action.triggered.connect(self._toggleItemActive)
@@ -1868,37 +1857,39 @@ class _TreeContextMenu(QMenu):
if self._item.isNovelLike(): if self._item.isNovelLike():
menu = self.addMenu(self.tr("Set Status to ...")) menu = self.addMenu(self.tr("Set Status to ..."))
current = self._item.itemStatus current = self._item.itemStatus
for n, (key, entry) in enumerate(SHARED.project.data.itemStatus.iterItems()): for key, entry in SHARED.project.data.itemStatus.iterItems():
name = entry.name name = entry.name
if not multi and current == key: if not multi and current == key:
name += f" ({nwUnicode.U_CHECK})" name += f" ({nwUnicode.U_CHECK})"
action = menu.addAction(entry.icon, name) action = menu.addAction(entry.icon, name)
if multi: if multi:
action.triggered.connect(lambda n, key=key: self._iterSetItemStatus(key)) action.triggered.connect(qtLambda(self._iterSetItemStatus, key))
else: else:
action.triggered.connect(lambda n, key=key: self._changeItemStatus(key)) action.triggered.connect(qtLambda(self._changeItemStatus, key))
menu.addSeparator() menu.addSeparator()
action = menu.addAction(self.tr("Manage Labels ...")) action = menu.addAction(self.tr("Manage Labels ..."))
action.triggered.connect( action.triggered.connect(qtLambda(
lambda: self.projView.projectSettingsRequest.emit(GuiProjectSettings.PAGE_STATUS) self.projView.projectSettingsRequest.emit,
) GuiProjectSettings.PAGE_STATUS
))
else: else:
menu = self.addMenu(self.tr("Set Importance to ...")) menu = self.addMenu(self.tr("Set Importance to ..."))
current = self._item.itemImport current = self._item.itemImport
for n, (key, entry) in enumerate(SHARED.project.data.itemImport.iterItems()): for key, entry in SHARED.project.data.itemImport.iterItems():
name = entry.name name = entry.name
if not multi and current == key: if not multi and current == key:
name += f" ({nwUnicode.U_CHECK})" name += f" ({nwUnicode.U_CHECK})"
action = menu.addAction(entry.icon, name) action = menu.addAction(entry.icon, name)
if multi: if multi:
action.triggered.connect(lambda n, key=key: self._iterSetItemImport(key)) action.triggered.connect(qtLambda(self._iterSetItemImport, key))
else: else:
action.triggered.connect(lambda n, key=key: self._changeItemImport(key)) action.triggered.connect(qtLambda(self._changeItemImport, key))
menu.addSeparator() menu.addSeparator()
action = menu.addAction(self.tr("Manage Labels ...")) action = menu.addAction(self.tr("Manage Labels ..."))
action.triggered.connect( action.triggered.connect(qtLambda(
lambda: self.projView.projectSettingsRequest.emit(GuiProjectSettings.PAGE_IMPORT) self.projView.projectSettingsRequest.emit,
) GuiProjectSettings.PAGE_IMPORT
))
return return
def _itemTransform(self, isFile: bool, isFolder: bool, hasChild: bool) -> None: def _itemTransform(self, isFile: bool, isFolder: bool, hasChild: bool) -> None:
@@ -1917,33 +1908,33 @@ class _TreeContextMenu(QMenu):
if isNoteFile and self._item.documentAllowed(): if isNoteFile and self._item.documentAllowed():
action = menu.addAction(self.tr("Convert to {0}").format(trDoc)) action = menu.addAction(self.tr("Convert to {0}").format(trDoc))
action.triggered.connect(lambda: self._changeItemLayout(loDoc)) action.triggered.connect(qtLambda(self._changeItemLayout, loDoc))
if isDocFile: if isDocFile:
action = menu.addAction(self.tr("Convert to {0}").format(trNote)) action = menu.addAction(self.tr("Convert to {0}").format(trNote))
action.triggered.connect(lambda: self._changeItemLayout(loNote)) action.triggered.connect(qtLambda(self._changeItemLayout, loNote))
if isFolder and self._item.documentAllowed(): if isFolder and self._item.documentAllowed():
action = menu.addAction(self.tr("Convert to {0}").format(trDoc)) action = menu.addAction(self.tr("Convert to {0}").format(trDoc))
action.triggered.connect(lambda: self._covertFolderToFile(loDoc)) action.triggered.connect(qtLambda(self._covertFolderToFile, loDoc))
if isFolder: if isFolder:
action = menu.addAction(self.tr("Convert to {0}").format(trNote)) action = menu.addAction(self.tr("Convert to {0}").format(trNote))
action.triggered.connect(lambda: self._covertFolderToFile(loNote)) action.triggered.connect(qtLambda(self._covertFolderToFile, loNote))
if hasChild and isFile: if hasChild and isFile:
action = menu.addAction(self.tr("Merge Child Items into Self")) action = menu.addAction(self.tr("Merge Child Items into Self"))
action.triggered.connect(lambda: tree._mergeDocuments(tHandle, False)) action.triggered.connect(qtLambda(tree._mergeDocuments, tHandle, False))
action = menu.addAction(self.tr("Merge Child Items into New")) action = menu.addAction(self.tr("Merge Child Items into New"))
action.triggered.connect(lambda: tree._mergeDocuments(tHandle, True)) action.triggered.connect(qtLambda(tree._mergeDocuments, tHandle, True))
if hasChild and isFolder: if hasChild and isFolder:
action = menu.addAction(self.tr("Merge Documents in Folder")) action = menu.addAction(self.tr("Merge Documents in Folder"))
action.triggered.connect(lambda: tree._mergeDocuments(tHandle, True)) action.triggered.connect(qtLambda(tree._mergeDocuments, tHandle, True))
if isFile: if isFile:
action = menu.addAction(self.tr("Split Document by Headings")) action = menu.addAction(self.tr("Split Document by Headings"))
action.triggered.connect(lambda: tree._splitDocument(tHandle)) action.triggered.connect(qtLambda(tree._splitDocument, tHandle))
return return
@@ -1953,19 +1944,19 @@ class _TreeContextMenu(QMenu):
tHandle = self._handle tHandle = self._handle
if hasChild: if hasChild:
action = self.addAction(self.tr("Expand All")) action = self.addAction(self.tr("Expand All"))
action.triggered.connect(lambda: tree.setExpandedFromHandle(tHandle, True)) action.triggered.connect(qtLambda(tree.setExpandedFromHandle, tHandle, True))
action = self.addAction(self.tr("Collapse All")) action = self.addAction(self.tr("Collapse All"))
action.triggered.connect(lambda: tree.setExpandedFromHandle(tHandle, False)) action.triggered.connect(qtLambda(tree.setExpandedFromHandle, tHandle, False))
action = self.addAction(self.tr("Duplicate")) action = self.addAction(self.tr("Duplicate"))
action.triggered.connect(lambda: tree._duplicateFromHandle(tHandle)) action.triggered.connect(qtLambda(tree._duplicateFromHandle, tHandle))
if self._item.itemClass == nwItemClass.TRASH or isRoot or (isFolder and not hasChild): if self._item.itemClass == nwItemClass.TRASH or isRoot or (isFolder and not hasChild):
action = self.addAction(self.tr("Delete Permanently")) action = self.addAction(self.tr("Delete Permanently"))
action.triggered.connect(lambda: tree.permDeleteItem(tHandle)) action.triggered.connect(qtLambda(tree.permDeleteItem, tHandle))
else: else:
action = self.addAction(self.tr("Move to Trash")) action = self.addAction(self.tr("Move to Trash"))
action.triggered.connect(lambda: tree.moveItemToTrash(tHandle)) action.triggered.connect(qtLambda(tree.moveItemToTrash, tHandle))
return return
+7 -6
View File
@@ -32,6 +32,7 @@ from PyQt5.QtGui import QPalette
from PyQt5.QtWidgets import QMenu, QVBoxLayout, QWidget from PyQt5.QtWidgets import QMenu, QVBoxLayout, QWidget
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import qtLambda
from novelwriter.enum import nwView from novelwriter.enum import nwView
from novelwriter.extensions.eventfilters import StatusTipFilter from novelwriter.extensions.eventfilters import StatusTipFilter
from novelwriter.extensions.modified import NIconToolButton from novelwriter.extensions.modified import NIconToolButton
@@ -54,7 +55,7 @@ class GuiSideBar(QWidget):
self.mainGui = mainGui self.mainGui = mainGui
iPx = int(1.2*SHARED.theme.baseButtonHeight) iPx = int(1.25*SHARED.theme.baseButtonHeight)
iSz = QSize(iPx, iPx) iSz = QSize(iPx, iPx)
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
@@ -63,19 +64,19 @@ class GuiSideBar(QWidget):
# Buttons # Buttons
self.tbProject = NIconToolButton(self, iSz) self.tbProject = NIconToolButton(self, iSz)
self.tbProject.setToolTip("{0} [Ctrl+T]".format(self.tr("Project Tree View"))) self.tbProject.setToolTip("{0} [Ctrl+T]".format(self.tr("Project Tree View")))
self.tbProject.clicked.connect(lambda: self.requestViewChange.emit(nwView.PROJECT)) self.tbProject.clicked.connect(qtLambda(self.requestViewChange.emit, nwView.PROJECT))
self.tbNovel = NIconToolButton(self, iSz) self.tbNovel = NIconToolButton(self, iSz)
self.tbNovel.setToolTip("{0} [Ctrl+T]".format(self.tr("Novel Tree View"))) self.tbNovel.setToolTip("{0} [Ctrl+T]".format(self.tr("Novel Tree View")))
self.tbNovel.clicked.connect(lambda: self.requestViewChange.emit(nwView.NOVEL)) self.tbNovel.clicked.connect(qtLambda(self.requestViewChange.emit, nwView.NOVEL))
self.tbSearch = NIconToolButton(self, iSz) self.tbSearch = NIconToolButton(self, iSz)
self.tbSearch.setToolTip("{0} [Ctrl+Shift+F]".format(self.tr("Project Search"))) self.tbSearch.setToolTip("{0} [Ctrl+Shift+F]".format(self.tr("Project Search")))
self.tbSearch.clicked.connect(lambda: self.requestViewChange.emit(nwView.SEARCH)) self.tbSearch.clicked.connect(qtLambda(self.requestViewChange.emit, nwView.SEARCH))
self.tbOutline = NIconToolButton(self, iSz) self.tbOutline = NIconToolButton(self, iSz)
self.tbOutline.setToolTip("{0} [Ctrl+Shift+T]".format(self.tr("Novel Outline View"))) self.tbOutline.setToolTip("{0} [Ctrl+Shift+T]".format(self.tr("Novel Outline View")))
self.tbOutline.clicked.connect(lambda: self.requestViewChange.emit(nwView.OUTLINE)) self.tbOutline.clicked.connect(qtLambda(self.requestViewChange.emit, nwView.OUTLINE))
self.tbBuild = NIconToolButton(self, iSz) self.tbBuild = NIconToolButton(self, iSz)
self.tbBuild.setToolTip("{0} [F5]".format(self.tr("Build Manuscript"))) self.tbBuild.setToolTip("{0} [F5]".format(self.tr("Build Manuscript")))
@@ -113,7 +114,7 @@ class GuiSideBar(QWidget):
self.outerBox.addWidget(self.tbStats) self.outerBox.addWidget(self.tbStats)
self.outerBox.addWidget(self.tbSettings) self.outerBox.addWidget(self.tbSettings)
self.outerBox.setContentsMargins(0, 0, 0, 0) self.outerBox.setContentsMargins(0, 0, 0, 0)
self.outerBox.setSpacing(CONFIG.pxInt(4)) self.outerBox.setSpacing(CONFIG.pxInt(6))
self.setLayout(self.outerBox) self.setLayout(self.outerBox)
self.updateTheme() self.updateTheme()
+19 -19
View File
@@ -37,7 +37,7 @@ from PyQt5.QtWidgets import (
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import describeFont from novelwriter.common import describeFont, qtLambda
from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwStyles, trConst from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwStyles, 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 (
@@ -329,16 +329,16 @@ class _FilterTab(NFixedPage):
self.includedButton = NIconToolButton(self, iSz) self.includedButton = NIconToolButton(self, iSz)
self.includedButton.setToolTip(self.tr("Always included")) self.includedButton.setToolTip(self.tr("Always included"))
self.includedButton.setIcon(self._statusFlags[self.F_INCLUDED]) self.includedButton.setIcon(self._statusFlags[self.F_INCLUDED])
self.includedButton.clicked.connect(lambda: self._setSelectedMode(self.F_INCLUDED)) self.includedButton.clicked.connect(qtLambda(self._setSelectedMode, self.F_INCLUDED))
self.excludedButton = NIconToolButton(self, iSz) self.excludedButton = NIconToolButton(self, iSz)
self.excludedButton.setToolTip(self.tr("Always excluded")) self.excludedButton.setToolTip(self.tr("Always excluded"))
self.excludedButton.setIcon(self._statusFlags[self.F_EXCLUDED]) self.excludedButton.setIcon(self._statusFlags[self.F_EXCLUDED])
self.excludedButton.clicked.connect(lambda: self._setSelectedMode(self.F_EXCLUDED)) self.excludedButton.clicked.connect(qtLambda(self._setSelectedMode, self.F_EXCLUDED))
self.resetButton = NIconToolButton(self, iSz, "revert") self.resetButton = NIconToolButton(self, iSz, "revert")
self.resetButton.setToolTip(self.tr("Reset to default")) self.resetButton.setToolTip(self.tr("Reset to default"))
self.resetButton.clicked.connect(lambda: self._setSelectedMode(self.F_FILTERED)) self.resetButton.clicked.connect(qtLambda(self._setSelectedMode, self.F_FILTERED))
self.modeBox = QHBoxLayout() self.modeBox = QHBoxLayout()
self.modeBox.addWidget(QLabel(self.tr("Mark selection as"), self)) self.modeBox.addWidget(QLabel(self.tr("Mark selection as"), self))
@@ -589,7 +589,7 @@ class _HeadingsTab(NScrollablePage):
self.fmtPart = QLineEdit("", self) self.fmtPart = QLineEdit("", self)
self.fmtPart.setReadOnly(True) self.fmtPart.setReadOnly(True)
self.btnPart = NIconToolButton(self, iSz, "edit") self.btnPart = NIconToolButton(self, iSz, "edit")
self.btnPart.clicked.connect(lambda: self._editHeading(self.EDIT_TITLE)) self.btnPart.clicked.connect(qtLambda(self._editHeading, self.EDIT_TITLE))
self.hdePart = QLabel(trHide, self) self.hdePart = QLabel(trHide, self)
self.hdePart.setIndent(bSp) self.hdePart.setIndent(bSp)
self.swtPart = NSwitch(self, height=iPx) self.swtPart = NSwitch(self, height=iPx)
@@ -605,7 +605,7 @@ class _HeadingsTab(NScrollablePage):
self.fmtChapter = QLineEdit("", self) self.fmtChapter = QLineEdit("", self)
self.fmtChapter.setReadOnly(True) self.fmtChapter.setReadOnly(True)
self.btnChapter = NIconToolButton(self, iSz, "edit") self.btnChapter = NIconToolButton(self, iSz, "edit")
self.btnChapter.clicked.connect(lambda: self._editHeading(self.EDIT_CHAPTER)) self.btnChapter.clicked.connect(qtLambda(self._editHeading, self.EDIT_CHAPTER))
self.hdeChapter = QLabel(trHide, self) self.hdeChapter = QLabel(trHide, self)
self.hdeChapter.setIndent(bSp) self.hdeChapter.setIndent(bSp)
self.swtChapter = NSwitch(self, height=iPx) self.swtChapter = NSwitch(self, height=iPx)
@@ -621,7 +621,7 @@ class _HeadingsTab(NScrollablePage):
self.fmtUnnumbered = QLineEdit("", self) self.fmtUnnumbered = QLineEdit("", self)
self.fmtUnnumbered.setReadOnly(True) self.fmtUnnumbered.setReadOnly(True)
self.btnUnnumbered = NIconToolButton(self, iSz, "edit") self.btnUnnumbered = NIconToolButton(self, iSz, "edit")
self.btnUnnumbered.clicked.connect(lambda: self._editHeading(self.EDIT_UNNUM)) self.btnUnnumbered.clicked.connect(qtLambda(self._editHeading, self.EDIT_UNNUM))
self.hdeUnnumbered = QLabel(trHide, self) self.hdeUnnumbered = QLabel(trHide, self)
self.hdeUnnumbered.setIndent(bSp) self.hdeUnnumbered.setIndent(bSp)
self.swtUnnumbered = NSwitch(self, height=iPx) self.swtUnnumbered = NSwitch(self, height=iPx)
@@ -637,7 +637,7 @@ class _HeadingsTab(NScrollablePage):
self.fmtScene = QLineEdit("", self) self.fmtScene = QLineEdit("", self)
self.fmtScene.setReadOnly(True) self.fmtScene.setReadOnly(True)
self.btnScene = NIconToolButton(self, iSz, "edit") self.btnScene = NIconToolButton(self, iSz, "edit")
self.btnScene.clicked.connect(lambda: self._editHeading(self.EDIT_SCENE)) self.btnScene.clicked.connect(qtLambda(self._editHeading, self.EDIT_SCENE))
self.hdeScene = QLabel(trHide, self) self.hdeScene = QLabel(trHide, self)
self.hdeScene.setIndent(bSp) self.hdeScene.setIndent(bSp)
self.swtScene = NSwitch(self, height=iPx) self.swtScene = NSwitch(self, height=iPx)
@@ -653,7 +653,7 @@ class _HeadingsTab(NScrollablePage):
self.fmtAScene = QLineEdit("", self) self.fmtAScene = QLineEdit("", self)
self.fmtAScene.setReadOnly(True) self.fmtAScene.setReadOnly(True)
self.btnAScene = NIconToolButton(self, iSz, "edit") self.btnAScene = NIconToolButton(self, iSz, "edit")
self.btnAScene.clicked.connect(lambda: self._editHeading(self.EDIT_HSCENE)) self.btnAScene.clicked.connect(qtLambda(self._editHeading, self.EDIT_HSCENE))
self.hdeAScene = QLabel(trHide, self) self.hdeAScene = QLabel(trHide, self)
self.hdeAScene.setIndent(bSp) self.hdeAScene.setIndent(bSp)
self.swtAScene = NSwitch(self, height=iPx) self.swtAScene = NSwitch(self, height=iPx)
@@ -669,7 +669,7 @@ class _HeadingsTab(NScrollablePage):
self.fmtSection = QLineEdit("", self) self.fmtSection = QLineEdit("", self)
self.fmtSection.setReadOnly(True) self.fmtSection.setReadOnly(True)
self.btnSection = NIconToolButton(self, iSz, "edit") self.btnSection = NIconToolButton(self, iSz, "edit")
self.btnSection.clicked.connect(lambda: self._editHeading(self.EDIT_SECTION)) self.btnSection.clicked.connect(qtLambda(self._editHeading, self.EDIT_SECTION))
self.hdeSection = QLabel(trHide, self) self.hdeSection = QLabel(trHide, self)
self.hdeSection.setIndent(bSp) self.hdeSection.setIndent(bSp)
self.swtSection = NSwitch(self, height=iPx) self.swtSection = NSwitch(self, height=iPx)
@@ -702,15 +702,15 @@ class _HeadingsTab(NScrollablePage):
self.aInsCharPOV = self.menuInsert.addAction(self.tr("Point of View Character")) self.aInsCharPOV = self.menuInsert.addAction(self.tr("Point of View Character"))
self.aInsCharFocus = self.menuInsert.addAction(self.tr("Focus Character")) self.aInsCharFocus = self.menuInsert.addAction(self.tr("Focus Character"))
self.aInsTitle.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.TITLE)) self.aInsTitle.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.TITLE))
self.aInsChNum.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CH_NUM)) self.aInsChNum.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.CH_NUM))
self.aInsChWord.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CH_WORD)) self.aInsChWord.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.CH_WORD))
self.aInsChRomU.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CH_ROMU)) self.aInsChRomU.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.CH_ROMU))
self.aInsChRomL.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CH_ROML)) self.aInsChRomL.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.CH_ROML))
self.aInsScNum.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.SC_NUM)) self.aInsScNum.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.SC_NUM))
self.aInsScAbs.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.SC_ABS)) self.aInsScAbs.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.SC_ABS))
self.aInsCharPOV.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CHAR_POV)) self.aInsCharPOV.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.CHAR_POV))
self.aInsCharFocus.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CHAR_FOCUS)) self.aInsCharFocus.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.CHAR_FOCUS))
self.btnInsert = QPushButton(self.tr("Insert"), self) self.btnInsert = QPushButton(self.tr("Insert"), self)
self.btnInsert.setMenu(self.menuInsert) self.btnInsert.setMenu(self.menuInsert)
+3 -3
View File
@@ -38,7 +38,7 @@ from PyQt5.QtWidgets import (
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import checkInt, checkIntTuple, formatTime, minmax from novelwriter.common import checkInt, checkIntTuple, formatTime, minmax, qtLambda
from novelwriter.constants import nwConst from novelwriter.constants import nwConst
from novelwriter.error import formatException from novelwriter.error import formatException
from novelwriter.extensions.modified import NToolDialog from novelwriter.extensions.modified import NToolDialog
@@ -277,11 +277,11 @@ class GuiWritingStats(NToolDialog):
self.btnSave.setMenu(self.saveMenu) self.btnSave.setMenu(self.saveMenu)
self.saveJSON = QAction(self.tr("JSON Data File (.json)"), self) self.saveJSON = QAction(self.tr("JSON Data File (.json)"), self)
self.saveJSON.triggered.connect(lambda: self._saveData(self.FMT_JSON)) self.saveJSON.triggered.connect(qtLambda(self._saveData, self.FMT_JSON))
self.saveMenu.addAction(self.saveJSON) self.saveMenu.addAction(self.saveJSON)
self.saveCSV = QAction(self.tr("CSV Data File (.csv)"), self) self.saveCSV = QAction(self.tr("CSV Data File (.csv)"), self)
self.saveCSV.triggered.connect(lambda: self._saveData(self.FMT_CSV)) self.saveCSV.triggered.connect(qtLambda(self._saveData, self.FMT_CSV))
self.saveMenu.addAction(self.saveCSV) self.saveMenu.addAction(self.saveCSV)
# Assemble # Assemble
+1 -1
View File
@@ -134,7 +134,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
assert docViewer.docAction(nwDocAction.COPY) is False assert docViewer.docAction(nwDocAction.COPY) is False
# Open again via menu # Open again via menu
assert nwGUI.projView.projTree.setSelectedHandle("88243afbe5ed8") nwGUI.projView.projTree.setSelectedHandle("88243afbe5ed8")
nwGUI.mainMenu.aViewDoc.activate(QAction.ActionEvent.Trigger) nwGUI.mainMenu.aViewDoc.activate(QAction.ActionEvent.Trigger)
# Open context menu # Open context menu
+2 -1
View File
@@ -29,6 +29,7 @@ import pytest
from PyQt5.QtWidgets import QAction, QFileDialog, QWidget from PyQt5.QtWidgets import QAction, QFileDialog, QWidget
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.constants import nwKeyWords
from novelwriter.enum import nwItemClass, nwOutline, nwView from novelwriter.enum import nwItemClass, nwOutline, nwView
from novelwriter.types import QtScrollAlwaysOff, QtScrollAsNeeded from novelwriter.types import QtScrollAlwaysOff, QtScrollAsNeeded
@@ -261,7 +262,7 @@ def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, prjLipsum, fncPath, tstPat
assert outlineData.itemValue.text() == "Finished" assert outlineData.itemValue.text() == "Finished"
# Click POV Link # Click POV Link
assert outlineData.povKeyValue.text() == "<a href='Bod'>Bod</a>" assert outlineData.tagValues[nwKeyWords.POV_KEY][1].text() == "<a href='Bod'>Bod</a>"
outlineView._tagClicked("Bod") outlineView._tagClicked("Bod")
assert nwGUI.docViewer.docHandle == "4c4f28287af27" assert nwGUI.docViewer.docHandle == "4c4f28287af27"
+16 -12
View File
@@ -21,6 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from unittest.mock import MagicMock
import pytest import pytest
@@ -107,7 +108,6 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn
assert project.tree["0000000000013"].itemClass == nwItemClass.NOVEL # type: ignore assert project.tree["0000000000013"].itemClass == nwItemClass.NOVEL # type: ignore
assert nwGUI.openDocument("0000000000013") assert nwGUI.openDocument("0000000000013")
assert nwGUI.docEditor.getText() == "## New Chapter\n\n" assert nwGUI.docEditor.getText() == "## New Chapter\n\n"
assert projTree._getItemWordCount("0000000000013") == 2
# Add a new scene next to the other new file # Add a new scene next to the other new file
projView.setSelectedHandle("0000000000012") projView.setSelectedHandle("0000000000012")
@@ -117,7 +117,6 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn
assert project.tree["0000000000014"].itemClass == nwItemClass.NOVEL # type: ignore assert project.tree["0000000000014"].itemClass == nwItemClass.NOVEL # type: ignore
assert nwGUI.openDocument("0000000000014") assert nwGUI.openDocument("0000000000014")
assert nwGUI.docEditor.getText() == "### New Scene\n\n" assert nwGUI.docEditor.getText() == "### New Scene\n\n"
assert projTree._getItemWordCount("0000000000014") == 2
# Add a new scene with the content copied from the previous # Add a new scene with the content copied from the previous
assert nwGUI.openDocument("0000000000014") assert nwGUI.openDocument("0000000000014")
@@ -130,7 +129,6 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn
assert project.tree["0000000000015"].itemClass == nwItemClass.NOVEL # type: ignore assert project.tree["0000000000015"].itemClass == nwItemClass.NOVEL # type: ignore
assert nwGUI.openDocument("0000000000015") assert nwGUI.openDocument("0000000000015")
assert nwGUI.docEditor.getText() == "### New Scene\n\nWith Stuff\n\n" assert nwGUI.docEditor.getText() == "### New Scene\n\nWith Stuff\n\n"
assert projTree._getItemWordCount("0000000000015") == 4
# Add a new file to the characters folder # Add a new file to the characters folder
projView.setSelectedHandle(C.hCharRoot) projView.setSelectedHandle(C.hCharRoot)
@@ -140,7 +138,6 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn
assert project.tree["0000000000016"].itemClass == nwItemClass.CHARACTER # type: ignore assert project.tree["0000000000016"].itemClass == nwItemClass.CHARACTER # type: ignore
assert nwGUI.openDocument("0000000000016") assert nwGUI.openDocument("0000000000016")
assert nwGUI.docEditor.getText() == "# New Note\n\n" assert nwGUI.docEditor.getText() == "# New Note\n\n"
assert projTree._getItemWordCount("0000000000016") == 2
# Make sure the sibling folder bug trap works # Make sure the sibling folder bug trap works
projView.setSelectedHandle("0000000000013") projView.setSelectedHandle("0000000000013")
@@ -1146,30 +1143,37 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
def itemPos(tHandle): def itemPos(tHandle):
return projTree.visualItemRect(projTree._getTreeItem(tHandle)).center() return projTree.visualItemRect(projTree._getTreeItem(tHandle)).center()
# Pop the menu # Pop the menu in various positions and check for success
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(QMenu, "exec", lambda *a: None) mockMenu = MagicMock()
mp.setattr(QMenu, "exec", mockMenu)
projTree.clearSelection() projTree.clearSelection()
# No item under menu # No item under menu
assert projTree._openContextMenu(projTree.viewport().rect().bottomRight()) is False projTree.openContextMenu(projTree.viewport().rect().bottomRight())
assert mockMenu.call_count == 0
# Open Trash Menu # Open Trash Menu
assert projTree._openContextMenu(itemPos(hTrashRoot)) is True projTree.openContextMenu(itemPos(hTrashRoot))
assert mockMenu.call_count == 1
# Open Single Select Menu # Open Single Select Menu
assert projTree._openContextMenu(itemPos(C.hNovelRoot)) is True projTree.openContextMenu(itemPos(C.hNovelRoot))
assert mockMenu.call_count == 2
# Open Multi-Select Menu # Open Multi-Select Menu
projTree._getTreeItem(hNovelNote).setSelected(True) projTree._getTreeItem(hNovelNote).setSelected(True)
projTree._getTreeItem(hSubNote).setSelected(True) projTree._getTreeItem(hSubNote).setSelected(True)
assert projTree._openContextMenu(itemPos(hCharNote)) is True projTree.openContextMenu(itemPos(hCharNote))
assert mockMenu.call_count == 3
# Check the keyboard shortcut handler as well # Check the keyboard shortcut handler as well
projTree.setSelectedHandle(C.hNovelRoot) projTree.setSelectedHandle(C.hNovelRoot)
assert projTree.openContextOnSelected() is True projTree.openContextMenu(None)
assert mockMenu.call_count == 4
projTree.clearSelection() projTree.clearSelection()
assert projTree.openContextOnSelected() is False projTree.openContextMenu(None)
assert mockMenu.call_count == 4
# Menu Builders # Menu Builders
# ============= # =============