Refactor and clean up GUI code (#2078)
This commit is contained in:
@@ -436,7 +436,7 @@ def describeFont(font: QFont) -> str:
|
||||
|
||||
def qtLambda(func: Callable, *args: Any, **kwargs: Any) -> Callable:
|
||||
"""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)
|
||||
return wrapper
|
||||
|
||||
|
||||
@@ -73,6 +73,9 @@ VALID_MAP: dict[str, set[str]] = {
|
||||
"widthCol3", "widthCol4", "wordsPerPage", "countFrom", "clearDouble",
|
||||
"novelRoot",
|
||||
},
|
||||
"GuiOutlineDetails": {
|
||||
"detailsWidth", "tagsWidth",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ from PyQt5.QtWidgets import (
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.common import simplified
|
||||
from novelwriter.common import qtLambda, simplified
|
||||
from novelwriter.constants import nwLabels, trConst
|
||||
from novelwriter.core.status import NWStatus, StatusEntry
|
||||
from novelwriter.enum import nwStatusShape
|
||||
@@ -365,10 +365,10 @@ class _StatusPage(NFixedPage):
|
||||
self.delButton.clicked.connect(self._delItem)
|
||||
|
||||
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.clicked.connect(lambda: self._moveItem(1))
|
||||
self.dnButton.clicked.connect(qtLambda(self._moveItem, 1))
|
||||
|
||||
# Edit Form
|
||||
self.editName = QLineEdit(self)
|
||||
|
||||
@@ -38,6 +38,7 @@ from PyQt5.QtWidgets import (
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.common import qtLambda
|
||||
from novelwriter.constants import nwStyles, nwUnicode
|
||||
from novelwriter.enum import nwDocAction, nwDocMode, nwItemType
|
||||
from novelwriter.error import logException
|
||||
@@ -396,25 +397,25 @@ class GuiDocViewer(QTextBrowser):
|
||||
|
||||
if userSelection:
|
||||
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.addSeparator()
|
||||
|
||||
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)
|
||||
|
||||
mnuSelWord = QAction(self.tr("Select Word"), ctxMenu)
|
||||
mnuSelWord.triggered.connect(
|
||||
lambda: self._makePosSelection(QTextCursor.SelectionType.WordUnderCursor, point)
|
||||
)
|
||||
mnuSelWord.triggered.connect(qtLambda(
|
||||
self._makePosSelection, QTextCursor.SelectionType.WordUnderCursor, point
|
||||
))
|
||||
ctxMenu.addAction(mnuSelWord)
|
||||
|
||||
mnuSelPara = QAction(self.tr("Select Paragraph"), ctxMenu)
|
||||
mnuSelPara.triggered.connect(
|
||||
lambda: self._makePosSelection(QTextCursor.SelectionType.BlockUnderCursor, point)
|
||||
)
|
||||
mnuSelPara.triggered.connect(qtLambda(
|
||||
self._makePosSelection, QTextCursor.SelectionType.BlockUnderCursor, point
|
||||
))
|
||||
ctxMenu.addAction(mnuSelPara)
|
||||
|
||||
# Open the context menu
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ from PyQt5.QtWidgets import (
|
||||
)
|
||||
|
||||
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.core.index import IndexHeading
|
||||
from novelwriter.enum import nwDocMode, nwItemClass, nwOutline
|
||||
@@ -346,7 +346,7 @@ class GuiNovelToolBar(QWidget):
|
||||
aLast = self.mLastCol.addAction(actionLabel)
|
||||
aLast.setCheckable(True)
|
||||
aLast.setActionGroup(self.gLastCol)
|
||||
aLast.triggered.connect(lambda: self.setLastColType(colType))
|
||||
aLast.triggered.connect(qtLambda(self.setLastColType, colType))
|
||||
self.aLastCol[colType] = aLast
|
||||
return
|
||||
|
||||
@@ -484,16 +484,9 @@ class GuiNovelTree(QTreeWidget):
|
||||
if rootHandle is None:
|
||||
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
|
||||
if selItem:
|
||||
titleKey = selItem[0].data(self.C_DATA, self.D_KEY)
|
||||
if selItems := self.selectedItems():
|
||||
titleKey = selItems[0].data(self.C_DATA, self.D_KEY)
|
||||
|
||||
self._populateTree(rootHandle)
|
||||
SHARED.project.data.setLastHandle(rootHandle, "novelTree")
|
||||
|
||||
+73
-170
@@ -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,27 +936,42 @@ 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"))
|
||||
self.titleValue.setText("")
|
||||
self.fileValue.setText("")
|
||||
self.itemValue.setText("")
|
||||
self.cCValue.setText("")
|
||||
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("")
|
||||
self.titleValue.clear()
|
||||
self.fileValue.clear()
|
||||
self.itemValue.clear()
|
||||
self.cCValue.clear()
|
||||
self.wCValue.clear()
|
||||
self.pCValue.clear()
|
||||
self.synopValue.clear()
|
||||
|
||||
for _, value in self.tagValues.values():
|
||||
value.clear()
|
||||
|
||||
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
|
||||
|
||||
+115
-124
@@ -29,7 +29,6 @@ from __future__ import annotations
|
||||
import logging
|
||||
|
||||
from enum import Enum
|
||||
from time import time
|
||||
|
||||
from PyQt5.QtCore import QPoint, Qt, QTimer, pyqtSignal, pyqtSlot
|
||||
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.common import minmax
|
||||
from novelwriter.common import minmax, qtLambda
|
||||
from novelwriter.constants import nwLabels, nwStyles, nwUnicode, trConst
|
||||
from novelwriter.core.coretools import DocDuplicator, DocMerger, DocSplitter
|
||||
from novelwriter.core.item import NWItem
|
||||
@@ -97,37 +96,37 @@ class GuiProjectView(QWidget):
|
||||
self.keyMoveUp = QShortcut(self.projTree)
|
||||
self.keyMoveUp.setKey("Ctrl+Up")
|
||||
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.setKey("Ctrl+Down")
|
||||
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.setKey("Alt+Up")
|
||||
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.setKey("Alt+Down")
|
||||
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.setKey("Alt+Left")
|
||||
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.setKey("Alt+Right")
|
||||
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.setKey("Ctrl+.")
|
||||
self.keyContext.setContext(Qt.ShortcutContext.WidgetShortcut)
|
||||
self.keyContext.activated.connect(lambda: self.projTree.openContextOnSelected())
|
||||
self.keyContext.activated.connect(self.projTree.openContextMenu)
|
||||
|
||||
# Signals
|
||||
self.selectedItemChanged.connect(self.projBar.treeSelectionChanged)
|
||||
@@ -138,7 +137,6 @@ class GuiProjectView(QWidget):
|
||||
self.emptyTrash = self.projTree.emptyTrash
|
||||
self.requestDeleteItem = self.projTree.requestDeleteItem
|
||||
self.getSelectedHandle = self.projTree.getSelectedHandle
|
||||
self.changedSince = self.projTree.changedSince
|
||||
|
||||
return
|
||||
|
||||
@@ -292,38 +290,38 @@ class GuiProjectToolBar(QWidget):
|
||||
# Move Buttons
|
||||
self.tbMoveU = NIconToolButton(self, iSz)
|
||||
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.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
|
||||
self.mAdd = QMenu(self)
|
||||
|
||||
self.aAddEmpty = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["document"]))
|
||||
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.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.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.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.triggered.connect(
|
||||
lambda: self.projTree.newTreeItem(nwItemType.FOLDER)
|
||||
qtLambda(self.projTree.newTreeItem, nwItemType.FOLDER)
|
||||
)
|
||||
|
||||
self.mTemplates = _UpdatableMenu(self.mAdd)
|
||||
@@ -343,13 +341,17 @@ class GuiProjectToolBar(QWidget):
|
||||
self.mMore = QMenu(self)
|
||||
|
||||
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.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.triggered.connect(lambda: self.projTree.emptyTrash())
|
||||
self.aEmptyTrash.triggered.connect(qtLambda(self.projTree.emptyTrash))
|
||||
|
||||
self.tbMore = NIconToolButton(self, iSz)
|
||||
self.tbMore.setToolTip(self.tr("More Options"))
|
||||
@@ -422,7 +424,7 @@ class GuiProjectToolBar(QWidget):
|
||||
action.setData(tHandle)
|
||||
action.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass]))
|
||||
action.triggered.connect(
|
||||
lambda _, tHandle=tHandle: self.projView.setSelectedHandle(tHandle, doScroll=True)
|
||||
qtLambda(self.projView.setSelectedHandle, tHandle, doScroll=True)
|
||||
)
|
||||
return
|
||||
|
||||
@@ -461,7 +463,9 @@ class GuiProjectToolBar(QWidget):
|
||||
def addClass(itemClass: nwItemClass) -> None:
|
||||
aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[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)
|
||||
return
|
||||
|
||||
@@ -504,7 +508,6 @@ class GuiProjectTree(QTreeWidget):
|
||||
|
||||
# Internal Variables
|
||||
self._treeMap: dict[str, QTreeWidgetItem] = {}
|
||||
self._timeChanged = 0.0
|
||||
self._popAlert = None
|
||||
self._actHandle = None
|
||||
|
||||
@@ -518,7 +521,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
|
||||
# Context Menu
|
||||
self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
||||
self.customContextMenuRequested.connect(self._openContextMenu)
|
||||
self.customContextMenuRequested.connect(self.openContextMenu)
|
||||
|
||||
# Tree Settings
|
||||
iPx = SHARED.theme.baseIconHeight
|
||||
@@ -602,7 +605,6 @@ class GuiProjectTree(QTreeWidget):
|
||||
"""Clear the GUI content and the related map."""
|
||||
self.clear()
|
||||
self._treeMap = {}
|
||||
self._timeChanged = 0.0
|
||||
return
|
||||
|
||||
def createNewNote(self, tag: str, itemClass: nwItemClass) -> None:
|
||||
@@ -1121,12 +1123,10 @@ class GuiProjectTree(QTreeWidget):
|
||||
"""
|
||||
logger.debug("Building the project tree ...")
|
||||
self.clearTree()
|
||||
count = 0
|
||||
for nwItem in SHARED.project.iterProjectItems():
|
||||
count += 1
|
||||
self._addTreeItem(nwItem)
|
||||
if count > 0:
|
||||
logger.info("%d item(s) added to the project tree", count)
|
||||
self.setActiveHandle(self._actHandle)
|
||||
logger.info("%d item(s) added to the project tree", len(self._treeMap))
|
||||
return
|
||||
|
||||
def getSelectedHandle(self) -> str | None:
|
||||
@@ -1137,19 +1137,13 @@ class GuiProjectTree(QTreeWidget):
|
||||
return items[0].data(self.C_DATA, self.D_HANDLE)
|
||||
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."""
|
||||
tItem = self._getTreeItem(tHandle)
|
||||
if tItem is None:
|
||||
return False
|
||||
|
||||
if tHandle in self._treeMap:
|
||||
self.setCurrentItem(self._treeMap[tHandle])
|
||||
|
||||
if (indexes := self.selectedIndexes()) and doScroll:
|
||||
self.scrollTo(indexes[0], QAbstractItemView.ScrollHint.PositionAtCenter)
|
||||
|
||||
return True
|
||||
if (indexes := self.selectedIndexes()) and doScroll:
|
||||
self.scrollTo(indexes[0], QAbstractItemView.ScrollHint.PositionAtCenter)
|
||||
return
|
||||
|
||||
def setActiveHandle(self, tHandle: str | None) -> None:
|
||||
"""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
|
||||
entire tree.
|
||||
"""
|
||||
trItem = self._getTreeItem(tHandle) or self.invisibleRootItem()
|
||||
self._recursiveSetExpanded(trItem, isExpanded)
|
||||
item = self._getTreeItem(tHandle) or self.invisibleRootItem()
|
||||
self._recursiveSetExpanded(item, isExpanded)
|
||||
return
|
||||
|
||||
def openContextOnSelected(self) -> bool:
|
||||
"""Open the context menu on the current selected item."""
|
||||
if items := self.selectedItems():
|
||||
return self._openContextMenu(self.visualItemRect(items[0]).center())
|
||||
return False
|
||||
##
|
||||
# Public Slots
|
||||
##
|
||||
|
||||
def changedSince(self, checkTime: float) -> bool:
|
||||
"""Check if the tree has changed since a given time."""
|
||||
return self._timeChanged > checkTime
|
||||
@pyqtSlot()
|
||||
@pyqtSlot("QPoint")
|
||||
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
|
||||
@@ -1224,40 +1251,6 @@ class GuiProjectTree(QTreeWidget):
|
||||
|
||||
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()
|
||||
def _doAutoScroll(self) -> None:
|
||||
"""Scroll one item up or down based on direction value."""
|
||||
@@ -1397,11 +1390,6 @@ class GuiProjectTree(QTreeWidget):
|
||||
|
||||
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:
|
||||
"""Return the QTreeWidgetItem of a given item handle."""
|
||||
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
|
||||
deleted.
|
||||
"""
|
||||
self._timeChanged = time()
|
||||
SHARED.project.setProjectChanged(True)
|
||||
if flush:
|
||||
self.saveTreeOrder()
|
||||
@@ -1785,7 +1772,7 @@ class _TreeContextMenu(QMenu):
|
||||
|
||||
# Edit Item Settings
|
||||
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:
|
||||
self._itemHeader()
|
||||
self._itemActive(False)
|
||||
@@ -1821,13 +1808,15 @@ class _TreeContextMenu(QMenu):
|
||||
def _docActions(self) -> None:
|
||||
"""Add document actions."""
|
||||
action = self.addAction(self.tr("Open Document"))
|
||||
action.triggered.connect(
|
||||
lambda: self.projView.openDocumentRequest.emit(self._handle, nwDocMode.EDIT, "", True)
|
||||
)
|
||||
action.triggered.connect(qtLambda(
|
||||
self.projView.openDocumentRequest.emit,
|
||||
self._handle, nwDocMode.EDIT, "", True
|
||||
))
|
||||
action = self.addAction(self.tr("View Document"))
|
||||
action.triggered.connect(
|
||||
lambda: self.projView.openDocumentRequest.emit(self._handle, nwDocMode.VIEW, "", False)
|
||||
)
|
||||
action.triggered.connect(qtLambda(
|
||||
self.projView.openDocumentRequest.emit,
|
||||
self._handle, nwDocMode.VIEW, "", False
|
||||
))
|
||||
return
|
||||
|
||||
def _itemCreation(self) -> None:
|
||||
@@ -1846,7 +1835,7 @@ class _TreeContextMenu(QMenu):
|
||||
if hItem := SHARED.project.index.getItemHeading(self._handle, "T0001"):
|
||||
action = self.addAction(self.tr("Rename to Heading"))
|
||||
action.triggered.connect(
|
||||
lambda: self.projTree.renameTreeItem(self._handle, hItem.title)
|
||||
qtLambda(self.projTree.renameTreeItem, self._handle, hItem.title)
|
||||
)
|
||||
return
|
||||
|
||||
@@ -1855,9 +1844,9 @@ class _TreeContextMenu(QMenu):
|
||||
if multi:
|
||||
mSub = self.addMenu(self.tr("Set Active to ..."))
|
||||
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.triggered.connect(lambda: self._iterItemActive(False))
|
||||
aTwo.triggered.connect(qtLambda(self._iterItemActive, False))
|
||||
else:
|
||||
action = self.addAction(self.tr("Toggle Active"))
|
||||
action.triggered.connect(self._toggleItemActive)
|
||||
@@ -1868,37 +1857,39 @@ class _TreeContextMenu(QMenu):
|
||||
if self._item.isNovelLike():
|
||||
menu = self.addMenu(self.tr("Set Status to ..."))
|
||||
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
|
||||
if not multi and current == key:
|
||||
name += f" ({nwUnicode.U_CHECK})"
|
||||
action = menu.addAction(entry.icon, name)
|
||||
if multi:
|
||||
action.triggered.connect(lambda n, key=key: self._iterSetItemStatus(key))
|
||||
action.triggered.connect(qtLambda(self._iterSetItemStatus, key))
|
||||
else:
|
||||
action.triggered.connect(lambda n, key=key: self._changeItemStatus(key))
|
||||
action.triggered.connect(qtLambda(self._changeItemStatus, key))
|
||||
menu.addSeparator()
|
||||
action = menu.addAction(self.tr("Manage Labels ..."))
|
||||
action.triggered.connect(
|
||||
lambda: self.projView.projectSettingsRequest.emit(GuiProjectSettings.PAGE_STATUS)
|
||||
)
|
||||
action.triggered.connect(qtLambda(
|
||||
self.projView.projectSettingsRequest.emit,
|
||||
GuiProjectSettings.PAGE_STATUS
|
||||
))
|
||||
else:
|
||||
menu = self.addMenu(self.tr("Set Importance to ..."))
|
||||
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
|
||||
if not multi and current == key:
|
||||
name += f" ({nwUnicode.U_CHECK})"
|
||||
action = menu.addAction(entry.icon, name)
|
||||
if multi:
|
||||
action.triggered.connect(lambda n, key=key: self._iterSetItemImport(key))
|
||||
action.triggered.connect(qtLambda(self._iterSetItemImport, key))
|
||||
else:
|
||||
action.triggered.connect(lambda n, key=key: self._changeItemImport(key))
|
||||
action.triggered.connect(qtLambda(self._changeItemImport, key))
|
||||
menu.addSeparator()
|
||||
action = menu.addAction(self.tr("Manage Labels ..."))
|
||||
action.triggered.connect(
|
||||
lambda: self.projView.projectSettingsRequest.emit(GuiProjectSettings.PAGE_IMPORT)
|
||||
)
|
||||
action.triggered.connect(qtLambda(
|
||||
self.projView.projectSettingsRequest.emit,
|
||||
GuiProjectSettings.PAGE_IMPORT
|
||||
))
|
||||
return
|
||||
|
||||
def _itemTransform(self, isFile: bool, isFolder: bool, hasChild: bool) -> None:
|
||||
@@ -1917,33 +1908,33 @@ class _TreeContextMenu(QMenu):
|
||||
|
||||
if isNoteFile and self._item.documentAllowed():
|
||||
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:
|
||||
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():
|
||||
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:
|
||||
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:
|
||||
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.triggered.connect(lambda: tree._mergeDocuments(tHandle, True))
|
||||
action.triggered.connect(qtLambda(tree._mergeDocuments, tHandle, True))
|
||||
|
||||
if hasChild and isFolder:
|
||||
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:
|
||||
action = menu.addAction(self.tr("Split Document by Headings"))
|
||||
action.triggered.connect(lambda: tree._splitDocument(tHandle))
|
||||
action.triggered.connect(qtLambda(tree._splitDocument, tHandle))
|
||||
|
||||
return
|
||||
|
||||
@@ -1953,19 +1944,19 @@ class _TreeContextMenu(QMenu):
|
||||
tHandle = self._handle
|
||||
if hasChild:
|
||||
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.triggered.connect(lambda: tree.setExpandedFromHandle(tHandle, False))
|
||||
action.triggered.connect(qtLambda(tree.setExpandedFromHandle, tHandle, False))
|
||||
|
||||
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):
|
||||
action = self.addAction(self.tr("Delete Permanently"))
|
||||
action.triggered.connect(lambda: tree.permDeleteItem(tHandle))
|
||||
action.triggered.connect(qtLambda(tree.permDeleteItem, tHandle))
|
||||
else:
|
||||
action = self.addAction(self.tr("Move to Trash"))
|
||||
action.triggered.connect(lambda: tree.moveItemToTrash(tHandle))
|
||||
action.triggered.connect(qtLambda(tree.moveItemToTrash, tHandle))
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ from PyQt5.QtGui import QPalette
|
||||
from PyQt5.QtWidgets import QMenu, QVBoxLayout, QWidget
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.common import qtLambda
|
||||
from novelwriter.enum import nwView
|
||||
from novelwriter.extensions.eventfilters import StatusTipFilter
|
||||
from novelwriter.extensions.modified import NIconToolButton
|
||||
@@ -54,7 +55,7 @@ class GuiSideBar(QWidget):
|
||||
|
||||
self.mainGui = mainGui
|
||||
|
||||
iPx = int(1.2*SHARED.theme.baseButtonHeight)
|
||||
iPx = int(1.25*SHARED.theme.baseButtonHeight)
|
||||
iSz = QSize(iPx, iPx)
|
||||
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
@@ -63,19 +64,19 @@ class GuiSideBar(QWidget):
|
||||
# Buttons
|
||||
self.tbProject = NIconToolButton(self, iSz)
|
||||
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.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.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.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.setToolTip("{0} [F5]".format(self.tr("Build Manuscript")))
|
||||
@@ -113,7 +114,7 @@ class GuiSideBar(QWidget):
|
||||
self.outerBox.addWidget(self.tbStats)
|
||||
self.outerBox.addWidget(self.tbSettings)
|
||||
self.outerBox.setContentsMargins(0, 0, 0, 0)
|
||||
self.outerBox.setSpacing(CONFIG.pxInt(4))
|
||||
self.outerBox.setSpacing(CONFIG.pxInt(6))
|
||||
|
||||
self.setLayout(self.outerBox)
|
||||
self.updateTheme()
|
||||
|
||||
@@ -37,7 +37,7 @@ from PyQt5.QtWidgets import (
|
||||
)
|
||||
|
||||
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.core.buildsettings import BuildSettings, FilterMode
|
||||
from novelwriter.extensions.configlayout import (
|
||||
@@ -329,16 +329,16 @@ class _FilterTab(NFixedPage):
|
||||
self.includedButton = NIconToolButton(self, iSz)
|
||||
self.includedButton.setToolTip(self.tr("Always 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.setToolTip(self.tr("Always 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.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.addWidget(QLabel(self.tr("Mark selection as"), self))
|
||||
@@ -589,7 +589,7 @@ class _HeadingsTab(NScrollablePage):
|
||||
self.fmtPart = QLineEdit("", self)
|
||||
self.fmtPart.setReadOnly(True)
|
||||
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.setIndent(bSp)
|
||||
self.swtPart = NSwitch(self, height=iPx)
|
||||
@@ -605,7 +605,7 @@ class _HeadingsTab(NScrollablePage):
|
||||
self.fmtChapter = QLineEdit("", self)
|
||||
self.fmtChapter.setReadOnly(True)
|
||||
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.setIndent(bSp)
|
||||
self.swtChapter = NSwitch(self, height=iPx)
|
||||
@@ -621,7 +621,7 @@ class _HeadingsTab(NScrollablePage):
|
||||
self.fmtUnnumbered = QLineEdit("", self)
|
||||
self.fmtUnnumbered.setReadOnly(True)
|
||||
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.setIndent(bSp)
|
||||
self.swtUnnumbered = NSwitch(self, height=iPx)
|
||||
@@ -637,7 +637,7 @@ class _HeadingsTab(NScrollablePage):
|
||||
self.fmtScene = QLineEdit("", self)
|
||||
self.fmtScene.setReadOnly(True)
|
||||
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.setIndent(bSp)
|
||||
self.swtScene = NSwitch(self, height=iPx)
|
||||
@@ -653,7 +653,7 @@ class _HeadingsTab(NScrollablePage):
|
||||
self.fmtAScene = QLineEdit("", self)
|
||||
self.fmtAScene.setReadOnly(True)
|
||||
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.setIndent(bSp)
|
||||
self.swtAScene = NSwitch(self, height=iPx)
|
||||
@@ -669,7 +669,7 @@ class _HeadingsTab(NScrollablePage):
|
||||
self.fmtSection = QLineEdit("", self)
|
||||
self.fmtSection.setReadOnly(True)
|
||||
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.setIndent(bSp)
|
||||
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.aInsCharFocus = self.menuInsert.addAction(self.tr("Focus Character"))
|
||||
|
||||
self.aInsTitle.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.TITLE))
|
||||
self.aInsChNum.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CH_NUM))
|
||||
self.aInsChWord.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CH_WORD))
|
||||
self.aInsChRomU.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CH_ROMU))
|
||||
self.aInsChRomL.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CH_ROML))
|
||||
self.aInsScNum.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.SC_NUM))
|
||||
self.aInsScAbs.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.SC_ABS))
|
||||
self.aInsCharPOV.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CHAR_POV))
|
||||
self.aInsCharFocus.triggered.connect(lambda: self._insertIntoForm(nwHeadFmt.CHAR_FOCUS))
|
||||
self.aInsTitle.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.TITLE))
|
||||
self.aInsChNum.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.CH_NUM))
|
||||
self.aInsChWord.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.CH_WORD))
|
||||
self.aInsChRomU.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.CH_ROMU))
|
||||
self.aInsChRomL.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.CH_ROML))
|
||||
self.aInsScNum.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.SC_NUM))
|
||||
self.aInsScAbs.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.SC_ABS))
|
||||
self.aInsCharPOV.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.CHAR_POV))
|
||||
self.aInsCharFocus.triggered.connect(qtLambda(self._insertIntoForm, nwHeadFmt.CHAR_FOCUS))
|
||||
|
||||
self.btnInsert = QPushButton(self.tr("Insert"), self)
|
||||
self.btnInsert.setMenu(self.menuInsert)
|
||||
|
||||
@@ -38,7 +38,7 @@ from PyQt5.QtWidgets import (
|
||||
)
|
||||
|
||||
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.error import formatException
|
||||
from novelwriter.extensions.modified import NToolDialog
|
||||
@@ -277,11 +277,11 @@ class GuiWritingStats(NToolDialog):
|
||||
self.btnSave.setMenu(self.saveMenu)
|
||||
|
||||
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.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)
|
||||
|
||||
# Assemble
|
||||
|
||||
@@ -134,7 +134,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
assert docViewer.docAction(nwDocAction.COPY) is False
|
||||
|
||||
# Open again via menu
|
||||
assert nwGUI.projView.projTree.setSelectedHandle("88243afbe5ed8")
|
||||
nwGUI.projView.projTree.setSelectedHandle("88243afbe5ed8")
|
||||
nwGUI.mainMenu.aViewDoc.activate(QAction.ActionEvent.Trigger)
|
||||
|
||||
# Open context menu
|
||||
|
||||
@@ -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() == "<a href='Bod'>Bod</a>"
|
||||
assert outlineData.tagValues[nwKeyWords.POV_KEY][1].text() == "<a href='Bod'>Bod</a>"
|
||||
outlineView._tagClicked("Bod")
|
||||
assert nwGUI.docViewer.docHandle == "4c4f28287af27"
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
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 nwGUI.openDocument("0000000000013")
|
||||
assert nwGUI.docEditor.getText() == "## New Chapter\n\n"
|
||||
assert projTree._getItemWordCount("0000000000013") == 2
|
||||
|
||||
# Add a new scene next to the other new file
|
||||
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 nwGUI.openDocument("0000000000014")
|
||||
assert nwGUI.docEditor.getText() == "### New Scene\n\n"
|
||||
assert projTree._getItemWordCount("0000000000014") == 2
|
||||
|
||||
# Add a new scene with the content copied from the previous
|
||||
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 nwGUI.openDocument("0000000000015")
|
||||
assert nwGUI.docEditor.getText() == "### New Scene\n\nWith Stuff\n\n"
|
||||
assert projTree._getItemWordCount("0000000000015") == 4
|
||||
|
||||
# Add a new file to the characters folder
|
||||
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 nwGUI.openDocument("0000000000016")
|
||||
assert nwGUI.docEditor.getText() == "# New Note\n\n"
|
||||
assert projTree._getItemWordCount("0000000000016") == 2
|
||||
|
||||
# Make sure the sibling folder bug trap works
|
||||
projView.setSelectedHandle("0000000000013")
|
||||
@@ -1146,30 +1143,37 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
def itemPos(tHandle):
|
||||
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:
|
||||
mp.setattr(QMenu, "exec", lambda *a: None)
|
||||
mockMenu = MagicMock()
|
||||
mp.setattr(QMenu, "exec", mockMenu)
|
||||
projTree.clearSelection()
|
||||
|
||||
# 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
|
||||
assert projTree._openContextMenu(itemPos(hTrashRoot)) is True
|
||||
projTree.openContextMenu(itemPos(hTrashRoot))
|
||||
assert mockMenu.call_count == 1
|
||||
|
||||
# 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
|
||||
projTree._getTreeItem(hNovelNote).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
|
||||
projTree.setSelectedHandle(C.hNovelRoot)
|
||||
assert projTree.openContextOnSelected() is True
|
||||
projTree.openContextMenu(None)
|
||||
assert mockMenu.call_count == 4
|
||||
projTree.clearSelection()
|
||||
assert projTree.openContextOnSelected() is False
|
||||
projTree.openContextMenu(None)
|
||||
assert mockMenu.call_count == 4
|
||||
|
||||
# Menu Builders
|
||||
# =============
|
||||
|
||||
Reference in New Issue
Block a user