From 7f49979bc9bccd8355b5eaa2c74825bdd94859b9 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 31 Oct 2024 17:30:49 +0100
Subject: [PATCH 1/5] Clean up project tree class
---
novelwriter/gui/noveltree.py | 11 +-
novelwriter/gui/projtree.py | 234 +++++++++++++--------------
tests/test_gui/test_gui_docviewer.py | 2 +-
tests/test_gui/test_gui_projtree.py | 24 ++-
4 files changed, 134 insertions(+), 137 deletions(-)
diff --git a/novelwriter/gui/noveltree.py b/novelwriter/gui/noveltree.py
index 8db8a1e0..aa5adb2f 100644
--- a/novelwriter/gui/noveltree.py
+++ b/novelwriter/gui/noveltree.py
@@ -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")
diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py
index 6bcae09d..d335f99b 100644
--- a/novelwriter/gui/projtree.py
+++ b/novelwriter/gui/projtree.py
@@ -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."""
@@ -1658,7 +1651,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 +1777,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 +1813,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 +1840,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 +1849,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 +1862,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 +1913,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 +1949,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
diff --git a/tests/test_gui/test_gui_docviewer.py b/tests/test_gui/test_gui_docviewer.py
index 14082922..30b0f89e 100644
--- a/tests/test_gui/test_gui_docviewer.py
+++ b/tests/test_gui/test_gui_docviewer.py
@@ -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
diff --git a/tests/test_gui/test_gui_projtree.py b/tests/test_gui/test_gui_projtree.py
index 7f663698..cc0a96cb 100644
--- a/tests/test_gui/test_gui_projtree.py
+++ b/tests/test_gui/test_gui_projtree.py
@@ -21,6 +21,7 @@ along with this program. If not, see .
from __future__ import annotations
from pathlib import Path
+from unittest.mock import MagicMock
import pytest
@@ -1146,30 +1147,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
# =============
From 55d26052223680e01938a0566b31044a6bbe8129 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 31 Oct 2024 17:31:58 +0100
Subject: [PATCH 2/5] Use the new qtLambda wrapper where appropriate
---
novelwriter/common.py | 2 +-
novelwriter/dialogs/projectsettings.py | 6 ++--
novelwriter/gui/docviewer.py | 17 ++++++------
novelwriter/gui/noveltree.py | 4 +--
novelwriter/gui/sidebar.py | 9 +++---
novelwriter/tools/manussettings.py | 38 +++++++++++++-------------
novelwriter/tools/writingstats.py | 6 ++--
7 files changed, 42 insertions(+), 40 deletions(-)
diff --git a/novelwriter/common.py b/novelwriter/common.py
index bf150794..92b304e7 100644
--- a/novelwriter/common.py
+++ b/novelwriter/common.py
@@ -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
diff --git a/novelwriter/dialogs/projectsettings.py b/novelwriter/dialogs/projectsettings.py
index 7e7b4f4a..6cbff7d2 100644
--- a/novelwriter/dialogs/projectsettings.py
+++ b/novelwriter/dialogs/projectsettings.py
@@ -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)
diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py
index a304b12e..aa6248e2 100644
--- a/novelwriter/gui/docviewer.py
+++ b/novelwriter/gui/docviewer.py
@@ -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
diff --git a/novelwriter/gui/noveltree.py b/novelwriter/gui/noveltree.py
index aa5adb2f..795622c0 100644
--- a/novelwriter/gui/noveltree.py
+++ b/novelwriter/gui/noveltree.py
@@ -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
diff --git a/novelwriter/gui/sidebar.py b/novelwriter/gui/sidebar.py
index 6f4669c7..124ecf83 100644
--- a/novelwriter/gui/sidebar.py
+++ b/novelwriter/gui/sidebar.py
@@ -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
@@ -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")))
diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py
index 350e48a4..098b1ce7 100644
--- a/novelwriter/tools/manussettings.py
+++ b/novelwriter/tools/manussettings.py
@@ -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)
diff --git a/novelwriter/tools/writingstats.py b/novelwriter/tools/writingstats.py
index 864ad177..0ba1d52f 100644
--- a/novelwriter/tools/writingstats.py
+++ b/novelwriter/tools/writingstats.py
@@ -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
From ccb5f8418b2a383db50fba3abc9bf505f178f8e1 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 31 Oct 2024 18:28:35 +0100
Subject: [PATCH 3/5] Remove redundant function in project tree
---
novelwriter/gui/projtree.py | 5 -----
tests/test_gui/test_gui_projtree.py | 4 ----
2 files changed, 9 deletions(-)
diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py
index d335f99b..843eb687 100644
--- a/novelwriter/gui/projtree.py
+++ b/novelwriter/gui/projtree.py
@@ -1390,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
diff --git a/tests/test_gui/test_gui_projtree.py b/tests/test_gui/test_gui_projtree.py
index cc0a96cb..af14b26c 100644
--- a/tests/test_gui/test_gui_projtree.py
+++ b/tests/test_gui/test_gui_projtree.py
@@ -108,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")
@@ -118,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")
@@ -131,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)
@@ -141,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")
From dd09d3af2f2d7c5abfc3c62aaf2d10a59bf45dab Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 31 Oct 2024 18:28:58 +0100
Subject: [PATCH 4/5] Refactor outline details panel code
---
novelwriter/core/options.py | 3 +
novelwriter/gui/itemdetails.py | 12 +-
novelwriter/gui/outline.py | 229 +++++++++--------------------
tests/test_gui/test_gui_outline.py | 3 +-
4 files changed, 79 insertions(+), 168 deletions(-)
diff --git a/novelwriter/core/options.py b/novelwriter/core/options.py
index 9f813abc..419e4ec2 100644
--- a/novelwriter/core/options.py
+++ b/novelwriter/core/options.py
@@ -73,6 +73,9 @@ VALID_MAP: dict[str, set[str]] = {
"widthCol3", "widthCol4", "wordsPerPage", "countFrom", "clearDouble",
"novelRoot",
},
+ "GuiOutlineDetails": {
+ "detailsWidth", "tagsWidth",
+ }
}
diff --git a/novelwriter/gui/itemdetails.py b/novelwriter/gui/itemdetails.py
index 73542889..6d67d74c 100644
--- a/novelwriter/gui/itemdetails.py
+++ b/novelwriter/gui/itemdetails.py
@@ -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)
diff --git a/novelwriter/gui/outline.py b/novelwriter/gui/outline.py
index 54b8102e..a55c61ce 100644
--- a/novelwriter/gui/outline.py
+++ b/novelwriter/gui/outline.py
@@ -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,6 +936,28 @@ 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"))
@@ -1034,17 +968,10 @@ class GuiOutlineDetails(QScrollArea):
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("")
+
+ for _, value in self.tagValues.values():
+ value.setText("")
+
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
diff --git a/tests/test_gui/test_gui_outline.py b/tests/test_gui/test_gui_outline.py
index 3dc6b32b..dabd9cf0 100644
--- a/tests/test_gui/test_gui_outline.py
+++ b/tests/test_gui/test_gui_outline.py
@@ -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() == "Bod"
+ assert outlineData.tagValues[nwKeyWords.POV_KEY][1].text() == "Bod"
outlineView._tagClicked("Bod")
assert nwGUI.docViewer.docHandle == "4c4f28287af27"
From bbb07c9963d9de03d15322f3f66f96bc127ed891 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 31 Oct 2024 18:35:28 +0100
Subject: [PATCH 5/5] Make some minor GUI tweaks
---
novelwriter/gui/outline.py | 16 ++++++++--------
novelwriter/gui/sidebar.py | 4 ++--
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/novelwriter/gui/outline.py b/novelwriter/gui/outline.py
index a55c61ce..ee0e1c9f 100644
--- a/novelwriter/gui/outline.py
+++ b/novelwriter/gui/outline.py
@@ -961,16 +961,16 @@ class GuiOutlineDetails(QScrollArea):
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.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.setText("")
+ value.clear()
self.updateClasses()
return
diff --git a/novelwriter/gui/sidebar.py b/novelwriter/gui/sidebar.py
index 124ecf83..7fccd311 100644
--- a/novelwriter/gui/sidebar.py
+++ b/novelwriter/gui/sidebar.py
@@ -55,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)
@@ -114,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()