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.core.indexdata import NOTE_TYPES, TT_NONE, IndexHeading, IndexNode, T_NoteTypes
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.text.comments import processComment
from novelwriter.text.counting import standardCounter
@@ -92,6 +92,7 @@ class Index:
# Models
self._novelModels: dict[str, NovelModel] = {}
self._novelExtra = nwNovelExtra.HIDDEN
# TimeStamps
self._indexChange = 0.0
@@ -120,6 +121,15 @@ class Index:
self._generateNovelModel(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
##
@@ -184,6 +194,7 @@ class Index:
logger.info("Refreshing novel model '%s'", tHandle)
model.beginResetModel()
model.clear()
model.setExtraColumn(self._novelExtra)
self._appendSubTreeToModel(tHandle, model)
model.endResetModel()
return
@@ -439,8 +450,9 @@ class Index:
self._itemIndex.setHeadingCounts(tHandle, sTitle, cC, wC, pC)
return
def _indexKeyword(self, tHandle: str, line: str, sTitle: str,
itemClass: nwItemClass, tags: dict[str, bool]) -> None:
def _indexKeyword(
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
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
@@ -469,6 +481,7 @@ class Index:
"""Generate a novel model for a specific handle."""
if (item := SHARED.project.tree[tHandle]) and item.isRootType() and item.isNovelLike():
model = NovelModel()
model.setExtraColumn(self._novelExtra)
self._appendSubTreeToModel(tHandle, model)
self._novelModels[tHandle] = model
return
+35 -6
View File
@@ -25,12 +25,14 @@ from __future__ import annotations
import logging
from PyQt5.QtCore import QSize
from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt
from PyQt6.QtGui import QIcon, QPixmap
from novelwriter import SHARED
from novelwriter.constants import nwStyles
from novelwriter.constants import nwKeyWords, nwStyles
from novelwriter.core.indexdata import IndexHeading, IndexNode
from novelwriter.enum import nwNovelExtra
from novelwriter.error import logException
from novelwriter.types import QtAlignRight
@@ -50,26 +52,49 @@ T_NodeData = str | tuple[str, str] | QIcon | QPixmap | Qt.AlignmentFlag | None
class NovelModel(QAbstractTableModel):
__slots__ = ("_rows", "_more", "_columns")
__slots__ = ("_rows", "_header", "_more", "_columns", "_extra")
def __init__(self) -> None:
super().__init__()
self._rows: list[dict[int, T_NodeData]] = []
self._header: list[QSize] = []
self._more = SHARED.theme.getIcon("more_arrow")
self._columns = 3
self._extra = ""
return
def __del__(self) -> None: # pragma: no cover
logger.debug("Delete: NovelModel")
return
##
# Properties
##
@property
def columns(self) -> int:
"""Return the number of columns."""
return self._columns
##
# Setters
##
def setExtraColumn(self, state: bool) -> None:
def setExtraColumn(self, extra: nwNovelExtra) -> None:
"""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
##
@@ -183,13 +208,17 @@ class NovelModel(QAbstractTableModel):
def _generateEntry(self, handle: str, key: str, heading: IndexHeading) -> dict:
"""Generate a cache entry."""
iLevel = nwStyles.H_LEVEL.get(heading.level, 0)
more = self._columns - 1
data = {}
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}"
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_KEY] = key
return data
+8
View File
@@ -180,6 +180,14 @@ class nwOutline(Enum):
SYNOP = 19
class nwNovelExtra(Enum):
HIDDEN = 0
POV = 1
FOCUS = 2
PLOT = 3
class nwBuildFmt(Enum):
ODT = 0
+84 -84
View File
@@ -30,36 +30,28 @@ import logging
from enum import Enum
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 (
QAbstractItemView, QFrame, QHBoxLayout, QMenu, QStyleOptionViewItem,
QToolTip, QVBoxLayout, QWidget
QAbstractItemView, QFrame, QHBoxLayout, QInputDialog, QMenu,
QStyleOptionViewItem, QToolTip, QVBoxLayout, QWidget
)
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.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.novelselector import NovelSelector
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
from novelwriter.types import (
QtHeaderStretch, QtHeaderToContents, QtScrollAlwaysOff, QtScrollAsNeeded,
QtSizeExpanding
QtHeaderFixed, QtHeaderStretch, QtHeaderToContents, QtScrollAlwaysOff,
QtScrollAsNeeded, QtSizeExpanding
)
logger = logging.getLogger(__name__)
class NovelTreeColumn(Enum):
HIDDEN = 0
POV = 1
FOCUS = 2
PLOT = 3
class GuiNovelView(QWidget):
# 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)
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.novelBar.buildNovelRootMenu()
@@ -131,18 +123,18 @@ class GuiNovelView(QWidget):
self.novelBar.setCurrentRoot(lastNovel)
self.novelBar.setEnabled(True)
# self.novelTree.setLastColSize(lastColSize)
self.novelTree.setLastColSize(lastColSize)
return
def closeProjectTasks(self) -> None:
"""Run closing project tasks."""
# lastColType = self.novelTree.lastColType
# lastColSize = self.novelTree.lastColSize
# logger.debug("Saving State: GuiNovelView")
# pOptions = SHARED.project.options
# pOptions.setValue("GuiNovelView", "lastCol", lastColType)
# pOptions.setValue("GuiNovelView", "lastColSize", lastColSize)
lastColType = self.novelTree.lastColType
lastColSize = self.novelTree.lastColSize
logger.debug("Saving State: GuiNovelView")
options = SHARED.project.options
options.setValue("GuiNovelView", "lastCol", lastColType)
options.setValue("GuiNovelView", "lastColSize", lastColSize)
self.clearNovelView()
return
@@ -164,7 +156,7 @@ class GuiNovelView(QWidget):
"""Set the current novel to display."""
if rootHandle and (model := SHARED.project.index.getNovelModel(rootHandle)):
self.novelTree.setModel(model)
self.novelTree.resizeColumsn()
self.novelTree.resizeColumns()
return
@pyqtSlot(str)
@@ -232,10 +224,10 @@ class GuiNovelToolBar(QWidget):
self.mLastCol = qtAddMenu(self.mMore, self.tr("Last Column"))
self.gLastCol = QActionGroup(self.mMore)
self.aLastCol = {}
self._addLastColAction(NovelTreeColumn.HIDDEN, self.tr("Hidden"))
self._addLastColAction(NovelTreeColumn.POV, self.tr("Point of View Character"))
self._addLastColAction(NovelTreeColumn.FOCUS, self.tr("Focus Character"))
self._addLastColAction(NovelTreeColumn.PLOT, self.tr("Novel Plot"))
self._addLastColAction(nwNovelExtra.HIDDEN, self.tr("Hidden"))
self._addLastColAction(nwNovelExtra.POV, self.tr("Point of View Character"))
self._addLastColAction(nwNovelExtra.FOCUS, self.tr("Focus Character"))
self._addLastColAction(nwNovelExtra.PLOT, self.tr("Novel Plot"))
self.mLastCol.addSeparator()
self.aLastColSize = qtAddAction(self.mLastCol, self.tr("Column Size"))
@@ -312,10 +304,13 @@ class GuiNovelToolBar(QWidget):
self.novelView.setCurrentNovel(rootHandle)
return
def setLastColType(self, colType: NovelTreeColumn, doRefresh: bool = True) -> None:
def setLastColType(self, colType: nwNovelExtra, doRefresh: bool = True) -> None:
"""Set the last column type."""
# self.aLastCol[colType].setChecked(True)
# self.novelView.novelTree.setLastColType(colType, doRefresh=doRefresh)
self.aLastCol[colType].setChecked(True)
self.novelView.novelTree.setLastColType(colType)
if doRefresh:
self._forceRefreshNovelTree()
self.novelView.novelTree.resizeColumns()
return
def setActive(self, state: bool) -> None:
@@ -352,20 +347,20 @@ class GuiNovelToolBar(QWidget):
@pyqtSlot()
def _selectLastColumnSize(self) -> None:
"""Set the maximum width for the last column."""
# oldSize = self.novelView.novelTree.lastColSize
# newSize, isOk = QInputDialog.getInt(
# self, self.tr("Column Size"), self.tr("Maximum column size in %"), oldSize, 15, 75, 5
# )
# if isOk:
# self.novelView.novelTree.setLastColSize(newSize)
# self._refreshNovelTree()
oldSize = self.novelView.novelTree.lastColSize
newSize, isOk = QInputDialog.getInt(
self, self.tr("Column Size"), self.tr("Maximum column size in %"), oldSize, 15, 75, 5
)
if isOk:
self.novelView.novelTree.setLastColSize(newSize)
self.novelView.novelTree.resizeColumns()
return
##
# 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."""
aLast = qtAddAction(self.mLastCol, actionLabel)
aLast.setCheckable(True)
@@ -385,10 +380,10 @@ class GuiNovelTree(NTreeView):
self.novelView = novelView
# Internal Variables
self._actHandle = None
self._actHandle = None
self._lastColType = nwNovelExtra.POV
self._lastColSize = 0.25
# self._lastBuild = 0
# self._lastCol = NovelTreeColumn.POV
# self._lastColSize = 0.25
# self._treeMap: dict[str, QTreeWidgetItem] = {}
# Cached Strings
@@ -432,6 +427,20 @@ class GuiNovelTree(NTreeView):
self.setHorizontalScrollBarPolicy(QtScrollAsNeeded)
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
##
@@ -456,18 +465,34 @@ class GuiNovelTree(NTreeView):
viewport.repaint()
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
##
def resizeColumsn(self) -> None:
def resizeColumns(self) -> None:
"""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.setMinimumSectionSize(SHARED.theme.baseIconHeight + 6)
header.setSectionResizeMode(0, QtHeaderStretch)
for i in range(1, model.columnCount(QModelIndex())):
header.setSectionResizeMode(i, QtHeaderToContents)
header.setSectionResizeMode(1, 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
##
@@ -481,6 +506,16 @@ class GuiNovelTree(NTreeView):
super().drawRow(painter, opt, index)
return
##
# Events
##
def resizeEvent(self, event: QResizeEvent) -> None:
"""Process size changed."""
super().resizeEvent(event)
self.resizeColumns()
return
##
# Private Slots
##
@@ -566,18 +601,6 @@ class GuiNovelTree(NTreeView):
# Old Code
##
##
# Properties
##
# @property
# def lastColType(self) -> NovelTreeColumn:
# return self._lastCol
# @property
# def lastColSize(self) -> int:
# return int(self._lastColSize * 100)
##
# Class Methods
##
@@ -632,11 +655,6 @@ class GuiNovelTree(NTreeView):
# self.refreshTree(rootHandle=lastNovel, overRide=True)
# 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:
# """Highlight the rows associated with a given handle."""
# didScroll = False
@@ -693,24 +711,6 @@ class GuiNovelTree(NTreeView):
# self.clearSelection()
# 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
##