From b3eb276437dcee8bbac9ec868d42a10283e64ad3 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 12 Jun 2022 15:45:39 +0200 Subject: [PATCH] Wrap the novel tree in an outer widget --- novelwriter/gui/__init__.py | 4 +- novelwriter/gui/noveltree.py | 93 +++++++++++++++++++++++----- novelwriter/guimain.py | 11 ++-- tests/test_gui/test_gui_guimain.py | 8 +-- tests/test_gui/test_gui_noveltree.py | 77 +++++++++++------------ 5 files changed, 126 insertions(+), 67 deletions(-) diff --git a/novelwriter/gui/__init__.py b/novelwriter/gui/__init__.py index 1892e83d..17699942 100644 --- a/novelwriter/gui/__init__.py +++ b/novelwriter/gui/__init__.py @@ -23,7 +23,7 @@ from novelwriter.gui.doceditor import GuiDocEditor from novelwriter.gui.docviewer import GuiDocViewer, GuiDocViewDetails from novelwriter.gui.itemdetails import GuiItemDetails 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.projtree import GuiProjectView from novelwriter.gui.statusbar import GuiMainStatus @@ -37,7 +37,7 @@ __all__ = [ "GuiItemDetails", "GuiMainMenu", "GuiMainStatus", - "GuiNovelTree", + "GuiNovelView", "GuiOutlineView", "GuiProjectView", "GuiTheme", diff --git a/novelwriter/gui/noveltree.py b/novelwriter/gui/noveltree.py index f634ce23..85773478 100644 --- a/novelwriter/gui/noveltree.py +++ b/novelwriter/gui/noveltree.py @@ -30,7 +30,8 @@ from time import time from PyQt5.QtCore import Qt, QSize from PyQt5.QtWidgets import ( - QTreeWidget, QTreeWidgetItem, QAbstractItemView, QFrame + QAbstractItemView, QFrame, QTreeWidget, QTreeWidgetItem, QVBoxLayout, + QWidget ) from novelwriter.common import checkInt @@ -39,21 +40,88 @@ from novelwriter.constants import nwKeyWords 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): C_TITLE = 0 C_WORDS = 1 C_POV = 2 - def __init__(self, mainGui): - QTreeWidget.__init__(self, mainGui) + def __init__(self, novelView): + QTreeWidget.__init__(self, novelView) logger.debug("Initialising GuiNovelTree ...") self.mainConf = novelwriter.CONFIG - self.mainGui = mainGui - self.mainTheme = mainGui.mainTheme - self.theProject = mainGui.theProject + self.novelView = novelView + self.mainGui = novelView.mainGui + self.mainTheme = novelView.mainGui.mainTheme + self.theProject = novelView.mainGui.theProject # Internal Variables self._treeMap = {} @@ -97,13 +165,13 @@ class GuiNovelTree(QTreeWidget): self.resizeColumnToContents(self.C_POV) # Set custom settings - self.initTree() + self.initSettings() logger.debug("GuiNovelTree initialisation complete") return - def initTree(self): + def initSettings(self): """Set or update tree widget settings. """ # Scroll bars @@ -162,15 +230,6 @@ class GuiNovelTree(QTreeWidget): self._treeMap[titleKey].setText(self.C_WORDS, f"{wCount:n}") return - def getColumnSizes(self): - """Return the column widths for the tree columns. - """ - retVals = [ - self.columnWidth(0), - self.columnWidth(1), - ] - return retVals - def getSelectedHandle(self): """Get the currently selected handle. If multiple items are selected, return the first. diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index e15a82b0..6fa52eb6 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -40,7 +40,7 @@ from PyQt5.QtWidgets import ( from novelwriter.gui import ( GuiDocEditor, GuiDocViewDetails, GuiDocViewer, GuiItemDetails, GuiMainMenu, - GuiMainStatus, GuiNovelTree, GuiOutlineView, GuiProjectView, GuiTheme, + GuiMainStatus, GuiNovelView, GuiOutlineView, GuiProjectView, GuiTheme, GuiViewsBar ) from novelwriter.dialogs import ( @@ -106,7 +106,7 @@ class GuiMain(QMainWindow): # Main GUI Elements self.statusBar = GuiMainStatus(self) self.projView = GuiProjectView(self) - self.novelView = GuiNovelTree(self) + self.novelView = GuiNovelView(self) self.docEditor = GuiDocEditor(self) self.viewMeta = GuiDocViewDetails(self) self.docViewer = GuiDocViewer(self) @@ -292,7 +292,7 @@ class GuiMain(QMainWindow): """ # Project Area self.projView.clearProject() - self.novelView.clearTree() + self.novelView.clearProject() self.itemDetails.clearDetails() # Work Area @@ -791,7 +791,7 @@ class GuiMain(QMainWindow): tLine = None if self.projView.treeFocus(): tHandle = self.projView.getSelectedHandle() - elif self.novelView.hasFocus(): + elif self.novelView.treeFocus(): tHandle, tLine = self.novelView.getSelectedHandle() elif self.outlineView.treeFocus(): tHandle, tLine = self.outlineView.getSelectedHandle() @@ -920,7 +920,7 @@ class GuiMain(QMainWindow): self.docEditor.initEditor() self.docViewer.initViewer() self.projView.initSettings() - self.novelView.initTree() + self.novelView.initSettings() self.outlineView.initOutline() self._updateStatusWordCount() @@ -1161,7 +1161,6 @@ class GuiMain(QMainWindow): self.mainConf.setViewPanePos(self.splitView.sizes()) self.mainConf.setShowRefPanel(self.viewMeta.isVisible()) - self.mainConf.setNovelColWidths(self.novelView.getColumnSizes()) if not self.mainConf.isFullScreen: self.mainConf.setWinSize(self.width(), self.height()) diff --git a/tests/test_gui/test_gui_guimain.py b/tests/test_gui/test_gui_guimain.py index 5fa6951b..9a56af3d 100644 --- a/tests/test_gui/test_gui_guimain.py +++ b/tests/test_gui/test_gui_guimain.py @@ -28,7 +28,7 @@ from tools import cmpFiles, buildTestProject, XML_IGNORE, writeFile from PyQt5.QtCore import Qt 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.tools import GuiProjectWizard from novelwriter.gui.projtree import GuiProjectTree @@ -134,12 +134,12 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): nwGUI.projStack.setCurrentIndex(1) nwGUI.novelView.refreshTree(True) 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 - actItem = nwGUI.novelView.topLevelItem(0) + actItem = nwGUI.novelView.novelTree.topLevelItem(0) chpItem = actItem.child(0) selItem = chpItem.child(0) - nwGUI.novelView.setCurrentItem(selItem) + nwGUI.novelView.novelTree.setCurrentItem(selItem) nwGUI._keyPressReturn() assert nwGUI.docEditor.docHandle() == sHandle assert nwGUI.closeDocument() is True diff --git a/tests/test_gui/test_gui_noveltree.py b/tests/test_gui/test_gui_noveltree.py index 2bf2df8a..644839d3 100644 --- a/tests/test_gui/test_gui_noveltree.py +++ b/tests/test_gui/test_gui_noveltree.py @@ -37,23 +37,24 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal): monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) nwGUI.openProject(nwMinimal) - nwTree = nwGUI.novelView + novelView = nwGUI.novelView + novelTree = novelView.novelTree ## # Show/Hide Scrollbars ## - nwTree.mainConf.hideVScroll = True - nwTree.mainConf.hideHScroll = True - nwTree.initTree() - assert not nwTree.verticalScrollBar().isVisible() - assert not nwTree.horizontalScrollBar().isVisible() + nwGUI.mainConf.hideVScroll = True + nwGUI.mainConf.hideHScroll = True + novelView.initSettings() + assert not novelTree.verticalScrollBar().isVisible() + assert not novelTree.horizontalScrollBar().isVisible() - nwTree.mainConf.hideVScroll = False - nwTree.mainConf.hideHScroll = False - nwTree.initTree() - assert nwTree.verticalScrollBar().isEnabled() - assert nwTree.horizontalScrollBar().isEnabled() + nwGUI.mainConf.hideVScroll = False + nwGUI.mainConf.hideHScroll = False + novelView.initSettings() + assert novelTree.verticalScrollBar().isEnabled() + assert novelTree.horizontalScrollBar().isEnabled() ## # Populate Tree @@ -61,31 +62,31 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal): nwGUI.projStack.setCurrentIndex(nwGUI.idxNovelView) nwGUI.rebuildIndex() - nwTree._populateTree() - assert nwTree.topLevelItemCount() == 1 + novelTree._populateTree() + assert novelTree.topLevelItemCount() == 1 # Rebuild should preserve selection - topItem = nwTree.topLevelItem(0) + topItem = novelTree.topLevelItem(0) assert not topItem.isSelected() topItem.setSelected(True) - assert nwTree.selectedItems()[0] == topItem - assert nwTree.getSelectedHandle() == ("a35baf2e93843", 0) + assert novelTree.selectedItems()[0] == topItem + assert novelView.getSelectedHandle() == ("a35baf2e93843", 0) - nwTree.refreshTree() - assert nwTree.topLevelItem(0).isSelected() + novelView.refreshTree() + assert novelTree.topLevelItem(0).isSelected() ## # Open Items ## # Clear selection - nwTree.clearSelection() - scItem = nwTree.topLevelItem(0).child(0).child(0) + novelTree.clearSelection() + scItem = novelTree.topLevelItem(0).child(0).child(0) scItem.setSelected(True) assert scItem.isSelected() # Clear selection with mouse - vPort = nwTree.viewport() + vPort = novelTree.viewport() qtbot.mouseClick(vPort, Qt.LeftButton, pos=vPort.rect().center(), delay=10) assert not scItem.isSelected() @@ -93,7 +94,7 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal): scItem.setSelected(True) assert scItem.isSelected() assert nwGUI.docEditor.docHandle() is None - nwTree._treeDoubleClick(scItem, 0) + novelTree._treeDoubleClick(scItem, 0) assert nwGUI.docEditor.docHandle() == "8c659a11cd429" # 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) assert nwGUI.docViewer.docHandle() is None - scRect = nwTree.visualItemRect(scItem) - oldData = scItem.data(nwTree.C_TITLE, Qt.UserRole) - scItem.setData(nwTree.C_TITLE, Qt.UserRole, (None, "", "")) + scRect = novelTree.visualItemRect(scItem) + oldData = scItem.data(novelTree.C_TITLE, Qt.UserRole) + scItem.setData(novelTree.C_TITLE, Qt.UserRole, (None, "", "")) qtbot.mouseClick(vPort, Qt.MiddleButton, pos=scRect.center(), delay=10) 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) assert nwGUI.docViewer.docHandle() == "8c659a11cd429" @@ -132,23 +133,23 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal): "#### Section\n\n" )) nwGUI.rebuildIndex() - nwTree._populateTree() - assert nwTree.topLevelItem(0).text(nwTree.C_TITLE) == "Section wo/Scene" - assert nwTree.topLevelItem(1).text(nwTree.C_TITLE) == "Scene wo/Chapter" - assert nwTree.topLevelItem(2).text(nwTree.C_TITLE) == "Chapter wo/Title" - assert nwTree.topLevelItem(3).text(nwTree.C_TITLE) == "Title" + novelTree._populateTree() + assert novelTree.topLevelItem(0).text(novelTree.C_TITLE) == "Section wo/Scene" + assert novelTree.topLevelItem(1).text(novelTree.C_TITLE) == "Scene wo/Chapter" + assert novelTree.topLevelItem(2).text(novelTree.C_TITLE) == "Chapter wo/Title" + assert novelTree.topLevelItem(3).text(novelTree.C_TITLE) == "Title" - tTitle = nwTree.topLevelItem(3) - assert tTitle.child(0).text(nwTree.C_TITLE) == "Section w/Title, wo/Scene" - assert tTitle.child(1).text(nwTree.C_TITLE) == "Scene w/Title, wo/Chapter" - assert tTitle.child(2).text(nwTree.C_TITLE) == "Chapter" + tTitle = novelTree.topLevelItem(3) + assert tTitle.child(0).text(novelTree.C_TITLE) == "Section w/Title, wo/Scene" + assert tTitle.child(1).text(novelTree.C_TITLE) == "Scene w/Title, wo/Chapter" + assert tTitle.child(2).text(novelTree.C_TITLE) == "Chapter" tChap = tTitle.child(2) - assert tChap.child(0).text(nwTree.C_TITLE) == "Section w/Chapter, wo/Scene" - assert tChap.child(1).text(nwTree.C_TITLE) == "Scene" + assert tChap.child(0).text(novelTree.C_TITLE) == "Section w/Chapter, wo/Scene" + assert tChap.child(1).text(novelTree.C_TITLE) == "Scene" tScene = tChap.child(1) - assert tScene.child(0).text(nwTree.C_TITLE) == "Section" + assert tScene.child(0).text(novelTree.C_TITLE) == "Section" ## # Close