Update novel tree menu processing and fix issue with reaload of document

This commit is contained in:
Veronica Berglyd Olsen
2022-06-15 16:14:28 +02:00
parent 538b722bdf
commit 3a061eec3f
6 changed files with 110 additions and 92 deletions
+92 -67
View File
@@ -28,11 +28,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import logging
import novelwriter
from time import time
from enum import Enum
from time import time
from PyQt5.QtCore import Qt, QSize, pyqtSlot, pyqtSignal
from PyQt5.QtGui import QPalette, QPixmap, QColor
from PyQt5.QtCore import Qt, QSize, pyqtSlot, pyqtSignal
from PyQt5.QtWidgets import (
QAbstractItemView, QActionGroup, QFrame, QHBoxLayout, QHeaderView, QLabel,
QMenu, QSizePolicy, QToolButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
@@ -81,13 +81,7 @@ class GuiNovelView(QWidget):
self.setLayout(self.outerBox)
# Connect Signals
self.novelBar.rootFolderSelectionChanged.connect(
lambda tHandle: self.novelTree.refreshTree(rootHandle=tHandle, overRide=True)
)
# Function Mappings
self.refreshTree = self.novelTree.refreshTree
self.updateWordCounts = self.novelTree.updateWordCounts
self.getSelectedHandle = self.novelTree.getSelectedHandle
@@ -101,30 +95,44 @@ class GuiNovelView(QWidget):
self.novelTree.initSettings()
return
def refreshTree(self):
"""Refresh the current tree.
"""
self.novelTree.refreshTree(rootHandle=self.theProject.lastNovel)
return
def clearProject(self):
self.novelTree.clearTree()
"""Clear project-related GUI content.
"""
self.novelTree.clearContent()
self.novelBar.clearContent()
return
def openProjectTasks(self):
"""Run tasks when opening a project.
"""Run opening project tasks.
"""
lastNovel = self.theProject.lastNovel
if lastNovel is None:
if lastNovel not in self.theProject.tree:
lastNovel = self.theProject.tree.findRoot(nwItemClass.NOVEL)
logger.debug("Setting novel tree to root item '%s'", lastNovel)
lastCol = self.theProject.options.getEnum(
"GuiNovelView", "lastCol", NovelTreeColumn, NovelTreeColumn.HIDDEN
)
self.clearProject()
self.novelBar.rebuildNovelRootMenu(selHandle=lastNovel)
self.novelTree.loadOptions()
self.novelTree.refreshTree(rootHandle=lastNovel, overRide=True)
self.novelBar.buildNovelRootMenu()
self.novelBar.setLastColType(lastCol, doRefresh=False)
self.novelBar.setCurrentRoot(lastNovel)
return
def closeProjectTasks(self):
"""Run tasks when closing a project.
"""Run closing project tasks.
"""
self.novelTree.saveOptions()
lastColType = self.novelTree.lastColType
self.theProject.options.setValue("GuiNovelView", "lastCol", lastColType)
return
def setFocus(self):
@@ -133,7 +141,7 @@ class GuiNovelView(QWidget):
self.novelTree.setFocus()
return
def treeFocus(self):
def treeHasFocus(self):
"""Check if the novel tree has focus.
"""
return self.novelTree.hasFocus()
@@ -144,9 +152,9 @@ class GuiNovelView(QWidget):
@pyqtSlot(str)
def updateRootItem(self, tHandle):
"""Should be called whenever a root folders changes.
"""If any root item changes, rebuild the novel root menu.
"""
self.novelBar.rebuildNovelRootMenu()
self.novelBar.buildNovelRootMenu()
return
# END Class GuiNovelView
@@ -154,8 +162,6 @@ class GuiNovelView(QWidget):
class GuiNovelToolBar(QWidget):
rootFolderSelectionChanged = pyqtSignal(str)
def __init__(self, novelView):
QTreeWidget.__init__(self, novelView)
@@ -167,7 +173,7 @@ class GuiNovelToolBar(QWidget):
self.mainTheme = novelView.mainGui.mainTheme
iPx = self.mainTheme.baseIconSize
mPx = self.mainConf.pxInt(4)
mPx = self.mainConf.pxInt(3)
self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True)
@@ -197,6 +203,8 @@ class GuiNovelToolBar(QWidget):
# Novel Root Menu
self.mRoot = QMenu()
self.gRoot = QActionGroup(self.mRoot)
self.aRoot = {}
self.tbRoot = QToolButton(self)
self.tbRoot.setToolTip(self.tr("Novel Root"))
@@ -209,19 +217,13 @@ class GuiNovelToolBar(QWidget):
# More Options Menu
self.mMore = QMenu()
self.mCol3 = self.mMore.addMenu(self.tr("Third Column"))
self.mCol3.addAction(self.tr("Hide Column")).triggered.connect(
lambda: self.novelView.novelTree.setLastColType(NovelTreeColumn.HIDDEN)
)
self.mCol3.addAction(self.tr("Point of View Character")).triggered.connect(
lambda: self.novelView.novelTree.setLastColType(NovelTreeColumn.POV)
)
self.mCol3.addAction(self.tr("Focus Character")).triggered.connect(
lambda: self.novelView.novelTree.setLastColType(NovelTreeColumn.FOCUS)
)
self.mCol3.addAction(self.tr("Novel Plot")).triggered.connect(
lambda: self.novelView.novelTree.setLastColType(NovelTreeColumn.PLOT)
)
self.mLastCol = self.mMore.addMenu(self.tr("Last Column"))
self.gLastCol = QActionGroup(self.mMore)
self.aLastCol = {}
self._addLastColAction(NovelTreeColumn.HIDDEN, self.tr("Hidden"))
self._addLastColAction(NovelTreeColumn.POV, self.tr("Point of View Character"))
self._addLastColAction(NovelTreeColumn.FOCUS, self.tr("Focus Character"))
self._addLastColAction(NovelTreeColumn.PLOT, self.tr("Novel Plot"))
self.tbMore = QToolButton(self)
self.tbMore.setToolTip(self.tr("More Options"))
@@ -250,25 +252,41 @@ class GuiNovelToolBar(QWidget):
# Methods
##
def rebuildNovelRootMenu(self, selHandle=None):
def clearContent(self):
"""Run clearing project tasks.
"""
self.mRoot.clear()
self.aRoot = {}
return
def buildNovelRootMenu(self):
"""Build the novel root menu.
"""
self.mRoot.clear()
agRoot = QActionGroup(self.mRoot)
self.aRoot = {}
for n, (tHandle, nwItem) in enumerate(self.theProject.tree.iterRoots(nwItemClass.NOVEL)):
aRoot = self.mRoot.addAction(nwItem.itemName)
aRoot.setData(tHandle)
aRoot.setCheckable(True)
aRoot.triggered.connect(
lambda n, tHandle=tHandle: self.rootFolderSelectionChanged.emit(tHandle)
)
agRoot.addAction(aRoot)
aRoot.triggered.connect(lambda n, tHandle=tHandle: self.setCurrentRoot(tHandle))
self.gRoot.addAction(aRoot)
self.aRoot[tHandle] = aRoot
if n == 0:
aRoot.setChecked(True)
if selHandle == tHandle:
aRoot.setChecked(True)
return
def setCurrentRoot(self, rootHandle):
"""Set the current active root handle.
"""
if rootHandle in self.aRoot:
self.aRoot[rootHandle].setChecked(True)
self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True)
return
def setLastColType(self, colType, doRefresh=True):
"""Set the last column type.
"""
self.aLastCol[colType].setChecked(True)
self.novelView.novelTree.setLastColType(colType, doRefresh=doRefresh)
return
##
@@ -283,6 +301,20 @@ class GuiNovelToolBar(QWidget):
self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True)
return
##
# Internal Functions
##
def _addLastColAction(self, colType, actionLabel):
"""Add a column selection entry to the last column menu.
"""
aLast = self.mLastCol.addAction(actionLabel)
aLast.setCheckable(True)
aLast.setActionGroup(self.gLastCol)
aLast.triggered.connect(lambda: self.setLastColType(colType))
self.aLastCol[colType] = aLast
return
# END Class GuiNovelToolBar
@@ -319,13 +351,12 @@ class GuiNovelTree(QTreeWidget):
iPx = self.mainTheme.baseIconSize
nPx = self.mainTheme.textNWidth
cMg = self.mainConf.pxInt(6)
mPx = self.mainConf.pxInt(4)
nMg = self.mainConf.pxInt(6)
# self.setIconSize(QSize(iPx, iPx))
self.setFrameStyle(QFrame.NoFrame)
self.setHeaderHidden(True)
self.setIndentation(mPx)
self.setIndentation(0)
self.setColumnCount(3)
self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setSelectionMode(QAbstractItemView.SingleSelection)
@@ -349,7 +380,7 @@ class GuiNovelTree(QTreeWidget):
fH2.setBold(True)
self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()]
self._hIndent = ["", "", "", "\u2022\u00a0", "\u00bb\u00a0"]
self._hIndent = ["", "", "", "\u203a\u00a0", "\u00bb\u00a0"]
self._pIndent = [QPixmap(), QPixmap()]
hPix = QPixmap(QSize(iPx, iPx))
@@ -386,11 +417,19 @@ class GuiNovelTree(QTreeWidget):
return
##
# Properties
##
@property
def lastColType(self):
return self._lastCol
##
# Class Methods
##
def clearTree(self):
def clearContent(self):
"""Clear the GUI content and the related maps.
"""
self.clear()
@@ -398,21 +437,6 @@ class GuiNovelTree(QTreeWidget):
self._lastBuild = 0
return
def loadOptions(self):
"""Load user options.
"""
self._lastCol = self.theProject.options.getEnum(
"GuiNovelView", "lastCol", NovelTreeColumn, NovelTreeColumn.POV
)
self.setColumnHidden(self.C_LAST, self._lastCol == NovelTreeColumn.HIDDEN)
return True
def saveOptions(self):
"""Save user options.
"""
self.theProject.options.setValue("GuiNovelView", "lastCol", self._lastCol)
return
def refreshTree(self, rootHandle=None, overRide=False):
"""Called whenever the Novel tab is activated.
"""
@@ -461,14 +485,15 @@ class GuiNovelTree(QTreeWidget):
return tHandle, tLine
def setLastColType(self, colType):
def setLastColType(self, colType, doRefresh=True):
"""Change the content type of the last column and rebuild.
"""
if self._lastCol != colType:
logger.debug("Changing last column to %s", colType.name)
self._lastCol = colType
self.setColumnHidden(self.C_LAST, colType == NovelTreeColumn.HIDDEN)
self.refreshTree(rootHandle=self.theProject.lastNovel, overRide=True)
if doRefresh:
self.refreshTree(rootHandle=self.theProject.lastNovel, overRide=True)
return
##
@@ -531,7 +556,7 @@ class GuiNovelTree(QTreeWidget):
def _populateTree(self, rootHandle):
"""Build the tree based on the project index.
"""
self.clearTree()
self.clearContent()
tStart = time()
logger.verbose("Building novel tree for root item '%s'", rootHandle)
+1 -1
View File
@@ -117,7 +117,7 @@ class GuiOutlineView(QWidget):
self.outlineTree.refreshTree(overRide=overRide, novelChanged=novelChanged)
return
def treeFocus(self):
def treeHasFocus(self):
return self.outlineTree.hasFocus()
def setTreeFocus(self):
+11 -18
View File
@@ -31,8 +31,8 @@ import novelwriter
from enum import Enum
from time import time
from PyQt5.QtGui import QPalette
from PyQt5.QtCore import Qt, QSize, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QIcon, QPalette
from PyQt5.QtWidgets import (
QAbstractItemView, QFrame, QHBoxLayout, QHeaderView, QLabel,
QMenu, QShortcut, QSizePolicy, QToolButton, QTreeWidget, QTreeWidgetItem,
@@ -142,7 +142,7 @@ class GuiProjectView(QWidget):
self.projTree.setFocus()
return
def treeFocus(self):
def treeHasFocus(self):
"""Check if the project tree has focus.
"""
return self.projTree.hasFocus()
@@ -177,7 +177,7 @@ class GuiProjectToolBar(QWidget):
self.mainTheme = projView.mainGui.mainTheme
iPx = self.mainTheme.baseIconSize
mPx = self.mainConf.pxInt(4)
mPx = self.mainConf.pxInt(3)
self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True)
@@ -252,7 +252,7 @@ class GuiProjectToolBar(QWidget):
self._addRootFolderEntry(nwItemClass.PLOT)
self._addRootFolderEntry(nwItemClass.CHARACTER)
self._addRootFolderEntry(nwItemClass.WORLD)
self._addRootFolderEntry(nwItemClass.ARCHIVE)
self._addRootFolderEntry(nwItemClass.TIMELINE)
self._addRootFolderEntry(nwItemClass.OBJECT)
self._addRootFolderEntry(nwItemClass.ENTITY)
self._addRootFolderEntry(nwItemClass.CUSTOM)
@@ -778,13 +778,6 @@ class GuiProjectTree(QTreeWidget):
if trItem is None or nwItem is None:
return
expIcon = QIcon()
if nwItem.itemType == nwItemType.FILE:
if nwItem.isExported:
expIcon = self.mainTheme.getIcon("check")
else:
expIcon = self.mainTheme.getIcon("cross")
itemStatus, statusIcon = nwItem.getImportStatus()
hLevel = self.theProject.index.getHandleHeaderLevel(tHandle)
itemIcon = self.mainTheme.getItemIcon(
@@ -793,18 +786,18 @@ class GuiProjectTree(QTreeWidget):
trItem.setIcon(self.C_NAME, itemIcon)
trItem.setText(self.C_NAME, nwItem.itemName)
trItem.setIcon(self.C_EXPORT, expIcon)
trItem.setIcon(self.C_STATUS, statusIcon)
trItem.setToolTip(self.C_STATUS, itemStatus)
if nwItem.itemType == nwItemType.FILE:
trItem.setIcon(
self.C_EXPORT, self.mainTheme.getIcon("check" if nwItem.isExported else "cross")
)
if self.mainConf.emphLabels and nwItem.itemLayout == nwItemLayout.DOCUMENT:
trFont = trItem.font(self.C_NAME)
if hLevel in ("H1", "H2"):
trFont.setBold(True)
trFont.setUnderline(True)
else:
trFont.setBold(False)
trFont.setUnderline(False)
trFont.setBold(hLevel == "H1" or hLevel == "H2")
trFont.setUnderline(hLevel == "H1")
trItem.setFont(self.C_NAME, trFont)
return
+3 -3
View File
@@ -796,11 +796,11 @@ class GuiMain(QMainWindow):
tHandle = None
tLine = None
if self.projView.treeFocus():
if self.projView.treeHasFocus():
tHandle = self.projView.getSelectedHandle()
elif self.novelView.treeFocus():
elif self.novelView.treeHasFocus():
tHandle, tLine = self.novelView.getSelectedHandle()
elif self.outlineView.treeFocus():
elif self.outlineView.treeHasFocus():
tHandle, tLine = self.outlineView.getSelectedHandle()
else:
logger.warning("No item selected")
+2 -2
View File
@@ -135,7 +135,7 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
nwGUI._changeView(nwView.NOVEL)
nwGUI.novelView.refreshTree(rootHandle=None, overRide=True)
with monkeypatch.context() as mp:
mp.setattr(GuiNovelView, "treeFocus", lambda *a: True)
mp.setattr(GuiNovelView, "treeHasFocus", lambda *a: True)
assert nwGUI.docEditor.docHandle() is None
selItem = nwGUI.novelView.novelTree.topLevelItem(2)
nwGUI.novelView.novelTree.setCurrentItem(selItem)
@@ -147,7 +147,7 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
nwGUI._changeView(nwView.OUTLINE)
nwGUI.switchFocus(nwWidget.OUTLINE)
with monkeypatch.context() as mp:
mp.setattr(GuiOutlineView, "treeFocus", lambda *a: True)
mp.setattr(GuiOutlineView, "treeHasFocus", lambda *a: True)
assert nwGUI.docEditor.docHandle() is None
actItem = nwGUI.outlineView.outlineTree.topLevelItem(0)
chpItem = actItem.child(0)
+1 -1
View File
@@ -71,7 +71,7 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, fncDir):
# Check focus
with monkeypatch.context() as mp:
mp.setattr(QWidget, "hasFocus", lambda *a: True)
assert outlineView.treeFocus() is True
assert outlineView.treeHasFocus() is True
outlineView.setTreeFocus() # Can't check. just ensures that it doesn't error