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