Add back extra column content

This commit is contained in:
Veronica Berglyd Olsen
2025-03-23 16:41:40 +01:00
parent 9d63a018e8
commit 95d8e3ac98
7 changed files with 113 additions and 301 deletions
+7 -6
View File
@@ -87,7 +87,7 @@ class Index:
# Storage and State
self._tagsIndex = TagsIndex()
self._itemIndex = ItemIndex(project)
self._itemIndex = ItemIndex(project, self._tagsIndex)
self._indexBroken = False
# Models
@@ -906,10 +906,11 @@ class ItemIndex:
IndexHeading object for each heading of the text.
"""
__slots__ = ("_project", "_items")
__slots__ = ("_project", "_tags", "_items")
def __init__(self, project: NWProject) -> None:
def __init__(self, project: NWProject, tagsIndex: TagsIndex) -> None:
self._project = project
self._tags = tagsIndex
self._items: dict[str, IndexNode] = {}
return
@@ -936,7 +937,7 @@ class ItemIndex:
"""Add a new item to the index. This will overwrite the item if
it already exists.
"""
self._items[tHandle] = IndexNode(tHandle, nwItem)
self._items[tHandle] = IndexNode(self._tags, tHandle, nwItem)
return
def allItemTags(self, tHandle: str) -> list[str]:
@@ -994,7 +995,7 @@ class ItemIndex:
if tHandle in self._items:
tItem = self._items[tHandle]
sTitle = tItem.nextHeading()
tItem.addHeading(IndexHeading(sTitle, lineNo, level, text))
tItem.addHeading(IndexHeading(self._tags, sTitle, lineNo, level, text))
return sTitle
return TT_NONE
@@ -1065,7 +1066,7 @@ class ItemIndex:
nwItem = self._project.tree[tHandle]
if nwItem is not None:
tItem = IndexNode(tHandle, nwItem)
tItem = IndexNode(self._tags, tHandle, nwItem)
tItem.unpackData(tData)
self._items[tHandle] = tItem
+36 -6
View File
@@ -36,6 +36,7 @@ from novelwriter.common import checkInt, isListInstance, isTitleTag
from novelwriter.constants import nwKeyWords, nwStyles
if TYPE_CHECKING: # pragma: no cover
from novelwriter.core.index import TagsIndex
from novelwriter.core.item import NWItem
logger = logging.getLogger(__name__)
@@ -56,12 +57,13 @@ class IndexNode:
must be reset each time the item is re-indexed.
"""
__slots__ = ("_handle", "_item", "_headings", "_count", "_notes")
__slots__ = ("_tags", "_handle", "_item", "_headings", "_notes", "_count")
def __init__(self, tHandle: str, nwItem: NWItem) -> None:
def __init__(self, tagsIndex: TagsIndex, tHandle: str, nwItem: NWItem) -> None:
self._tags = tagsIndex
self._handle = tHandle
self._item = nwItem
self._headings: dict[str, IndexHeading] = {TT_NONE: IndexHeading(TT_NONE)}
self._headings: dict[str, IndexHeading] = {TT_NONE: IndexHeading(self._tags, TT_NONE)}
self._notes: dict[str, set[str]] = {}
self._count = 0
return
@@ -179,7 +181,7 @@ class IndexNode:
"""Unpack an item entry from the data."""
for key, entry in data.items():
if isTitleTag(key):
heading = IndexHeading(key)
heading = IndexHeading(self._tags, key)
heading.unpackData(entry)
self.addHeading(heading)
elif key == "document":
@@ -202,9 +204,16 @@ class IndexHeading:
of all references made under the heading.
"""
__slots__ = ("_key", "_line", "_level", "_title", "_counts", "_tag", "_refs", "_comments")
__slots__ = (
"_tags", "_key", "_line", "_level", "_title",
"_counts", "_tag", "_refs", "_comments",
)
def __init__(self, key: str, line: int = 0, level: str = "H0", title: str = "") -> None:
def __init__(
self, tagsIndex: TagsIndex, key: str, line: int = 0,
level: str = "H0", title: str = "",
) -> None:
self._tags = tagsIndex
self._key = key
self._line = line
self._level = level
@@ -314,6 +323,27 @@ class IndexHeading:
self._refs[tag].add(keyword)
return
##
# Getters
##
def getReferences(self) -> dict[str, list[str]]:
"""Extract all references for this heading."""
refs = {x: [] for x in nwKeyWords.VALID_KEYS}
for tag, types in self._refs.items():
for keyword in types:
if keyword in refs:
refs[keyword].append(self._tags.tagName(tag))
return refs
def getReferencesByKeyword(self, keyword: str) -> list[str]:
"""Extract all references for this heading."""
refs = []
for tag, types in self._refs.items():
if keyword in types:
refs.append(self._tags.tagName(tag))
return refs
##
# Data Methods
##
+17 -9
View File
@@ -30,7 +30,7 @@ from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt
from PyQt6.QtGui import QIcon, QPixmap
from novelwriter import SHARED
from novelwriter.constants import nwKeyWords, nwStyles
from novelwriter.constants import nwKeyWords, nwLabels, nwStyles, trConst
from novelwriter.core.indexdata import IndexHeading, IndexNode
from novelwriter.enum import nwNovelExtra
from novelwriter.error import logException
@@ -52,7 +52,7 @@ T_NodeData = str | tuple[str, str] | QIcon | QPixmap | Qt.AlignmentFlag | None
class NovelModel(QAbstractTableModel):
__slots__ = ("_rows", "_header", "_more", "_columns", "_extra")
__slots__ = ("_rows", "_header", "_more", "_columns", "_extraKey", "_extraLabel")
def __init__(self) -> None:
super().__init__()
@@ -60,7 +60,8 @@ class NovelModel(QAbstractTableModel):
self._header: list[QSize] = []
self._more = SHARED.theme.getIcon("more_arrow")
self._columns = 3
self._extra = ""
self._extraKey = ""
self._extraLabel = ""
return
def __del__(self) -> None: # pragma: no cover
@@ -85,16 +86,20 @@ class NovelModel(QAbstractTableModel):
match extra:
case nwNovelExtra.HIDDEN:
self._columns = 3
self._extra = ""
self._extraKey = ""
self._extraLabel = ""
case nwNovelExtra.POV:
self._columns = 4
self._extra = nwKeyWords.POV_KEY
self._extraKey = nwKeyWords.POV_KEY
self._extraLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY])
case nwNovelExtra.FOCUS:
self._columns = 4
self._extra = nwKeyWords.FOCUS_KEY
self._extraKey = nwKeyWords.FOCUS_KEY
self._extraLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY])
case nwNovelExtra.PLOT:
self._columns = 4
self._extra = nwKeyWords.PLOT_KEY
self._extraKey = nwKeyWords.PLOT_KEY
self._extraLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY])
return
##
@@ -209,6 +214,7 @@ class NovelModel(QAbstractTableModel):
"""Generate a cache entry."""
iLevel = nwStyles.H_LEVEL.get(heading.level, 0)
data = {}
data[C_FACTOR*0 | R_TIP] = heading.title
data[C_FACTOR*0 | R_TEXT] = heading.title
data[C_FACTOR*0 | R_ICON] = SHARED.theme.getHeaderDecoration(iLevel)
data[C_FACTOR*1 | R_TEXT] = f"{heading.mainCount:n}"
@@ -216,8 +222,10 @@ class NovelModel(QAbstractTableModel):
if self._columns == 3:
data[C_FACTOR*2 | R_ICON] = self._more
else:
data[C_FACTOR*2 | R_TIP] = "Hello World"
data[C_FACTOR*2 | R_TEXT] = "Hello World"
if self._extraKey and (refs := heading.getReferencesByKeyword(self._extraKey)):
text = ", ".join(refs)
data[C_FACTOR*2 | R_TEXT] = text
data[C_FACTOR*2 | R_TIP] = f"<b>{self._extraLabel}:</b> {text}"
data[C_FACTOR*3 | R_ICON] = self._more
data[R_HANDLE] = handle
data[R_KEY] = key
+16 -251
View File
@@ -383,14 +383,8 @@ class GuiNovelTree(NTreeView):
self._actHandle = None
self._lastColType = nwNovelExtra.POV
self._lastColSize = 0.25
# self._lastBuild = 0
# self._treeMap: dict[str, QTreeWidgetItem] = {}
# Cached Strings
# self._povLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY])
# self._focLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY])
# self._pltLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY])
# Widget Setup
self.setIconSize(SHARED.theme.baseIconSize)
self.setFrameStyle(QFrame.Shape.NoFrame)
self.setUniformRowHeights(True)
@@ -569,25 +563,26 @@ class GuiNovelTree(NTreeView):
logger.debug("Generating meta data tooltip for '%s:%s'", tHandle, sTitle)
if synopsis := head.synopsis:
label = trConst(nwLabels.OUTLINE_COLS[nwOutline.SYNOP])
synopsis = f"<p><b>{label}</b>: {synopsis}</p>"
synopsis = f"<p><b>{label}:</b> {synopsis}</p>"
def appendTags(refs: dict, key: str, lines: list[str]) -> list[str]:
def appendTags(refs: dict, key: str, lines: list[str]) -> None:
"""Generate a reference list for a given reference key."""
if tags := ", ".join(refs.get(key, [])):
lines.append(f"<b>{trConst(nwLabels.KEY_NAME[key])}</b>: {tags}")
return lines
lines.append(f"<b>{trConst(nwLabels.KEY_NAME[key])}:</b> {tags}")
return
tags = SHARED.project.index.getReferences(tHandle, sTitle)
lines = []
lines = appendTags(tags, nwKeyWords.POV_KEY, lines)
lines = appendTags(tags, nwKeyWords.FOCUS_KEY, lines)
lines = appendTags(tags, nwKeyWords.CHAR_KEY, lines)
lines = appendTags(tags, nwKeyWords.PLOT_KEY, lines)
lines = appendTags(tags, nwKeyWords.TIME_KEY, lines)
lines = appendTags(tags, nwKeyWords.WORLD_KEY, lines)
lines = appendTags(tags, nwKeyWords.OBJECT_KEY, lines)
lines = appendTags(tags, nwKeyWords.ENTITY_KEY, lines)
lines = appendTags(tags, nwKeyWords.CUSTOM_KEY, lines)
if head := SHARED.project.index.getItemHeading(tHandle, sTitle):
tags = head.getReferences()
appendTags(tags, nwKeyWords.POV_KEY, lines)
appendTags(tags, nwKeyWords.FOCUS_KEY, lines)
appendTags(tags, nwKeyWords.CHAR_KEY, lines)
appendTags(tags, nwKeyWords.PLOT_KEY, lines)
appendTags(tags, nwKeyWords.TIME_KEY, lines)
appendTags(tags, nwKeyWords.WORLD_KEY, lines)
appendTags(tags, nwKeyWords.OBJECT_KEY, lines)
appendTags(tags, nwKeyWords.ENTITY_KEY, lines)
appendTags(tags, nwKeyWords.CUSTOM_KEY, lines)
text = ""
if lines:
@@ -596,233 +591,3 @@ class GuiNovelTree(NTreeView):
if tooltip := (text + synopsis or self.tr("No meta data")):
QToolTip.showText(qPos, tooltip)
return
##
# Old Code
##
##
# Class Methods
##
# def clearContent(self) -> None:
# """Clear the GUI content and the related maps."""
# self.clear()
# self._treeMap = {}
# self._lastBuild = 0
# return
# def refreshTree(self, rootHandle: str | None = None, overRide: bool = False) -> None:
# """Refresh the tree if it has been changed."""
# logger.debug("Requesting refresh of the novel tree")
# if rootHandle is None:
# rootHandle = SHARED.project.tree.findRoot(nwItemClass.NOVEL)
# titleKey = None
# if selItems := self.selectedItems():
# titleKey = selItems[0].data(self.C_DATA, self.D_KEY)
# self._populateTree(rootHandle)
# SHARED.project.data.setLastHandle(rootHandle, "novelTree")
# if titleKey is not None and titleKey in self._treeMap:
# self._treeMap[titleKey].setSelected(True)
# return
# def refreshHandle(self, tHandle: str) -> None:
# """Refresh the data for a given handle."""
# if idxData := SHARED.project.index.getItemData(tHandle):
# logger.debug("Refreshing meta data for item '%s'", tHandle)
# for sTitle, tHeading in idxData.items():
# sKey = f"{tHandle}:{sTitle}"
# if trItem := self._treeMap.get(sKey, None):
# self._updateTreeItemValues(trItem, tHeading, tHandle, sTitle)
# else:
# logger.debug("Heading '%s' not in novel tree", sKey)
# self.refreshTree()
# return
# return
# def setLastColType(self, colType: NovelTreeColumn, doRefresh: bool = True) -> None:
# """Change the content type of the last column and rebuild."""
# if self._lastCol != colType:
# logger.debug("Changing last column to %s", colType.name)
# self._lastCol = colType
# self.setColumnHidden(self.C_EXTRA, colType == NovelTreeColumn.HIDDEN)
# if doRefresh:
# lastNovel = SHARED.project.data.getLastHandle("novelTree")
# self.refreshTree(rootHandle=lastNovel, overRide=True)
# return
# def setActiveHandle(self, tHandle: str | None) -> None:
# """Highlight the rows associated with a given handle."""
# didScroll = False
# brushOn = self.palette().alternateBase()
# brushOff = self.palette().base()
# if pHandle := self._actHandle:
# for key, item in self._treeMap.items():
# if key.startswith(pHandle):
# for i in range(self.columnCount()):
# item.setBackground(i, brushOff)
# if tHandle:
# for key, item in self._treeMap.items():
# if key.startswith(tHandle):
# for i in range(self.columnCount()):
# item.setBackground(i, brushOn)
# if not didScroll:
# self.scrollToItem(item, QAbstractItemView.ScrollHint.PositionAtCenter)
# didScroll = True
# self._actHandle = tHandle or None
# return
##
# Events
##
# def mousePressEvent(self, event: QMouseEvent) -> None:
# """Overload mousePressEvent to clear selection if clicking the
# mouse in a blank area of the tree view, and to load a document
# for viewing if the user middle-clicked.
# """
# super().mousePressEvent(event)
# if event.button() == QtMouseLeft:
# selItem = self.indexAt(event.pos())
# if not selItem.isValid():
# self.clearSelection()
# elif event.button() == QtMouseMiddle:
# selItem = self.itemAt(event.pos())
# if not isinstance(selItem, QTreeWidgetItem):
# return
# tHandle, sTitle = self.getSelectedHandle()
# if tHandle is None:
# return
# self.novelView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, sTitle or "", False)
# return
# def focusOutEvent(self, event: QFocusEvent) -> None:
# """Clear the selection when the tree no longer has focus."""
# super().focusOutEvent(event)
# self.clearSelection()
# return
##
# Private Slots
##
# @pyqtSlot("QModelIndex")
# def _treeItemClicked(self, index: QModelIndex) -> None:
# """The user clicked on an item in the tree."""
# if index.column() == self.C_MORE:
# tHandle = index.siblingAtColumn(self.C_DATA).data(self.D_HANDLE)
# sTitle = index.siblingAtColumn(self.C_DATA).data(self.D_TITLE)
# tipPos = self.mapToGlobal(self.visualRect(index).topRight())
# self._popMetaBox(tipPos, tHandle, sTitle)
# return
# @pyqtSlot()
# def _treeSelectionChange(self) -> None:
# """Extract the handle and line number of the currently selected
# title, and send it to the tree meta panel.
# """
# tHandle, _ = self.getSelectedHandle()
# if tHandle is not None:
# self.novelView.selectedItemChanged.emit(tHandle)
# return
# @pyqtSlot("QTreeWidgetItem*", int)
# def _treeDoubleClick(self, item: QTreeWidgetItem, column: int) -> None:
# """Extract the handle and line number of the title double-
# clicked, and send it to the main gui class for opening in the
# document editor.
# """
# tHandle, sTitle = self.getSelectedHandle()
# self.novelView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, sTitle or "", True)
# return
##
# Internal Functions
##
# def _populateTree(self, rootHandle: str | None) -> None:
# """Build the tree based on the project index."""
# self.clearContent()
# tStart = time()
# logger.debug("Building novel tree for root item '%s'", rootHandle)
# novStruct = SHARED.project.index.novelStructure(rootHandle=rootHandle, activeOnly=True)
# for tKey, tHandle, sTitle, novIdx in novStruct:
# if novIdx.level == "H0":
# continue
# newItem = QTreeWidgetItem()
# 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_KEY, tKey)
# newItem.setTextAlignment(self.C_WORDS, QtAlignRight)
# self._updateTreeItemValues(newItem, novIdx, tHandle, sTitle)
# self._treeMap[tKey] = newItem
# self.addTopLevelItem(newItem)
# self.setActiveHandle(self._actHandle)
# logger.debug("Novel Tree built in %.3f ms", (time() - tStart)*1000)
# self._lastBuild = time()
# return
# def _updateTreeItemValues(
# self, trItem: QTreeWidgetItem, idxItem: IndexHeading, tHandle: str, sTitle: str
# ) -> None:
# """Set the tree item values from the index entry."""
# iLevel = nwStyles.H_LEVEL.get(idxItem.level, 0)
# hDec = SHARED.theme.getHeaderDecoration(iLevel)
# trItem.setData(self.C_TITLE, QtDecoration, hDec)
# trItem.setText(self.C_TITLE, idxItem.title)
# trItem.setFont(self.C_TITLE, self._hFonts[iLevel])
# trItem.setText(self.C_WORDS, f"{idxItem.wordCount:n}")
# trItem.setData(self.C_MORE, QtDecoration, self._pMore)
# # Custom column
# viewport = self.viewport()
# mW = int(self._lastColSize * (viewport.width() if viewport else 100))
# lastText, toolTip = self._getLastColumnText(tHandle, sTitle)
# elideText = self.fontMetrics().elidedText(lastText, Qt.TextElideMode.ElideRight, mW)
# trItem.setText(self.C_EXTRA, elideText)
# trItem.setData(self.C_DATA, self.D_EXTRA, lastText)
# trItem.setToolTip(self.C_EXTRA, toolTip)
# return
# def _getLastColumnText(self, tHandle: str, sTitle: str) -> tuple[str, str]:
# """Generate text for the last column based on user settings."""
# if self._lastCol == NovelTreeColumn.HIDDEN:
# return "", ""
# refData = []
# refName = ""
# refs = SHARED.project.index.getReferences(tHandle, sTitle)
# if self._lastCol == NovelTreeColumn.POV:
# refData = refs[nwKeyWords.POV_KEY]
# refName = self._povLabel
# elif self._lastCol == NovelTreeColumn.FOCUS:
# refData = refs[nwKeyWords.FOCUS_KEY]
# refName = self._focLabel
# elif self._lastCol == NovelTreeColumn.PLOT:
# refData = refs[nwKeyWords.PLOT_KEY]
# refName = self._pltLabel
# if refData:
# toolText = ", ".join(refData)
# return toolText, f"{refName}: {toolText}"
# return "", ""
+23 -15
View File
@@ -22,6 +22,7 @@ from __future__ import annotations
import pytest
from novelwriter.core.index import TagsIndex
from novelwriter.core.indexdata import IndexHeading, IndexNode
from novelwriter.core.item import NWItem
from novelwriter.core.project import NWProject
@@ -33,9 +34,10 @@ def testCoreIndexData_IndexNode(mockGUI):
handle = "0123456789abc"
project = NWProject()
item = NWItem(project, handle)
tags = TagsIndex()
# Defaults
node = IndexNode(handle, item)
node = IndexNode(tags, handle, item)
assert node.handle == handle
assert node.item is item
assert str(node) == f"<IndexNode handle='{handle}'>"
@@ -44,8 +46,8 @@ def testCoreIndexData_IndexNode(mockGUI):
assert "T0000" in node # Placeholder heading
# Add a heading
head1 = IndexHeading(node.nextHeading(), line=1, level="H1", title="Heading 1")
head2 = IndexHeading(node.nextHeading(), line=10, level="H2", title="Heading 2")
head1 = IndexHeading(tags, node.nextHeading(), line=1, level="H1", title="Heading 1")
head2 = IndexHeading(tags, node.nextHeading(), line=10, level="H2", title="Heading 2")
node.addHeading(head1)
node.addHeading(head2)
assert len(node) == 2
@@ -105,11 +107,12 @@ def testCoreIndexData_IndexNodePackUnpack(mockGUI):
handle = "0123456789abc"
project = NWProject()
item = NWItem(project, handle)
node = IndexNode(handle, item)
tags = TagsIndex()
node = IndexNode(tags, handle, item)
# Add some headings and notes
head1 = IndexHeading(node.nextHeading(), line=1, level="H1", title="Heading 1")
head2 = IndexHeading(node.nextHeading(), line=10, level="H2", title="Heading 2")
head1 = IndexHeading(tags, node.nextHeading(), line=1, level="H1", title="Heading 1")
head2 = IndexHeading(tags, node.nextHeading(), line=10, level="H2", title="Heading 2")
node.addHeading(head1)
node.addHeading(head2)
node.setHeadingCounts("T0001", 42, 13, 3)
@@ -128,7 +131,7 @@ def testCoreIndexData_IndexNodePackUnpack(mockGUI):
assert set(data["document"]["footnotes"]) == {"key1", "key2"}
# Create a new node
new = IndexNode(handle, item)
new = IndexNode(tags, handle, item)
# Unpack heading one
data = {"T0001": {"meta": {
@@ -165,7 +168,8 @@ def testCoreIndexData_IndexNodePackUnpack(mockGUI):
def testCoreIndexData_IndexHeading():
"""Test the IndexHeading class."""
# Defaults
head = IndexHeading("T0001")
tags = TagsIndex()
head = IndexHeading(tags, "T0001")
assert str(head) == "<IndexHeading key='T0001'>"
assert repr(head) == "<IndexHeading key='T0001'>"
assert head.key == "T0001"
@@ -234,11 +238,13 @@ def testCoreIndexData_IndexHeading():
@pytest.mark.core
def testCoreIndexData_IndexHeadingUnpackMeta():
"""Test IndexHeading class meta unpacking."""
tags = TagsIndex()
# Valid
data = {"meta": {
"level": "H1", "title": "So it Begins", "line": 1, "tag": "begins", "counts": [95, 18, 1]
}}
head = IndexHeading("T0001")
head = IndexHeading(tags, "T0001")
head.unpackData(data)
assert head.level == "H1"
assert head.title == "So it Begins"
@@ -252,7 +258,7 @@ def testCoreIndexData_IndexHeadingUnpackMeta():
data = {"meta": {
"level": "H9", "title": None, "line": None, "tag": None, "counts": [42]
}}
head = IndexHeading("T0001")
head = IndexHeading(tags, "T0001")
head.unpackData(data)
assert head.level == "H0"
assert head.title == "None"
@@ -264,7 +270,7 @@ def testCoreIndexData_IndexHeadingUnpackMeta():
# Empty
data = {"meta": {}}
head = IndexHeading("T0001")
head = IndexHeading(tags, "T0001")
head.unpackData(data)
assert head.level == "H0"
assert head.title == ""
@@ -278,11 +284,13 @@ def testCoreIndexData_IndexHeadingUnpackMeta():
@pytest.mark.core
def testCoreIndexData_IndexHeadingUnpackRefs():
"""Test IndexHeading class refs unpacking."""
tags = TagsIndex()
# Valid
data = {"refs": {
"jane": "@char,@pov", "john": "@char", "earth": "@location", "space": "@mention,@location"
}}
head = IndexHeading("T0001")
head = IndexHeading(tags, "T0001")
head.unpackData(data)
assert head.references["jane"] == {"@char", "@pov"}
assert head.references["john"] == {"@char"}
@@ -291,18 +299,18 @@ def testCoreIndexData_IndexHeadingUnpackRefs():
# Invalid key
data = {"refs": {0: "@char,@pov"}}
head = IndexHeading("T0001")
head = IndexHeading(tags, "T0001")
with pytest.raises(ValueError, match="Heading reference key must be a string"):
head.unpackData(data)
# Invalid value
data = {"refs": {"jane": None}}
head = IndexHeading("T0001")
head = IndexHeading(tags, "T0001")
with pytest.raises(ValueError, match="Heading reference value must be a string"):
head.unpackData(data)
# Invalid keyword
data = {"refs": {"jane": "@char,@pov,@stuff"}}
head = IndexHeading("T0001")
head = IndexHeading(tags, "T0001")
with pytest.raises(ValueError, match="Heading reference contains an invalid keyword"):
head.unpackData(data)
+4 -4
View File
@@ -27,7 +27,7 @@ import pytest
from novelwriter.constants import nwFiles
from novelwriter.core.options import OptionState
from novelwriter.core.project import NWProject
from novelwriter.gui.noveltree import NovelTreeColumn
from novelwriter.enum import nwNovelExtra
from tests.mocked import causeOSError
@@ -110,7 +110,7 @@ def testCoreOptions_SetGet(mockGUI):
project = NWProject()
options = OptionState(project)
nwColHidden = NovelTreeColumn.HIDDEN
nwColHidden = nwNovelExtra.HIDDEN
# Set invalid values
assert options.setValue("MockGroup", "mockItem", None) is False
@@ -141,7 +141,7 @@ def testCoreOptions_SetGet(mockGUI):
assert options.getFloat("GuiNovelDetails", "mockItem", None) is None # type: ignore
assert options.getBool("GuiNovelDetails", "clearDouble", None) is True # type: ignore
assert options.getBool("GuiNovelDetails", "mockItem", None) is None # type: ignore
assert options.getEnum("GuiNovelView", "lastCol", NovelTreeColumn, nwColHidden) == nwColHidden
assert options.getEnum("GuiNovelView", "lastCol", nwNovelExtra, nwColHidden) == nwColHidden
# Get from non-existent groups
assert options.getValue("SomeGroup", "mockItem", None) is None
@@ -149,4 +149,4 @@ def testCoreOptions_SetGet(mockGUI):
assert options.getInt("SomeGroup", "mockItem", None) is None # type: ignore
assert options.getFloat("SomeGroup", "mockItem", None) is None # type: ignore
assert options.getBool("SomeGroup", "mockItem", None) is None # type: ignore
assert options.getEnum("SomeGroup", "mockItem", NovelTreeColumn, None) is None # type: ignore
assert options.getEnum("SomeGroup", "mockItem", nwNovelExtra, None) is None # type: ignore
+10 -10
View File
@@ -30,8 +30,8 @@ from PyQt6.QtWidgets import QInputDialog, QToolTip
from novelwriter import CONFIG, SHARED
from novelwriter.dialogs.editlabel import GuiEditLabel
from novelwriter.enum import nwFocus, nwItemType
from novelwriter.gui.noveltree import GuiNovelTree, NovelTreeColumn
from novelwriter.enum import nwFocus, nwItemType, nwNovelExtra
from novelwriter.gui.noveltree import GuiNovelTree
from novelwriter.types import QtMouseLeft, QtMouseMiddle
from tests.tools import C, buildTestProject
@@ -143,28 +143,28 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
# Last Column
# ===========
novelBar.setLastColType(NovelTreeColumn.HIDDEN)
novelBar.setLastColType(nwNovelExtra.HIDDEN)
assert novelTree.isColumnHidden(novelTree.C_EXTRA) is True
assert novelTree.lastColType == NovelTreeColumn.HIDDEN
assert novelTree.lastColType == nwNovelExtra.HIDDEN
assert novelTree._getLastColumnText(C.hSceneDoc, "T0001") == ("", "")
novelBar.setLastColType(NovelTreeColumn.PLOT)
novelBar.setLastColType(nwNovelExtra.PLOT)
assert novelTree.isColumnHidden(novelTree.C_EXTRA) is False
assert novelTree.lastColType == NovelTreeColumn.PLOT
assert novelTree.lastColType == nwNovelExtra.PLOT
assert novelTree._getLastColumnText(C.hSceneDoc, "T0001") == (
"", ""
)
novelBar.setLastColType(NovelTreeColumn.FOCUS)
novelBar.setLastColType(nwNovelExtra.FOCUS)
assert novelTree.isColumnHidden(novelTree.C_EXTRA) is False
assert novelTree.lastColType == NovelTreeColumn.FOCUS
assert novelTree.lastColType == nwNovelExtra.FOCUS
assert novelTree._getLastColumnText(C.hSceneDoc, "T0001") == (
"Jane", "Focus: Jane"
)
novelBar.setLastColType(NovelTreeColumn.POV)
novelBar.setLastColType(nwNovelExtra.POV)
assert novelTree.isColumnHidden(novelTree.C_EXTRA) is False
assert novelTree.lastColType == NovelTreeColumn.POV
assert novelTree.lastColType == nwNovelExtra.POV
assert novelTree._getLastColumnText(C.hSceneDoc, "T0001") == (
"Jane", "Point of View: Jane"
)