Clean up novel view and improve coverage

This commit is contained in:
Veronica Berglyd Olsen
2023-11-29 21:24:07 +01:00
parent ce666d9d7a
commit a31f42b04c
2 changed files with 58 additions and 46 deletions
+36 -44
View File
@@ -206,18 +206,18 @@ class GuiNovelToolBar(QWidget):
# Novel Selector # Novel Selector
selFont = self.font() selFont = self.font()
selFont.setWeight(QFont.Bold) selFont.setWeight(QFont.Weight.Bold)
self.novelPrefix = self.tr("Outline of {0}") self.novelPrefix = self.tr("Outline of {0}")
self.novelValue = NovelSelector(self) self.novelValue = NovelSelector(self)
self.novelValue.setFont(selFont) self.novelValue.setFont(selFont)
self.novelValue.setMinimumWidth(CONFIG.pxInt(150)) self.novelValue.setMinimumWidth(CONFIG.pxInt(150))
self.novelValue.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.novelValue.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self.novelValue.novelSelectionChanged.connect(self.setCurrentRoot) self.novelValue.novelSelectionChanged.connect(self.setCurrentRoot)
self.tbNovel = QToolButton(self) self.tbNovel = QToolButton(self)
self.tbNovel.setToolTip(self.tr("Novel Root")) self.tbNovel.setToolTip(self.tr("Novel Root"))
self.tbNovel.setIconSize(QSize(iPx, iPx)) self.tbNovel.setIconSize(QSize(iPx, iPx))
self.tbNovel.clicked.connect(self._openNovelSelector) self.tbNovel.clicked.connect(self.novelValue.showPopup)
# Refresh Button # Refresh Button
self.tbRefresh = QToolButton(self) self.tbRefresh = QToolButton(self)
@@ -244,7 +244,7 @@ class GuiNovelToolBar(QWidget):
self.tbMore.setToolTip(self.tr("More Options")) self.tbMore.setToolTip(self.tr("More Options"))
self.tbMore.setIconSize(QSize(iPx, iPx)) self.tbMore.setIconSize(QSize(iPx, iPx))
self.tbMore.setMenu(self.mMore) self.tbMore.setMenu(self.mMore)
self.tbMore.setPopupMode(QToolButton.InstantPopup) self.tbMore.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
# Assemble # Assemble
self.outerBox = QHBoxLayout() self.outerBox = QHBoxLayout()
@@ -275,7 +275,7 @@ class GuiNovelToolBar(QWidget):
self.tbMore.setIcon(SHARED.theme.getIcon("menu")) self.tbMore.setIcon(SHARED.theme.getIcon("menu"))
qPalette = self.palette() qPalette = self.palette()
qPalette.setBrush(QPalette.Window, qPalette.base()) qPalette.setBrush(QPalette.ColorRole.Window, qPalette.base())
self.setPalette(qPalette) self.setPalette(qPalette)
# StyleSheets # StyleSheets
@@ -327,12 +327,6 @@ class GuiNovelToolBar(QWidget):
# Private Slots # Private Slots
## ##
@pyqtSlot()
def _openNovelSelector(self) -> None:
"""Trigger the dropdown list of the novel selector."""
self.novelValue.showPopup()
return
@pyqtSlot() @pyqtSlot()
def _refreshNovelTree(self) -> None: def _refreshNovelTree(self) -> None:
"""Rebuild the current tree.""" """Rebuild the current tree."""
@@ -408,14 +402,14 @@ class GuiNovelTree(QTreeWidget):
cMg = CONFIG.pxInt(6) cMg = CONFIG.pxInt(6)
self.setIconSize(QSize(iPx, iPx)) self.setIconSize(QSize(iPx, iPx))
self.setFrameStyle(QFrame.NoFrame) self.setFrameStyle(QFrame.Shape.NoFrame)
self.setUniformRowHeights(True) self.setUniformRowHeights(True)
self.setAllColumnsShowFocus(True) self.setAllColumnsShowFocus(True)
self.setHeaderHidden(True) self.setHeaderHidden(True)
self.setIndentation(0) self.setIndentation(0)
self.setColumnCount(4) self.setColumnCount(4)
self.setSelectionBehavior(QAbstractItemView.SelectRows) self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.setSelectionMode(QAbstractItemView.SingleSelection) self.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
self.setExpandsOnDoubleClick(False) self.setExpandsOnDoubleClick(False)
self.setDragEnabled(False) self.setDragEnabled(False)
@@ -423,10 +417,10 @@ class GuiNovelTree(QTreeWidget):
treeHeader = self.header() treeHeader = self.header()
treeHeader.setStretchLastSection(False) treeHeader.setStretchLastSection(False)
treeHeader.setMinimumSectionSize(iPx + cMg) treeHeader.setMinimumSectionSize(iPx + cMg)
treeHeader.setSectionResizeMode(self.C_TITLE, QHeaderView.Stretch) treeHeader.setSectionResizeMode(self.C_TITLE, QHeaderView.ResizeMode.Stretch)
treeHeader.setSectionResizeMode(self.C_WORDS, QHeaderView.ResizeToContents) treeHeader.setSectionResizeMode(self.C_WORDS, QHeaderView.ResizeMode.ResizeToContents)
treeHeader.setSectionResizeMode(self.C_EXTRA, QHeaderView.ResizeToContents) treeHeader.setSectionResizeMode(self.C_EXTRA, QHeaderView.ResizeMode.ResizeToContents)
treeHeader.setSectionResizeMode(self.C_MORE, QHeaderView.ResizeToContents) treeHeader.setSectionResizeMode(self.C_MORE, QHeaderView.ResizeMode.ResizeToContents)
# Pre-Generate Tree Formatting # Pre-Generate Tree Formatting
fH1 = self.font() fH1 = self.font()
@@ -455,14 +449,14 @@ class GuiNovelTree(QTreeWidget):
"""Set or update tree widget settings.""" """Set or update tree widget settings."""
# Scroll bars # Scroll bars
if CONFIG.hideVScroll: if CONFIG.hideVScroll:
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
else: else:
self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
if CONFIG.hideHScroll: if CONFIG.hideHScroll:
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
else: else:
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
return return
@@ -522,21 +516,16 @@ class GuiNovelTree(QTreeWidget):
def refreshHandle(self, tHandle: str) -> None: def refreshHandle(self, tHandle: str) -> None:
"""Refresh the data for a given handle.""" """Refresh the data for a given handle."""
idxData = SHARED.project.index.getItemData(tHandle) if idxData := SHARED.project.index.getItemData(tHandle):
if idxData is None: logger.debug("Refreshing meta data for item '%s'", tHandle)
return for sTitle, tHeading in idxData.items():
sKey = f"{tHandle}:{sTitle}"
logger.debug("Refreshing meta data for item '%s'", tHandle) if trItem := self._treeMap.get(sKey, None):
for sTitle, tHeading in idxData.items(): self._updateTreeItemValues(trItem, tHeading, tHandle, sTitle)
sKey = f"{tHandle}:{sTitle}" else:
trItem = self._treeMap.get(sKey, None) logger.debug("Heading '%s' not in novel tree", sKey)
if trItem is None: self.refreshTree()
logger.debug("Heading '%s' not in novel tree", sKey) return
self.refreshTree()
return
self._updateTreeItemValues(trItem, tHeading, tHandle, sTitle)
return return
def getSelectedHandle(self) -> tuple[str | None, str | None]: def getSelectedHandle(self) -> tuple[str | None, str | None]:
@@ -599,12 +588,12 @@ class GuiNovelTree(QTreeWidget):
""" """
super().mousePressEvent(event) super().mousePressEvent(event)
if event.button() == Qt.LeftButton: if event.button() == Qt.MouseButton.LeftButton:
selItem = self.indexAt(event.pos()) selItem = self.indexAt(event.pos())
if not selItem.isValid(): if not selItem.isValid():
self.clearSelection() self.clearSelection()
elif event.button() == Qt.MiddleButton: elif event.button() == Qt.MouseButton.MiddleButton:
selItem = self.itemAt(event.pos()) selItem = self.itemAt(event.pos())
if not isinstance(selItem, QTreeWidgetItem): if not isinstance(selItem, QTreeWidgetItem):
return return
@@ -635,7 +624,10 @@ class GuiNovelTree(QTreeWidget):
trItem = self.topLevelItem(i) trItem = self.topLevelItem(i)
if isinstance(trItem, QTreeWidgetItem): if isinstance(trItem, QTreeWidgetItem):
lastText = trItem.data(self.C_DATA, self.D_EXTRA) lastText = trItem.data(self.C_DATA, self.D_EXTRA)
trItem.setText(self.C_EXTRA, fMetric.elidedText(lastText, Qt.ElideRight, eliW)) trItem.setText(
self.C_EXTRA,
fMetric.elidedText(lastText, Qt.TextElideMode.ElideRight, eliW)
)
return return
## ##
@@ -691,7 +683,7 @@ class GuiNovelTree(QTreeWidget):
newItem.setData(self.C_DATA, self.D_HANDLE, tHandle) newItem.setData(self.C_DATA, self.D_HANDLE, tHandle)
newItem.setData(self.C_DATA, self.D_TITLE, sTitle) newItem.setData(self.C_DATA, self.D_TITLE, sTitle)
newItem.setData(self.C_DATA, self.D_KEY, tKey) newItem.setData(self.C_DATA, self.D_KEY, tKey)
newItem.setTextAlignment(self.C_WORDS, Qt.AlignRight) newItem.setTextAlignment(self.C_WORDS, Qt.AlignmentFlag.AlignRight)
self._updateTreeItemValues(newItem, novIdx, tHandle, sTitle) self._updateTreeItemValues(newItem, novIdx, tHandle, sTitle)
self._treeMap[tKey] = newItem self._treeMap[tKey] = newItem
@@ -710,16 +702,16 @@ class GuiNovelTree(QTreeWidget):
iLevel = nwHeaders.H_LEVEL.get(idxItem.level, 0) iLevel = nwHeaders.H_LEVEL.get(idxItem.level, 0)
hDec = SHARED.theme.getHeaderDecoration(iLevel) hDec = SHARED.theme.getHeaderDecoration(iLevel)
trItem.setData(self.C_TITLE, Qt.DecorationRole, hDec) trItem.setData(self.C_TITLE, Qt.ItemDataRole.DecorationRole, hDec)
trItem.setText(self.C_TITLE, idxItem.title) trItem.setText(self.C_TITLE, idxItem.title)
trItem.setFont(self.C_TITLE, self._hFonts[iLevel]) trItem.setFont(self.C_TITLE, self._hFonts[iLevel])
trItem.setText(self.C_WORDS, f"{idxItem.wordCount:n}") trItem.setText(self.C_WORDS, f"{idxItem.wordCount:n}")
trItem.setData(self.C_MORE, Qt.DecorationRole, self._pMore) trItem.setData(self.C_MORE, Qt.ItemDataRole.DecorationRole, self._pMore)
# Custom column # Custom column
mW = int(self._lastColSize * self.viewport().width()) mW = int(self._lastColSize * self.viewport().width())
lastText, toolTip = self._getLastColumnText(tHandle, sTitle) lastText, toolTip = self._getLastColumnText(tHandle, sTitle)
elideText = self.fontMetrics().elidedText(lastText, Qt.ElideRight, mW) elideText = self.fontMetrics().elidedText(lastText, Qt.TextElideMode.ElideRight, mW)
trItem.setText(self.C_EXTRA, elideText) trItem.setText(self.C_EXTRA, elideText)
trItem.setData(self.C_DATA, self.D_EXTRA, lastText) trItem.setData(self.C_DATA, self.D_EXTRA, lastText)
trItem.setToolTip(self.C_EXTRA, toolTip) trItem.setToolTip(self.C_EXTRA, toolTip)
+22 -2
View File
@@ -27,12 +27,12 @@ from pathlib import Path
from tools import C, buildTestProject from tools import C, buildTestProject
from PyQt5.QtGui import QFocusEvent from PyQt5.QtGui import QFocusEvent
from PyQt5.QtCore import Qt, QEvent from PyQt5.QtCore import QPoint, Qt, QEvent
from PyQt5.QtWidgets import QInputDialog, QToolTip from PyQt5.QtWidgets import QInputDialog, QToolTip
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwWidget, nwItemType from novelwriter.enum import nwWidget, nwItemType
from novelwriter.gui.noveltree import NovelTreeColumn from novelwriter.gui.noveltree import GuiNovelTree, NovelTreeColumn
from novelwriter.dialogs.editlabel import GuiEditLabel from novelwriter.dialogs.editlabel import GuiEditLabel
@@ -192,12 +192,32 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
mIndex = novelTree.model().index(2, novelTree.C_MORE) mIndex = novelTree.model().index(2, novelTree.C_MORE)
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(QToolTip, "showText", showText) mp.setattr(QToolTip, "showText", showText)
ttText = ""
novelTree._treeItemClicked(mIndex) novelTree._treeItemClicked(mIndex)
assert ttText == ( assert ttText == (
"<p><b>Point of View</b>: Jane<br><b>Focus</b>: Jane</p>" "<p><b>Point of View</b>: Jane<br><b>Focus</b>: Jane</p>"
"<p><b>Synopsis</b>: This is a scene.</p>" "<p><b>Synopsis</b>: This is a scene.</p>"
) )
ttText = ""
novelTree._popMetaBox(QPoint(1, 1), C.hInvalid, "T0001")
assert ttText == ""
# Set Default Root
# ================
SHARED.project.data.setLastHandle(C.hInvalid, "novelTree")
novelView.openProjectTasks()
assert novelBar.novelValue.handle == C.hNovelRoot
# Tree Focus
# ==========
with monkeypatch.context() as mp:
mp.setattr(GuiNovelTree, "hasFocus", lambda *a: False)
assert novelView.treeHasFocus() is False
mp.setattr(GuiNovelTree, "hasFocus", lambda *a: True)
assert novelView.treeHasFocus() is True
# Other Checks # Other Checks
# ============ # ============