Wrap the novel tree in an outer widget

This commit is contained in:
Veronica Berglyd Olsen
2022-06-12 15:45:39 +02:00
parent ed5c52859d
commit b3eb276437
5 changed files with 126 additions and 67 deletions
+2 -2
View File
@@ -23,7 +23,7 @@ from novelwriter.gui.doceditor import GuiDocEditor
from novelwriter.gui.docviewer import GuiDocViewer, GuiDocViewDetails from novelwriter.gui.docviewer import GuiDocViewer, GuiDocViewDetails
from novelwriter.gui.itemdetails import GuiItemDetails from novelwriter.gui.itemdetails import GuiItemDetails
from novelwriter.gui.mainmenu import GuiMainMenu from novelwriter.gui.mainmenu import GuiMainMenu
from novelwriter.gui.noveltree import GuiNovelTree from novelwriter.gui.noveltree import GuiNovelView
from novelwriter.gui.outline import GuiOutlineView from novelwriter.gui.outline import GuiOutlineView
from novelwriter.gui.projtree import GuiProjectView from novelwriter.gui.projtree import GuiProjectView
from novelwriter.gui.statusbar import GuiMainStatus from novelwriter.gui.statusbar import GuiMainStatus
@@ -37,7 +37,7 @@ __all__ = [
"GuiItemDetails", "GuiItemDetails",
"GuiMainMenu", "GuiMainMenu",
"GuiMainStatus", "GuiMainStatus",
"GuiNovelTree", "GuiNovelView",
"GuiOutlineView", "GuiOutlineView",
"GuiProjectView", "GuiProjectView",
"GuiTheme", "GuiTheme",
+76 -17
View File
@@ -30,7 +30,8 @@ from time import time
from PyQt5.QtCore import Qt, QSize from PyQt5.QtCore import Qt, QSize
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QTreeWidget, QTreeWidgetItem, QAbstractItemView, QFrame QAbstractItemView, QFrame, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
QWidget
) )
from novelwriter.common import checkInt from novelwriter.common import checkInt
@@ -39,21 +40,88 @@ from novelwriter.constants import nwKeyWords
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiNovelView(QWidget):
def __init__(self, mainGui):
QWidget.__init__(self, mainGui)
self.mainGui = mainGui
# Build GUI
self.novelTree = GuiNovelTree(self)
self.novelBar = GuiNovelToolBar(self)
# Assemble
self.outerBox = QVBoxLayout()
self.outerBox.addWidget(self.novelBar, 0)
self.outerBox.addWidget(self.novelTree, 1)
self.outerBox.setContentsMargins(0, 0, 0, 0)
self.outerBox.setSpacing(0)
self.setLayout(self.outerBox)
# Function Mappings
self.refreshTree = self.novelTree.refreshTree
self.updateWordCounts = self.novelTree.updateWordCounts
self.getSelectedHandle = self.novelTree.getSelectedHandle
return
##
# Methods
##
def initSettings(self):
self.novelTree.initSettings()
return
def clearProject(self):
self.novelTree.clearTree()
return
def setFocus(self):
"""Forward the set focus call to the tree widget.
"""
self.novelTree.setFocus()
return
def treeFocus(self):
"""Check if the novel tree has focus.
"""
return self.novelTree.hasFocus()
# END Class GuiNovelView
class GuiNovelToolBar(QWidget):
def __init__(self, novelView):
QTreeWidget.__init__(self, novelView)
self.mainConf = novelwriter.CONFIG
self.novelView = novelView
return
# END Class GuiNovelToolBar
class GuiNovelTree(QTreeWidget): class GuiNovelTree(QTreeWidget):
C_TITLE = 0 C_TITLE = 0
C_WORDS = 1 C_WORDS = 1
C_POV = 2 C_POV = 2
def __init__(self, mainGui): def __init__(self, novelView):
QTreeWidget.__init__(self, mainGui) QTreeWidget.__init__(self, novelView)
logger.debug("Initialising GuiNovelTree ...") logger.debug("Initialising GuiNovelTree ...")
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.mainGui = mainGui self.novelView = novelView
self.mainTheme = mainGui.mainTheme self.mainGui = novelView.mainGui
self.theProject = mainGui.theProject self.mainTheme = novelView.mainGui.mainTheme
self.theProject = novelView.mainGui.theProject
# Internal Variables # Internal Variables
self._treeMap = {} self._treeMap = {}
@@ -97,13 +165,13 @@ class GuiNovelTree(QTreeWidget):
self.resizeColumnToContents(self.C_POV) self.resizeColumnToContents(self.C_POV)
# Set custom settings # Set custom settings
self.initTree() self.initSettings()
logger.debug("GuiNovelTree initialisation complete") logger.debug("GuiNovelTree initialisation complete")
return return
def initTree(self): def initSettings(self):
"""Set or update tree widget settings. """Set or update tree widget settings.
""" """
# Scroll bars # Scroll bars
@@ -162,15 +230,6 @@ class GuiNovelTree(QTreeWidget):
self._treeMap[titleKey].setText(self.C_WORDS, f"{wCount:n}") self._treeMap[titleKey].setText(self.C_WORDS, f"{wCount:n}")
return return
def getColumnSizes(self):
"""Return the column widths for the tree columns.
"""
retVals = [
self.columnWidth(0),
self.columnWidth(1),
]
return retVals
def getSelectedHandle(self): def getSelectedHandle(self):
"""Get the currently selected handle. If multiple items are """Get the currently selected handle. If multiple items are
selected, return the first. selected, return the first.
+5 -6
View File
@@ -40,7 +40,7 @@ from PyQt5.QtWidgets import (
from novelwriter.gui import ( from novelwriter.gui import (
GuiDocEditor, GuiDocViewDetails, GuiDocViewer, GuiItemDetails, GuiMainMenu, GuiDocEditor, GuiDocViewDetails, GuiDocViewer, GuiItemDetails, GuiMainMenu,
GuiMainStatus, GuiNovelTree, GuiOutlineView, GuiProjectView, GuiTheme, GuiMainStatus, GuiNovelView, GuiOutlineView, GuiProjectView, GuiTheme,
GuiViewsBar GuiViewsBar
) )
from novelwriter.dialogs import ( from novelwriter.dialogs import (
@@ -106,7 +106,7 @@ class GuiMain(QMainWindow):
# Main GUI Elements # Main GUI Elements
self.statusBar = GuiMainStatus(self) self.statusBar = GuiMainStatus(self)
self.projView = GuiProjectView(self) self.projView = GuiProjectView(self)
self.novelView = GuiNovelTree(self) self.novelView = GuiNovelView(self)
self.docEditor = GuiDocEditor(self) self.docEditor = GuiDocEditor(self)
self.viewMeta = GuiDocViewDetails(self) self.viewMeta = GuiDocViewDetails(self)
self.docViewer = GuiDocViewer(self) self.docViewer = GuiDocViewer(self)
@@ -292,7 +292,7 @@ class GuiMain(QMainWindow):
""" """
# Project Area # Project Area
self.projView.clearProject() self.projView.clearProject()
self.novelView.clearTree() self.novelView.clearProject()
self.itemDetails.clearDetails() self.itemDetails.clearDetails()
# Work Area # Work Area
@@ -791,7 +791,7 @@ class GuiMain(QMainWindow):
tLine = None tLine = None
if self.projView.treeFocus(): if self.projView.treeFocus():
tHandle = self.projView.getSelectedHandle() tHandle = self.projView.getSelectedHandle()
elif self.novelView.hasFocus(): elif self.novelView.treeFocus():
tHandle, tLine = self.novelView.getSelectedHandle() tHandle, tLine = self.novelView.getSelectedHandle()
elif self.outlineView.treeFocus(): elif self.outlineView.treeFocus():
tHandle, tLine = self.outlineView.getSelectedHandle() tHandle, tLine = self.outlineView.getSelectedHandle()
@@ -920,7 +920,7 @@ class GuiMain(QMainWindow):
self.docEditor.initEditor() self.docEditor.initEditor()
self.docViewer.initViewer() self.docViewer.initViewer()
self.projView.initSettings() self.projView.initSettings()
self.novelView.initTree() self.novelView.initSettings()
self.outlineView.initOutline() self.outlineView.initOutline()
self._updateStatusWordCount() self._updateStatusWordCount()
@@ -1161,7 +1161,6 @@ class GuiMain(QMainWindow):
self.mainConf.setViewPanePos(self.splitView.sizes()) self.mainConf.setViewPanePos(self.splitView.sizes())
self.mainConf.setShowRefPanel(self.viewMeta.isVisible()) self.mainConf.setShowRefPanel(self.viewMeta.isVisible())
self.mainConf.setNovelColWidths(self.novelView.getColumnSizes())
if not self.mainConf.isFullScreen: if not self.mainConf.isFullScreen:
self.mainConf.setWinSize(self.width(), self.height()) self.mainConf.setWinSize(self.width(), self.height())
+4 -4
View File
@@ -28,7 +28,7 @@ from tools import cmpFiles, buildTestProject, XML_IGNORE, writeFile
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMessageBox, QInputDialog from PyQt5.QtWidgets import QMessageBox, QInputDialog
from novelwriter.gui import GuiDocEditor, GuiNovelTree, GuiOutlineView from novelwriter.gui import GuiDocEditor, GuiNovelView, GuiOutlineView
from novelwriter.enum import nwItemType, nwWidget from novelwriter.enum import nwItemType, nwWidget
from novelwriter.tools import GuiProjectWizard from novelwriter.tools import GuiProjectWizard
from novelwriter.gui.projtree import GuiProjectTree from novelwriter.gui.projtree import GuiProjectTree
@@ -134,12 +134,12 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
nwGUI.projStack.setCurrentIndex(1) nwGUI.projStack.setCurrentIndex(1)
nwGUI.novelView.refreshTree(True) nwGUI.novelView.refreshTree(True)
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(GuiNovelTree, "hasFocus", lambda *a: True) mp.setattr(GuiNovelView, "treeFocus", lambda *a: True)
assert nwGUI.docEditor.docHandle() is None assert nwGUI.docEditor.docHandle() is None
actItem = nwGUI.novelView.topLevelItem(0) actItem = nwGUI.novelView.novelTree.topLevelItem(0)
chpItem = actItem.child(0) chpItem = actItem.child(0)
selItem = chpItem.child(0) selItem = chpItem.child(0)
nwGUI.novelView.setCurrentItem(selItem) nwGUI.novelView.novelTree.setCurrentItem(selItem)
nwGUI._keyPressReturn() nwGUI._keyPressReturn()
assert nwGUI.docEditor.docHandle() == sHandle assert nwGUI.docEditor.docHandle() == sHandle
assert nwGUI.closeDocument() is True assert nwGUI.closeDocument() is True
+39 -38
View File
@@ -37,23 +37,24 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal):
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
nwGUI.openProject(nwMinimal) nwGUI.openProject(nwMinimal)
nwTree = nwGUI.novelView novelView = nwGUI.novelView
novelTree = novelView.novelTree
## ##
# Show/Hide Scrollbars # Show/Hide Scrollbars
## ##
nwTree.mainConf.hideVScroll = True nwGUI.mainConf.hideVScroll = True
nwTree.mainConf.hideHScroll = True nwGUI.mainConf.hideHScroll = True
nwTree.initTree() novelView.initSettings()
assert not nwTree.verticalScrollBar().isVisible() assert not novelTree.verticalScrollBar().isVisible()
assert not nwTree.horizontalScrollBar().isVisible() assert not novelTree.horizontalScrollBar().isVisible()
nwTree.mainConf.hideVScroll = False nwGUI.mainConf.hideVScroll = False
nwTree.mainConf.hideHScroll = False nwGUI.mainConf.hideHScroll = False
nwTree.initTree() novelView.initSettings()
assert nwTree.verticalScrollBar().isEnabled() assert novelTree.verticalScrollBar().isEnabled()
assert nwTree.horizontalScrollBar().isEnabled() assert novelTree.horizontalScrollBar().isEnabled()
## ##
# Populate Tree # Populate Tree
@@ -61,31 +62,31 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal):
nwGUI.projStack.setCurrentIndex(nwGUI.idxNovelView) nwGUI.projStack.setCurrentIndex(nwGUI.idxNovelView)
nwGUI.rebuildIndex() nwGUI.rebuildIndex()
nwTree._populateTree() novelTree._populateTree()
assert nwTree.topLevelItemCount() == 1 assert novelTree.topLevelItemCount() == 1
# Rebuild should preserve selection # Rebuild should preserve selection
topItem = nwTree.topLevelItem(0) topItem = novelTree.topLevelItem(0)
assert not topItem.isSelected() assert not topItem.isSelected()
topItem.setSelected(True) topItem.setSelected(True)
assert nwTree.selectedItems()[0] == topItem assert novelTree.selectedItems()[0] == topItem
assert nwTree.getSelectedHandle() == ("a35baf2e93843", 0) assert novelView.getSelectedHandle() == ("a35baf2e93843", 0)
nwTree.refreshTree() novelView.refreshTree()
assert nwTree.topLevelItem(0).isSelected() assert novelTree.topLevelItem(0).isSelected()
## ##
# Open Items # Open Items
## ##
# Clear selection # Clear selection
nwTree.clearSelection() novelTree.clearSelection()
scItem = nwTree.topLevelItem(0).child(0).child(0) scItem = novelTree.topLevelItem(0).child(0).child(0)
scItem.setSelected(True) scItem.setSelected(True)
assert scItem.isSelected() assert scItem.isSelected()
# Clear selection with mouse # Clear selection with mouse
vPort = nwTree.viewport() vPort = novelTree.viewport()
qtbot.mouseClick(vPort, Qt.LeftButton, pos=vPort.rect().center(), delay=10) qtbot.mouseClick(vPort, Qt.LeftButton, pos=vPort.rect().center(), delay=10)
assert not scItem.isSelected() assert not scItem.isSelected()
@@ -93,7 +94,7 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal):
scItem.setSelected(True) scItem.setSelected(True)
assert scItem.isSelected() assert scItem.isSelected()
assert nwGUI.docEditor.docHandle() is None assert nwGUI.docEditor.docHandle() is None
nwTree._treeDoubleClick(scItem, 0) novelTree._treeDoubleClick(scItem, 0)
assert nwGUI.docEditor.docHandle() == "8c659a11cd429" assert nwGUI.docEditor.docHandle() == "8c659a11cd429"
# Open item with middle mouse button # Open item with middle mouse button
@@ -103,13 +104,13 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal):
qtbot.mouseClick(vPort, Qt.MiddleButton, pos=vPort.rect().center(), delay=10) qtbot.mouseClick(vPort, Qt.MiddleButton, pos=vPort.rect().center(), delay=10)
assert nwGUI.docViewer.docHandle() is None assert nwGUI.docViewer.docHandle() is None
scRect = nwTree.visualItemRect(scItem) scRect = novelTree.visualItemRect(scItem)
oldData = scItem.data(nwTree.C_TITLE, Qt.UserRole) oldData = scItem.data(novelTree.C_TITLE, Qt.UserRole)
scItem.setData(nwTree.C_TITLE, Qt.UserRole, (None, "", "")) scItem.setData(novelTree.C_TITLE, Qt.UserRole, (None, "", ""))
qtbot.mouseClick(vPort, Qt.MiddleButton, pos=scRect.center(), delay=10) qtbot.mouseClick(vPort, Qt.MiddleButton, pos=scRect.center(), delay=10)
assert nwGUI.docViewer.docHandle() is None assert nwGUI.docViewer.docHandle() is None
scItem.setData(nwTree.C_TITLE, Qt.UserRole, oldData) scItem.setData(novelTree.C_TITLE, Qt.UserRole, oldData)
qtbot.mouseClick(vPort, Qt.MiddleButton, pos=scRect.center(), delay=10) qtbot.mouseClick(vPort, Qt.MiddleButton, pos=scRect.center(), delay=10)
assert nwGUI.docViewer.docHandle() == "8c659a11cd429" assert nwGUI.docViewer.docHandle() == "8c659a11cd429"
@@ -132,23 +133,23 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal):
"#### Section\n\n" "#### Section\n\n"
)) ))
nwGUI.rebuildIndex() nwGUI.rebuildIndex()
nwTree._populateTree() novelTree._populateTree()
assert nwTree.topLevelItem(0).text(nwTree.C_TITLE) == "Section wo/Scene" assert novelTree.topLevelItem(0).text(novelTree.C_TITLE) == "Section wo/Scene"
assert nwTree.topLevelItem(1).text(nwTree.C_TITLE) == "Scene wo/Chapter" assert novelTree.topLevelItem(1).text(novelTree.C_TITLE) == "Scene wo/Chapter"
assert nwTree.topLevelItem(2).text(nwTree.C_TITLE) == "Chapter wo/Title" assert novelTree.topLevelItem(2).text(novelTree.C_TITLE) == "Chapter wo/Title"
assert nwTree.topLevelItem(3).text(nwTree.C_TITLE) == "Title" assert novelTree.topLevelItem(3).text(novelTree.C_TITLE) == "Title"
tTitle = nwTree.topLevelItem(3) tTitle = novelTree.topLevelItem(3)
assert tTitle.child(0).text(nwTree.C_TITLE) == "Section w/Title, wo/Scene" assert tTitle.child(0).text(novelTree.C_TITLE) == "Section w/Title, wo/Scene"
assert tTitle.child(1).text(nwTree.C_TITLE) == "Scene w/Title, wo/Chapter" assert tTitle.child(1).text(novelTree.C_TITLE) == "Scene w/Title, wo/Chapter"
assert tTitle.child(2).text(nwTree.C_TITLE) == "Chapter" assert tTitle.child(2).text(novelTree.C_TITLE) == "Chapter"
tChap = tTitle.child(2) tChap = tTitle.child(2)
assert tChap.child(0).text(nwTree.C_TITLE) == "Section w/Chapter, wo/Scene" assert tChap.child(0).text(novelTree.C_TITLE) == "Section w/Chapter, wo/Scene"
assert tChap.child(1).text(nwTree.C_TITLE) == "Scene" assert tChap.child(1).text(novelTree.C_TITLE) == "Scene"
tScene = tChap.child(1) tScene = tChap.child(1)
assert tScene.child(0).text(nwTree.C_TITLE) == "Section" assert tScene.child(0).text(novelTree.C_TITLE) == "Section"
## ##
# Close # Close