Add back extra column GUI handling

This commit is contained in:
Veronica Berglyd Olsen
2025-03-23 00:42:35 +01:00
parent 04c6847590
commit 9d63a018e8
4 changed files with 143 additions and 93 deletions
+16 -3
View File
@@ -39,7 +39,7 @@ from novelwriter.common import isHandle, isItemClass, isTitleTag, jsonEncode
from novelwriter.constants import nwFiles, nwKeyWords, nwStyles from novelwriter.constants import nwFiles, nwKeyWords, nwStyles
from novelwriter.core.indexdata import NOTE_TYPES, TT_NONE, IndexHeading, IndexNode, T_NoteTypes from novelwriter.core.indexdata import NOTE_TYPES, TT_NONE, IndexHeading, IndexNode, T_NoteTypes
from novelwriter.core.novelmodel import NovelModel from novelwriter.core.novelmodel import NovelModel
from novelwriter.enum import nwComment, nwItemClass, nwItemLayout, nwItemType from novelwriter.enum import nwComment, nwItemClass, nwItemLayout, nwItemType, nwNovelExtra
from novelwriter.error import logException from novelwriter.error import logException
from novelwriter.text.comments import processComment from novelwriter.text.comments import processComment
from novelwriter.text.counting import standardCounter from novelwriter.text.counting import standardCounter
@@ -92,6 +92,7 @@ class Index:
# Models # Models
self._novelModels: dict[str, NovelModel] = {} self._novelModels: dict[str, NovelModel] = {}
self._novelExtra = nwNovelExtra.HIDDEN
# TimeStamps # TimeStamps
self._indexChange = 0.0 self._indexChange = 0.0
@@ -120,6 +121,15 @@ class Index:
self._generateNovelModel(tHandle) self._generateNovelModel(tHandle)
return self._novelModels.get(tHandle) return self._novelModels.get(tHandle)
##
# Setters
##
def setNovelModelExtraColumn(self, extra: nwNovelExtra) -> None:
"""Set the data content type of the novel model extra column."""
self._novelExtra = extra
return
## ##
# Public Methods # Public Methods
## ##
@@ -184,6 +194,7 @@ class Index:
logger.info("Refreshing novel model '%s'", tHandle) logger.info("Refreshing novel model '%s'", tHandle)
model.beginResetModel() model.beginResetModel()
model.clear() model.clear()
model.setExtraColumn(self._novelExtra)
self._appendSubTreeToModel(tHandle, model) self._appendSubTreeToModel(tHandle, model)
model.endResetModel() model.endResetModel()
return return
@@ -439,8 +450,9 @@ class Index:
self._itemIndex.setHeadingCounts(tHandle, sTitle, cC, wC, pC) self._itemIndex.setHeadingCounts(tHandle, sTitle, cC, wC, pC)
return return
def _indexKeyword(self, tHandle: str, line: str, sTitle: str, def _indexKeyword(
itemClass: nwItemClass, tags: dict[str, bool]) -> None: self, tHandle: str, line: str, sTitle: str, itemClass: nwItemClass, tags: dict[str, bool]
) -> None:
"""Validate and save the information about a reference to a tag """Validate and save the information about a reference to a tag
in another file, or the setting of a tag in the file. A record in another file, or the setting of a tag in the file. A record
of active tags is updated so that no longer used tags can be of active tags is updated so that no longer used tags can be
@@ -469,6 +481,7 @@ class Index:
"""Generate a novel model for a specific handle.""" """Generate a novel model for a specific handle."""
if (item := SHARED.project.tree[tHandle]) and item.isRootType() and item.isNovelLike(): if (item := SHARED.project.tree[tHandle]) and item.isRootType() and item.isNovelLike():
model = NovelModel() model = NovelModel()
model.setExtraColumn(self._novelExtra)
self._appendSubTreeToModel(tHandle, model) self._appendSubTreeToModel(tHandle, model)
self._novelModels[tHandle] = model self._novelModels[tHandle] = model
return return
+35 -6
View File
@@ -25,12 +25,14 @@ from __future__ import annotations
import logging import logging
from PyQt5.QtCore import QSize
from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt
from PyQt6.QtGui import QIcon, QPixmap from PyQt6.QtGui import QIcon, QPixmap
from novelwriter import SHARED from novelwriter import SHARED
from novelwriter.constants import nwStyles from novelwriter.constants import nwKeyWords, nwStyles
from novelwriter.core.indexdata import IndexHeading, IndexNode from novelwriter.core.indexdata import IndexHeading, IndexNode
from novelwriter.enum import nwNovelExtra
from novelwriter.error import logException from novelwriter.error import logException
from novelwriter.types import QtAlignRight from novelwriter.types import QtAlignRight
@@ -50,26 +52,49 @@ T_NodeData = str | tuple[str, str] | QIcon | QPixmap | Qt.AlignmentFlag | None
class NovelModel(QAbstractTableModel): class NovelModel(QAbstractTableModel):
__slots__ = ("_rows", "_more", "_columns") __slots__ = ("_rows", "_header", "_more", "_columns", "_extra")
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
self._rows: list[dict[int, T_NodeData]] = [] self._rows: list[dict[int, T_NodeData]] = []
self._header: list[QSize] = []
self._more = SHARED.theme.getIcon("more_arrow") self._more = SHARED.theme.getIcon("more_arrow")
self._columns = 3 self._columns = 3
self._extra = ""
return return
def __del__(self) -> None: # pragma: no cover def __del__(self) -> None: # pragma: no cover
logger.debug("Delete: NovelModel") logger.debug("Delete: NovelModel")
return return
##
# Properties
##
@property
def columns(self) -> int:
"""Return the number of columns."""
return self._columns
## ##
# Setters # Setters
## ##
def setExtraColumn(self, state: bool) -> None: def setExtraColumn(self, extra: nwNovelExtra) -> None:
"""Set extra data column settings.""" """Set extra data column settings."""
self._columns = 4 if state else 3 match extra:
case nwNovelExtra.HIDDEN:
self._columns = 3
self._extra = ""
case nwNovelExtra.POV:
self._columns = 4
self._extra = nwKeyWords.POV_KEY
case nwNovelExtra.FOCUS:
self._columns = 4
self._extra = nwKeyWords.FOCUS_KEY
case nwNovelExtra.PLOT:
self._columns = 4
self._extra = nwKeyWords.PLOT_KEY
return return
## ##
@@ -183,13 +208,17 @@ class NovelModel(QAbstractTableModel):
def _generateEntry(self, handle: str, key: str, heading: IndexHeading) -> dict: def _generateEntry(self, handle: str, key: str, heading: IndexHeading) -> dict:
"""Generate a cache entry.""" """Generate a cache entry."""
iLevel = nwStyles.H_LEVEL.get(heading.level, 0) iLevel = nwStyles.H_LEVEL.get(heading.level, 0)
more = self._columns - 1
data = {} data = {}
data[C_FACTOR*0 | R_TEXT] = heading.title data[C_FACTOR*0 | R_TEXT] = heading.title
data[C_FACTOR*0 | R_ICON] = SHARED.theme.getHeaderDecoration(iLevel) data[C_FACTOR*0 | R_ICON] = SHARED.theme.getHeaderDecoration(iLevel)
data[C_FACTOR*1 | R_TEXT] = f"{heading.mainCount:n}" data[C_FACTOR*1 | R_TEXT] = f"{heading.mainCount:n}"
data[C_FACTOR*1 | R_ALIGN] = QtAlignRight data[C_FACTOR*1 | R_ALIGN] = QtAlignRight
data[C_FACTOR*more | R_ICON] = self._more 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"
data[C_FACTOR*3 | R_ICON] = self._more
data[R_HANDLE] = handle data[R_HANDLE] = handle
data[R_KEY] = key data[R_KEY] = key
return data return data
+8
View File
@@ -180,6 +180,14 @@ class nwOutline(Enum):
SYNOP = 19 SYNOP = 19
class nwNovelExtra(Enum):
HIDDEN = 0
POV = 1
FOCUS = 2
PLOT = 3
class nwBuildFmt(Enum): class nwBuildFmt(Enum):
ODT = 0 ODT = 0
+84 -84
View File
@@ -30,36 +30,28 @@ import logging
from enum import Enum from enum import Enum
from PyQt6.QtCore import QModelIndex, QPoint, pyqtSignal, pyqtSlot from PyQt6.QtCore import QModelIndex, QPoint, pyqtSignal, pyqtSlot
from PyQt6.QtGui import QActionGroup, QFont, QPainter, QPalette from PyQt6.QtGui import QActionGroup, QFont, QPainter, QPalette, QResizeEvent
from PyQt6.QtWidgets import ( from PyQt6.QtWidgets import (
QAbstractItemView, QFrame, QHBoxLayout, QMenu, QStyleOptionViewItem, QAbstractItemView, QFrame, QHBoxLayout, QInputDialog, QMenu,
QToolTip, QVBoxLayout, QWidget QStyleOptionViewItem, QToolTip, QVBoxLayout, QWidget
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import qtAddAction, qtAddMenu, qtLambda from novelwriter.common import minmax, qtAddAction, qtAddMenu, qtLambda
from novelwriter.constants import nwKeyWords, nwLabels, trConst from novelwriter.constants import nwKeyWords, nwLabels, trConst
from novelwriter.core.novelmodel import NovelModel from novelwriter.core.novelmodel import NovelModel
from novelwriter.enum import nwChange, nwDocMode, nwItemClass, nwOutline from novelwriter.enum import nwChange, nwDocMode, nwItemClass, nwNovelExtra, nwOutline
from novelwriter.extensions.modified import NIconToolButton, NTreeView from novelwriter.extensions.modified import NIconToolButton, NTreeView
from novelwriter.extensions.novelselector import NovelSelector from novelwriter.extensions.novelselector import NovelSelector
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
from novelwriter.types import ( from novelwriter.types import (
QtHeaderStretch, QtHeaderToContents, QtScrollAlwaysOff, QtScrollAsNeeded, QtHeaderFixed, QtHeaderStretch, QtHeaderToContents, QtScrollAlwaysOff,
QtSizeExpanding QtScrollAsNeeded, QtSizeExpanding
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class NovelTreeColumn(Enum):
HIDDEN = 0
POV = 1
FOCUS = 2
PLOT = 3
class GuiNovelView(QWidget): class GuiNovelView(QWidget):
# Signals for user interaction with the novel tree # Signals for user interaction with the novel tree
@@ -119,11 +111,11 @@ class GuiNovelView(QWidget):
logger.debug("Setting novel tree to root item '%s'", lastNovel) logger.debug("Setting novel tree to root item '%s'", lastNovel)
lastCol = SHARED.project.options.getEnum( lastCol = SHARED.project.options.getEnum(
"GuiNovelView", "lastCol", NovelTreeColumn, NovelTreeColumn.HIDDEN "GuiNovelView", "lastCol", nwNovelExtra, nwNovelExtra.HIDDEN
)
lastColSize = SHARED.project.options.getInt(
"GuiNovelView", "lastColSize", 25
) )
# lastColSize = SHARED.project.options.getInt(
# "GuiNovelView", "lastColSize", 25
# )
self.clearNovelView() self.clearNovelView()
self.novelBar.buildNovelRootMenu() self.novelBar.buildNovelRootMenu()
@@ -131,18 +123,18 @@ class GuiNovelView(QWidget):
self.novelBar.setCurrentRoot(lastNovel) self.novelBar.setCurrentRoot(lastNovel)
self.novelBar.setEnabled(True) self.novelBar.setEnabled(True)
# self.novelTree.setLastColSize(lastColSize) self.novelTree.setLastColSize(lastColSize)
return return
def closeProjectTasks(self) -> None: def closeProjectTasks(self) -> None:
"""Run closing project tasks.""" """Run closing project tasks."""
# lastColType = self.novelTree.lastColType lastColType = self.novelTree.lastColType
# lastColSize = self.novelTree.lastColSize lastColSize = self.novelTree.lastColSize
# logger.debug("Saving State: GuiNovelView") logger.debug("Saving State: GuiNovelView")
# pOptions = SHARED.project.options options = SHARED.project.options
# pOptions.setValue("GuiNovelView", "lastCol", lastColType) options.setValue("GuiNovelView", "lastCol", lastColType)
# pOptions.setValue("GuiNovelView", "lastColSize", lastColSize) options.setValue("GuiNovelView", "lastColSize", lastColSize)
self.clearNovelView() self.clearNovelView()
return return
@@ -164,7 +156,7 @@ class GuiNovelView(QWidget):
"""Set the current novel to display.""" """Set the current novel to display."""
if rootHandle and (model := SHARED.project.index.getNovelModel(rootHandle)): if rootHandle and (model := SHARED.project.index.getNovelModel(rootHandle)):
self.novelTree.setModel(model) self.novelTree.setModel(model)
self.novelTree.resizeColumsn() self.novelTree.resizeColumns()
return return
@pyqtSlot(str) @pyqtSlot(str)
@@ -232,10 +224,10 @@ class GuiNovelToolBar(QWidget):
self.mLastCol = qtAddMenu(self.mMore, self.tr("Last Column")) self.mLastCol = qtAddMenu(self.mMore, self.tr("Last Column"))
self.gLastCol = QActionGroup(self.mMore) self.gLastCol = QActionGroup(self.mMore)
self.aLastCol = {} self.aLastCol = {}
self._addLastColAction(NovelTreeColumn.HIDDEN, self.tr("Hidden")) self._addLastColAction(nwNovelExtra.HIDDEN, self.tr("Hidden"))
self._addLastColAction(NovelTreeColumn.POV, self.tr("Point of View Character")) self._addLastColAction(nwNovelExtra.POV, self.tr("Point of View Character"))
self._addLastColAction(NovelTreeColumn.FOCUS, self.tr("Focus Character")) self._addLastColAction(nwNovelExtra.FOCUS, self.tr("Focus Character"))
self._addLastColAction(NovelTreeColumn.PLOT, self.tr("Novel Plot")) self._addLastColAction(nwNovelExtra.PLOT, self.tr("Novel Plot"))
self.mLastCol.addSeparator() self.mLastCol.addSeparator()
self.aLastColSize = qtAddAction(self.mLastCol, self.tr("Column Size")) self.aLastColSize = qtAddAction(self.mLastCol, self.tr("Column Size"))
@@ -312,10 +304,13 @@ class GuiNovelToolBar(QWidget):
self.novelView.setCurrentNovel(rootHandle) self.novelView.setCurrentNovel(rootHandle)
return return
def setLastColType(self, colType: NovelTreeColumn, doRefresh: bool = True) -> None: def setLastColType(self, colType: nwNovelExtra, doRefresh: bool = True) -> None:
"""Set the last column type.""" """Set the last column type."""
# self.aLastCol[colType].setChecked(True) self.aLastCol[colType].setChecked(True)
# self.novelView.novelTree.setLastColType(colType, doRefresh=doRefresh) self.novelView.novelTree.setLastColType(colType)
if doRefresh:
self._forceRefreshNovelTree()
self.novelView.novelTree.resizeColumns()
return return
def setActive(self, state: bool) -> None: def setActive(self, state: bool) -> None:
@@ -352,20 +347,20 @@ class GuiNovelToolBar(QWidget):
@pyqtSlot() @pyqtSlot()
def _selectLastColumnSize(self) -> None: def _selectLastColumnSize(self) -> None:
"""Set the maximum width for the last column.""" """Set the maximum width for the last column."""
# oldSize = self.novelView.novelTree.lastColSize oldSize = self.novelView.novelTree.lastColSize
# newSize, isOk = QInputDialog.getInt( newSize, isOk = QInputDialog.getInt(
# self, self.tr("Column Size"), self.tr("Maximum column size in %"), oldSize, 15, 75, 5 self, self.tr("Column Size"), self.tr("Maximum column size in %"), oldSize, 15, 75, 5
# ) )
# if isOk: if isOk:
# self.novelView.novelTree.setLastColSize(newSize) self.novelView.novelTree.setLastColSize(newSize)
# self._refreshNovelTree() self.novelView.novelTree.resizeColumns()
return return
## ##
# Internal Functions # Internal Functions
## ##
def _addLastColAction(self, colType: NovelTreeColumn, actionLabel: str) -> None: def _addLastColAction(self, colType: nwNovelExtra, actionLabel: str) -> None:
"""Add a column selection entry to the last column menu.""" """Add a column selection entry to the last column menu."""
aLast = qtAddAction(self.mLastCol, actionLabel) aLast = qtAddAction(self.mLastCol, actionLabel)
aLast.setCheckable(True) aLast.setCheckable(True)
@@ -385,10 +380,10 @@ class GuiNovelTree(NTreeView):
self.novelView = novelView self.novelView = novelView
# Internal Variables # Internal Variables
self._actHandle = None self._actHandle = None
self._lastColType = nwNovelExtra.POV
self._lastColSize = 0.25
# self._lastBuild = 0 # self._lastBuild = 0
# self._lastCol = NovelTreeColumn.POV
# self._lastColSize = 0.25
# self._treeMap: dict[str, QTreeWidgetItem] = {} # self._treeMap: dict[str, QTreeWidgetItem] = {}
# Cached Strings # Cached Strings
@@ -432,6 +427,20 @@ class GuiNovelTree(NTreeView):
self.setHorizontalScrollBarPolicy(QtScrollAsNeeded) self.setHorizontalScrollBarPolicy(QtScrollAsNeeded)
return return
##
# Properties
##
@property
def lastColType(self) -> nwNovelExtra:
"""The data type of the extra column."""
return self._lastColType
@property
def lastColSize(self) -> int:
"""Return the size of the extra column."""
return int(self._lastColSize * 100)
## ##
# Getters # Getters
## ##
@@ -456,18 +465,34 @@ class GuiNovelTree(NTreeView):
viewport.repaint() viewport.repaint()
return return
def setLastColType(self, colType: nwNovelExtra) -> None:
"""Set the extra column type."""
self._lastColType = colType
SHARED.project.index.setNovelModelExtraColumn(colType)
return
def setLastColSize(self, colSize: int) -> None:
"""Set the extra column size between 15% and 75%."""
self._lastColSize = minmax(colSize, 15, 75)/100.0
return
## ##
# Class Methods # Class Methods
## ##
def resizeColumsn(self) -> None: def resizeColumns(self) -> None:
"""Set the correct column sizes.""" """Set the correct column sizes."""
if (header := self.header()) and (model := self._getModel()): if (header := self.header()) and (model := self._getModel()) and (vp := self.viewport()):
header.setStretchLastSection(False) header.setStretchLastSection(False)
header.setMinimumSectionSize(SHARED.theme.baseIconHeight + 6) header.setMinimumSectionSize(SHARED.theme.baseIconHeight + 6)
header.setSectionResizeMode(0, QtHeaderStretch) header.setSectionResizeMode(0, QtHeaderStretch)
for i in range(1, model.columnCount(QModelIndex())): header.setSectionResizeMode(1, QtHeaderToContents)
header.setSectionResizeMode(i, QtHeaderToContents) if model.columns == 3:
header.setSectionResizeMode(2, QtHeaderToContents)
elif model.columns == 4:
header.setSectionResizeMode(2, QtHeaderFixed)
header.setSectionResizeMode(3, QtHeaderToContents)
header.resizeSection(2, int(self._lastColSize * vp.width()))
return return
## ##
@@ -481,6 +506,16 @@ class GuiNovelTree(NTreeView):
super().drawRow(painter, opt, index) super().drawRow(painter, opt, index)
return return
##
# Events
##
def resizeEvent(self, event: QResizeEvent) -> None:
"""Process size changed."""
super().resizeEvent(event)
self.resizeColumns()
return
## ##
# Private Slots # Private Slots
## ##
@@ -566,18 +601,6 @@ class GuiNovelTree(NTreeView):
# Old Code # Old Code
## ##
##
# Properties
##
# @property
# def lastColType(self) -> NovelTreeColumn:
# return self._lastCol
# @property
# def lastColSize(self) -> int:
# return int(self._lastColSize * 100)
## ##
# Class Methods # Class Methods
## ##
@@ -632,11 +655,6 @@ class GuiNovelTree(NTreeView):
# self.refreshTree(rootHandle=lastNovel, overRide=True) # self.refreshTree(rootHandle=lastNovel, overRide=True)
# return # return
# def setLastColSize(self, colSize: int) -> None:
# """Set the column size in integer values between 15 and 75."""
# self._lastColSize = minmax(colSize, 15, 75)/100.0
# return
# def setActiveHandle(self, tHandle: str | None) -> None: # def setActiveHandle(self, tHandle: str | None) -> None:
# """Highlight the rows associated with a given handle.""" # """Highlight the rows associated with a given handle."""
# didScroll = False # didScroll = False
@@ -693,24 +711,6 @@ class GuiNovelTree(NTreeView):
# self.clearSelection() # self.clearSelection()
# return # return
# def resizeEvent(self, event: QResizeEvent) -> None:
# """Elide labels in the extra column."""
# super().resizeEvent(event)
# newW = event.size().width()
# oldW = event.oldSize().width()
# if newW != oldW:
# eliW = int(self._lastColSize * newW)
# fMetric = self.fontMetrics()
# for i in range(self.topLevelItemCount()):
# trItem = self.topLevelItem(i)
# if isinstance(trItem, QTreeWidgetItem):
# lastText = trItem.data(self.C_DATA, self.D_EXTRA)
# trItem.setText(
# self.C_EXTRA,
# fMetric.elidedText(lastText, Qt.TextElideMode.ElideRight, eliW)
# )
# return
## ##
# Private Slots # Private Slots
## ##