Add the Novel Tree meta data feature (#1088)
This commit is contained in:
@@ -88,3 +88,4 @@ deco_doc_h1 = nw_deco-h1.svg
|
||||
deco_doc_h2 = nw_deco-h2.svg
|
||||
deco_doc_h3 = nw_deco-h3.svg
|
||||
deco_doc_h4 = nw_deco-h4.svg
|
||||
deco_doc_more = nw_deco-noveltree-more.svg
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.2"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
id="svg2161"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata
|
||||
id="metadata2167">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs2165" />
|
||||
<path
|
||||
d="M 9.7,18.2 16,12 9.7,5.8 C 9.5,5.6 9.2,5.5 9,5.5 8.8,5.5 8.5,5.6 8.3,5.8 8.1,6 8,6.2 8,6.5 v 11 c 0,0.3 0.1,0.5 0.3,0.7 0.2,0.2 0.5,0.3 0.7,0.3 0.2,0 0.5,-0.1 0.7,-0.3 z"
|
||||
id="path3096"
|
||||
style="fill:#848484;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 928 B |
@@ -88,3 +88,4 @@ deco_doc_h1 = nw_deco-h1.svg
|
||||
deco_doc_h2 = nw_deco-h2.svg
|
||||
deco_doc_h3 = nw_deco-h3.svg
|
||||
deco_doc_h4 = nw_deco-h4.svg
|
||||
deco_doc_more = nw_deco-noveltree-more.svg
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.2"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
id="svg2161"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata
|
||||
id="metadata2167">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs2165" />
|
||||
<path
|
||||
d="M 9.7,18.2 16,12 9.7,5.8 C 9.5,5.6 9.2,5.5 9,5.5 8.8,5.5 8.5,5.6 8.3,5.8 8.1,6 8,6.2 8,6.5 v 11 c 0,0.3 0.1,0.5 0.3,0.7 0.2,0.2 0.5,0.3 0.7,0.3 0.2,0 0.5,-0.1 0.7,-0.3 z"
|
||||
id="path3096"
|
||||
style="fill:#848484;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 928 B |
@@ -10,7 +10,7 @@ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
|
||||
window = 54, 54, 54
|
||||
windowtext = 174, 174, 174
|
||||
base = 62, 62, 62
|
||||
alternatebase = 67, 67, 67
|
||||
alternatebase = 78, 78, 78
|
||||
text = 174, 174, 174
|
||||
tooltipbase = 255, 255, 192
|
||||
tooltiptext = 21, 21, 13
|
||||
|
||||
@@ -10,7 +10,7 @@ licenseurl = https://github.com/altercation/solarized/blob/master/LICENSE
|
||||
window = 0, 43, 54
|
||||
windowtext = 253, 246, 227
|
||||
base = 7, 54, 66
|
||||
alternatebase = 67, 67, 67
|
||||
alternatebase = 0, 43, 54
|
||||
text = 253, 246, 227
|
||||
tooltipbase = 133, 153, 0
|
||||
tooltiptext = 0, 43, 54
|
||||
|
||||
+1
-27
@@ -96,8 +96,6 @@ class Config:
|
||||
# Sizes
|
||||
self.winGeometry = [1200, 650]
|
||||
self.prefGeometry = [700, 615]
|
||||
self.treeColWidth = [200, 50, 30]
|
||||
self.novelColWidth = [200, 50]
|
||||
self.projColWidth = [200, 60, 140]
|
||||
self.mainPanePos = [300, 800]
|
||||
self.docPanePos = [400, 400]
|
||||
@@ -117,7 +115,7 @@ class Config:
|
||||
# Text Editor
|
||||
self.textFont = None # Editor font
|
||||
self.textSize = 12 # Editor font size
|
||||
self.textWidth = 600 # Editor text width
|
||||
self.textWidth = 700 # Editor text width
|
||||
self.textMargin = 40 # Editor/viewer text margin
|
||||
self.tabWidth = 40 # Editor tabulator width
|
||||
|
||||
@@ -460,8 +458,6 @@ class Config:
|
||||
cnfSec = "Sizes"
|
||||
self.winGeometry = theConf.rdIntList(cnfSec, "geometry", self.winGeometry)
|
||||
self.prefGeometry = theConf.rdIntList(cnfSec, "preferences", self.prefGeometry)
|
||||
self.treeColWidth = theConf.rdIntList(cnfSec, "treecols", self.treeColWidth)
|
||||
self.novelColWidth = theConf.rdIntList(cnfSec, "novelcols", self.novelColWidth)
|
||||
self.projColWidth = theConf.rdIntList(cnfSec, "projcols", self.projColWidth)
|
||||
self.mainPanePos = theConf.rdIntList(cnfSec, "mainpane", self.mainPanePos)
|
||||
self.docPanePos = theConf.rdIntList(cnfSec, "docpane", self.docPanePos)
|
||||
@@ -581,8 +577,6 @@ class Config:
|
||||
theConf["Sizes"] = {
|
||||
"geometry": self._packList(self.winGeometry),
|
||||
"preferences": self._packList(self.prefGeometry),
|
||||
"treecols": self._packList(self.treeColWidth),
|
||||
"novelcols": self._packList(self.novelColWidth),
|
||||
"projcols": self._packList(self.projColWidth),
|
||||
"mainpane": self._packList(self.mainPanePos),
|
||||
"docpane": self._packList(self.docPanePos),
|
||||
@@ -811,20 +805,6 @@ class Config:
|
||||
self.confChanged = True
|
||||
return True
|
||||
|
||||
def setTreeColWidths(self, colWidths):
|
||||
"""Set the column widths of the main project tree.
|
||||
"""
|
||||
self.treeColWidth = [int(x/self.guiScale) for x in colWidths]
|
||||
self.confChanged = True
|
||||
return True
|
||||
|
||||
def setNovelColWidths(self, colWidths):
|
||||
"""Set the column widths of the novel tree.
|
||||
"""
|
||||
self.novelColWidth = [int(x/self.guiScale) for x in colWidths]
|
||||
self.confChanged = True
|
||||
return True
|
||||
|
||||
def setProjColWidths(self, colWidths):
|
||||
"""Set the column widths of the Load Project dialog.
|
||||
"""
|
||||
@@ -910,12 +890,6 @@ class Config:
|
||||
def getPreferencesSize(self):
|
||||
return [int(x*self.guiScale) for x in self.prefGeometry]
|
||||
|
||||
def getTreeColWidths(self):
|
||||
return [int(x*self.guiScale) for x in self.treeColWidth]
|
||||
|
||||
def getNovelColWidths(self):
|
||||
return [int(x*self.guiScale) for x in self.novelColWidth]
|
||||
|
||||
def getProjColWidths(self):
|
||||
return [int(x*self.guiScale) for x in self.projColWidth]
|
||||
|
||||
|
||||
+120
-15
@@ -35,11 +35,11 @@ from PyQt5.QtGui import QPalette
|
||||
from PyQt5.QtCore import Qt, QSize, pyqtSlot, pyqtSignal
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractItemView, QActionGroup, QFrame, QHBoxLayout, QHeaderView, QLabel,
|
||||
QMenu, QSizePolicy, QToolButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
|
||||
QWidget
|
||||
QMenu, QSizePolicy, QToolButton, QToolTip, QTreeWidget, QTreeWidgetItem,
|
||||
QVBoxLayout, QWidget
|
||||
)
|
||||
|
||||
from novelwriter.enum import nwDocMode, nwItemClass
|
||||
from novelwriter.enum import nwDocMode, nwItemClass, nwOutline
|
||||
from novelwriter.common import checkInt
|
||||
from novelwriter.constants import nwHeaders, nwKeyWords, nwLabels, trConst
|
||||
|
||||
@@ -84,6 +84,7 @@ class GuiNovelView(QWidget):
|
||||
# Function Mappings
|
||||
self.updateWordCounts = self.novelTree.updateWordCounts
|
||||
self.getSelectedHandle = self.novelTree.getSelectedHandle
|
||||
self.setActiveHandle = self.novelTree.setActiveHandle
|
||||
|
||||
return
|
||||
|
||||
@@ -322,7 +323,12 @@ class GuiNovelTree(QTreeWidget):
|
||||
|
||||
C_TITLE = 0
|
||||
C_WORDS = 1
|
||||
C_LAST = 2
|
||||
C_EXTRA = 2
|
||||
C_MORE = 3
|
||||
|
||||
D_HANDLE = Qt.UserRole
|
||||
D_TITLE = Qt.UserRole + 1
|
||||
D_KEY = Qt.UserRole + 2
|
||||
|
||||
def __init__(self, novelView):
|
||||
QTreeWidget.__init__(self, novelView)
|
||||
@@ -339,6 +345,7 @@ class GuiNovelTree(QTreeWidget):
|
||||
self._treeMap = {}
|
||||
self._lastBuild = 0
|
||||
self._lastCol = NovelTreeColumn.POV
|
||||
self._actHandle = None
|
||||
|
||||
# Cached Strings
|
||||
self._povLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY])
|
||||
@@ -353,9 +360,11 @@ class GuiNovelTree(QTreeWidget):
|
||||
|
||||
self.setIconSize(QSize(iPx, iPx))
|
||||
self.setFrameStyle(QFrame.NoFrame)
|
||||
self.setUniformRowHeights(True)
|
||||
self.setAllColumnsShowFocus(True)
|
||||
self.setHeaderHidden(True)
|
||||
self.setIndentation(0)
|
||||
self.setColumnCount(3)
|
||||
self.setColumnCount(4)
|
||||
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.setExpandsOnDoubleClick(False)
|
||||
@@ -367,7 +376,8 @@ class GuiNovelTree(QTreeWidget):
|
||||
treeHeader.setMinimumSectionSize(iPx + cMg)
|
||||
treeHeader.setSectionResizeMode(self.C_TITLE, QHeaderView.Stretch)
|
||||
treeHeader.setSectionResizeMode(self.C_WORDS, QHeaderView.ResizeToContents)
|
||||
treeHeader.setSectionResizeMode(self.C_LAST, QHeaderView.ResizeToContents)
|
||||
treeHeader.setSectionResizeMode(self.C_EXTRA, QHeaderView.ResizeToContents)
|
||||
treeHeader.setSectionResizeMode(self.C_MORE, QHeaderView.ResizeToContents)
|
||||
|
||||
# Pre-Generate Tree Formatting
|
||||
fH1 = self.font()
|
||||
@@ -385,8 +395,12 @@ class GuiNovelTree(QTreeWidget):
|
||||
self.mainTheme.loadDecoration("deco_doc_h3", pxH=iPx),
|
||||
self.mainTheme.loadDecoration("deco_doc_h4", pxH=iPx),
|
||||
]
|
||||
self._pMore = self.mainTheme.loadDecoration("deco_doc_more", pxH=iPx)
|
||||
self._pActive = self.mainTheme.loadDecoration("deco_more_on", pxH=iPx)
|
||||
self._pInactive = self.mainTheme.loadDecoration("deco_more_off", pxH=iPx)
|
||||
|
||||
# Connect signals
|
||||
self.clicked.connect(self._treeItemClicked)
|
||||
self.itemDoubleClicked.connect(self._treeDoubleClick)
|
||||
self.itemSelectionChanged.connect(self._treeSelectionChange)
|
||||
|
||||
@@ -449,7 +463,7 @@ class GuiNovelTree(QTreeWidget):
|
||||
selItem = self.selectedItems()
|
||||
titleKey = None
|
||||
if selItem:
|
||||
titleKey = selItem[0].data(self.C_TITLE, Qt.UserRole)[2]
|
||||
titleKey = selItem[0].data(self.C_TITLE, self.D_KEY)
|
||||
|
||||
self._populateTree(rootHandle)
|
||||
self.theProject.setLastNovelViewed(rootHandle)
|
||||
@@ -476,8 +490,9 @@ class GuiNovelTree(QTreeWidget):
|
||||
tHandle = None
|
||||
tLine = 0
|
||||
if selItem:
|
||||
tHandle = selItem[0].data(self.C_TITLE, Qt.UserRole)[0]
|
||||
tLine = checkInt(selItem[0].data(self.C_TITLE, Qt.UserRole)[1], 1) - 1
|
||||
tHandle = selItem[0].data(self.C_TITLE, self.D_HANDLE)
|
||||
sTitle = selItem[0].data(self.C_TITLE, self.D_TITLE)
|
||||
tLine = checkInt(sTitle[1:], 1) - 1
|
||||
|
||||
return tHandle, tLine
|
||||
|
||||
@@ -487,11 +502,34 @@ class GuiNovelTree(QTreeWidget):
|
||||
if self._lastCol != colType:
|
||||
logger.debug("Changing last column to %s", colType.name)
|
||||
self._lastCol = colType
|
||||
self.setColumnHidden(self.C_LAST, colType == NovelTreeColumn.HIDDEN)
|
||||
self.setColumnHidden(self.C_EXTRA, colType == NovelTreeColumn.HIDDEN)
|
||||
if doRefresh:
|
||||
self.refreshTree(rootHandle=self.theProject.lastNovel, overRide=True)
|
||||
return
|
||||
|
||||
def setActiveHandle(self, tHandle):
|
||||
"""Highlight the rows associated with a given handle.
|
||||
"""
|
||||
tStart = time()
|
||||
|
||||
self._actHandle = tHandle
|
||||
for i in range(self.topLevelItemCount()):
|
||||
tItem = self.topLevelItem(i)
|
||||
if tItem.data(self.C_TITLE, self.D_HANDLE) == tHandle:
|
||||
tItem.setBackground(self.C_TITLE, self.palette().alternateBase())
|
||||
tItem.setBackground(self.C_WORDS, self.palette().alternateBase())
|
||||
tItem.setBackground(self.C_EXTRA, self.palette().alternateBase())
|
||||
tItem.setBackground(self.C_MORE, self.palette().alternateBase())
|
||||
else:
|
||||
tItem.setBackground(self.C_TITLE, self.palette().base())
|
||||
tItem.setBackground(self.C_WORDS, self.palette().base())
|
||||
tItem.setBackground(self.C_EXTRA, self.palette().base())
|
||||
tItem.setBackground(self.C_MORE, self.palette().base())
|
||||
|
||||
logger.verbose("Highlighted Novel Tree in %.3f ms", (time() - tStart)*1000)
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Events
|
||||
##
|
||||
@@ -521,10 +559,28 @@ class GuiNovelTree(QTreeWidget):
|
||||
|
||||
return
|
||||
|
||||
def focusOutEvent(self, theEvent):
|
||||
"""Clear the selection when the tree no longer has focus.
|
||||
"""
|
||||
QTreeWidget.focusOutEvent(self, theEvent)
|
||||
self.clearSelection()
|
||||
return
|
||||
|
||||
##
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
@pyqtSlot("QModelIndex")
|
||||
def _treeItemClicked(self, mIndex):
|
||||
"""The user clicked on an item in the tree.
|
||||
"""
|
||||
if mIndex.column() == self.C_MORE:
|
||||
tHandle = mIndex.siblingAtColumn(self.C_TITLE).data(self.D_HANDLE)
|
||||
sTitle = mIndex.siblingAtColumn(self.C_TITLE).data(self.D_TITLE)
|
||||
tipPos = self.mapToGlobal(self.visualRect(mIndex).topRight())
|
||||
self._popMetaBox(tipPos, tHandle, sTitle)
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _treeSelectionChange(self):
|
||||
"""Extract the handle and line number of the currently selected
|
||||
@@ -564,23 +620,27 @@ class GuiNovelTree(QTreeWidget):
|
||||
continue
|
||||
|
||||
newItem = QTreeWidgetItem()
|
||||
theData = (tHandle, sTitle[1:].lstrip("0"), tKey)
|
||||
|
||||
newItem.setData(self.C_TITLE, Qt.DecorationRole, self._pIndent[iLevel])
|
||||
newItem.setText(self.C_TITLE, novIdx.title)
|
||||
newItem.setData(self.C_TITLE, Qt.UserRole, theData)
|
||||
newItem.setData(self.C_TITLE, self.D_HANDLE, tHandle)
|
||||
newItem.setData(self.C_TITLE, self.D_TITLE, sTitle)
|
||||
newItem.setData(self.C_TITLE, self.D_KEY, tKey)
|
||||
newItem.setFont(self.C_TITLE, self._hFonts[iLevel])
|
||||
newItem.setText(self.C_WORDS, f"{novIdx.wordCount:n}")
|
||||
newItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
|
||||
newItem.setData(self.C_MORE, Qt.DecorationRole, self._pMore)
|
||||
|
||||
# Custom column
|
||||
lastText, toolTip = self._getLastColumnText(tHandle, sTitle)
|
||||
newItem.setText(self.C_LAST, lastText)
|
||||
newItem.setText(self.C_EXTRA, lastText)
|
||||
if lastText:
|
||||
newItem.setToolTip(self.C_LAST, toolTip)
|
||||
newItem.setToolTip(self.C_EXTRA, toolTip)
|
||||
|
||||
self._treeMap[tKey] = newItem
|
||||
self.addTopLevelItem(newItem)
|
||||
|
||||
self.setActiveHandle(self._actHandle)
|
||||
|
||||
logger.verbose("Novel Tree built in %.3f ms", (time() - tStart)*1000)
|
||||
self._lastBuild = time()
|
||||
|
||||
@@ -607,4 +667,49 @@ class GuiNovelTree(QTreeWidget):
|
||||
|
||||
return "", ""
|
||||
|
||||
def _popMetaBox(self, qPos, tHandle, sTitle):
|
||||
"""Show the novel meta data box.
|
||||
"""
|
||||
logger.debug("Generating meta data tooltip for '%s:%s'", tHandle, sTitle)
|
||||
|
||||
pIndex = self.theProject.index
|
||||
novIdx = pIndex.getNovelData(tHandle, sTitle)
|
||||
refTags = pIndex.getReferences(tHandle, sTitle)
|
||||
|
||||
synopText = novIdx.synopsis
|
||||
if synopText:
|
||||
synopLabel = trConst(nwLabels.OUTLINE_COLS[nwOutline.SYNOP])
|
||||
synopText = f"<p><b>{synopLabel}</b>: {synopText}</p>"
|
||||
|
||||
refLines = []
|
||||
refLines = self._appendMetaTag(refTags, nwKeyWords.POV_KEY, refLines)
|
||||
refLines = self._appendMetaTag(refTags, nwKeyWords.FOCUS_KEY, refLines)
|
||||
refLines = self._appendMetaTag(refTags, nwKeyWords.CHAR_KEY, refLines)
|
||||
refLines = self._appendMetaTag(refTags, nwKeyWords.PLOT_KEY, refLines)
|
||||
refLines = self._appendMetaTag(refTags, nwKeyWords.TIME_KEY, refLines)
|
||||
refLines = self._appendMetaTag(refTags, nwKeyWords.WORLD_KEY, refLines)
|
||||
refLines = self._appendMetaTag(refTags, nwKeyWords.OBJECT_KEY, refLines)
|
||||
refLines = self._appendMetaTag(refTags, nwKeyWords.ENTITY_KEY, refLines)
|
||||
refLines = self._appendMetaTag(refTags, nwKeyWords.CUSTOM_KEY, refLines)
|
||||
|
||||
refText = ""
|
||||
if refLines:
|
||||
refList = "<br>".join(refLines)
|
||||
refText = f"<p>{refList}</p>"
|
||||
|
||||
ttText = refText + synopText or self.tr("No meta data")
|
||||
if ttText:
|
||||
QToolTip.showText(qPos, ttText)
|
||||
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def _appendMetaTag(refs, key, lines):
|
||||
"""Generate a reference list for a given reference key.
|
||||
"""
|
||||
tags = ", ".join(refs.get(key, []))
|
||||
if tags:
|
||||
lines.append(f"<b>{trConst(nwLabels.KEY_NAME[key])}</b>: {tags}")
|
||||
return lines
|
||||
|
||||
# END Class GuiNovelTree
|
||||
|
||||
@@ -326,6 +326,7 @@ class GuiOutlineTree(QTreeWidget):
|
||||
self.theProject = theOutline.mainGui.theProject
|
||||
self.mainTheme = theOutline.mainGui.mainTheme
|
||||
|
||||
self.setUniformRowHeights(True)
|
||||
self.setFrameStyle(QFrame.NoFrame)
|
||||
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
|
||||
@@ -350,7 +350,10 @@ class GuiProjectTree(QTreeWidget):
|
||||
|
||||
self.setIconSize(QSize(iPx, iPx))
|
||||
self.setFrameStyle(QFrame.NoFrame)
|
||||
self.setUniformRowHeights(True)
|
||||
self.setAllColumnsShowFocus(True)
|
||||
self.setExpandsOnDoubleClick(False)
|
||||
self.setAutoExpandDelay(1000)
|
||||
self.setHeaderHidden(True)
|
||||
self.setIndentation(iPx)
|
||||
self.setColumnCount(4)
|
||||
|
||||
@@ -474,7 +474,7 @@ class GuiIcons:
|
||||
"bullet-on", "bullet-off",
|
||||
|
||||
# Decorations
|
||||
"deco_doc_h0", "deco_doc_h1", "deco_doc_h2", "deco_doc_h3", "deco_doc_h4",
|
||||
"deco_doc_h0", "deco_doc_h1", "deco_doc_h2", "deco_doc_h3", "deco_doc_h4", "deco_doc_more",
|
||||
}
|
||||
|
||||
IMAGE_MAP = {
|
||||
|
||||
@@ -558,7 +558,7 @@ class GuiMain(QMainWindow):
|
||||
# Document Actions
|
||||
##
|
||||
|
||||
def closeDocument(self):
|
||||
def closeDocument(self, beforeOpen=False):
|
||||
"""Close the document and clear the editor and title field.
|
||||
"""
|
||||
if not self.hasProject:
|
||||
@@ -573,6 +573,8 @@ class GuiMain(QMainWindow):
|
||||
if self.docEditor.docChanged():
|
||||
self.saveDocument()
|
||||
self.docEditor.clearEditor()
|
||||
if not beforeOpen:
|
||||
self.novelView.setActiveHandle(None)
|
||||
|
||||
return True
|
||||
|
||||
@@ -587,13 +589,14 @@ class GuiMain(QMainWindow):
|
||||
logger.debug("Requested item '%s' is not a document", tHandle)
|
||||
return False
|
||||
|
||||
self.closeDocument()
|
||||
self.closeDocument(beforeOpen=True)
|
||||
self._changeView(nwView.EDITOR)
|
||||
if self.docEditor.loadText(tHandle, tLine):
|
||||
if changeFocus:
|
||||
self.docEditor.setFocus()
|
||||
self.theProject.setLastEdited(tHandle)
|
||||
self.projView.setSelectedHandle(tHandle, doScroll=doScroll)
|
||||
self.novelView.setActiveHandle(tHandle)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
@@ -13,8 +13,6 @@ hidehscroll = False
|
||||
[Sizes]
|
||||
geometry = 1200, 650
|
||||
preferences = 700, 615
|
||||
treecols = 200, 50, 30
|
||||
novelcols = 200, 50
|
||||
projcols = 200, 60, 140
|
||||
mainpane = 300, 800
|
||||
docpane = 400, 400
|
||||
@@ -30,7 +28,7 @@ emphlabels = True
|
||||
[Editor]
|
||||
textfont = None
|
||||
textsize = 12
|
||||
width = 600
|
||||
width = 700
|
||||
margin = 40
|
||||
tabwidth = 40
|
||||
focuswidth = 800
|
||||
|
||||
@@ -13,8 +13,6 @@ hidehscroll = True
|
||||
[Sizes]
|
||||
geometry = 1200, 650
|
||||
preferences = 670, 589
|
||||
treecols = 200, 50, 30
|
||||
novelcols = 200, 50
|
||||
projcols = 200, 60, 140
|
||||
mainpane = 300, 800
|
||||
docpane = 400, 400
|
||||
|
||||
@@ -410,32 +410,6 @@ def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
|
||||
|
||||
assert tmpConf.setPreferencesSize(700, 615)
|
||||
|
||||
# Project Tree Columns
|
||||
tmpConf.guiScale = 2.0
|
||||
assert tmpConf.setTreeColWidths([10, 20, 25])
|
||||
assert tmpConf.getTreeColWidths() == [10, 20, 24]
|
||||
assert tmpConf.treeColWidth == [5, 10, 12]
|
||||
|
||||
tmpConf.guiScale = 1.0
|
||||
assert tmpConf.setTreeColWidths([10, 20, 25])
|
||||
assert tmpConf.getTreeColWidths() == [10, 20, 25]
|
||||
assert tmpConf.treeColWidth == [10, 20, 25]
|
||||
|
||||
assert tmpConf.setTreeColWidths([200, 50, 30])
|
||||
|
||||
# Novel Tree Columns
|
||||
tmpConf.guiScale = 2.0
|
||||
assert tmpConf.setNovelColWidths([10, 20])
|
||||
assert tmpConf.getNovelColWidths() == [10, 20]
|
||||
assert tmpConf.novelColWidth == [5, 10]
|
||||
|
||||
tmpConf.guiScale = 1.0
|
||||
assert tmpConf.setNovelColWidths([10, 20])
|
||||
assert tmpConf.getNovelColWidths() == [10, 20]
|
||||
assert tmpConf.novelColWidth == [10, 20]
|
||||
|
||||
assert tmpConf.setNovelColWidths([200, 50])
|
||||
|
||||
# Project Settings Tree Columns
|
||||
tmpConf.guiScale = 2.0
|
||||
assert tmpConf.setProjColWidths([10, 20, 30])
|
||||
@@ -505,13 +479,13 @@ def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
|
||||
# ============
|
||||
|
||||
tmpConf.guiScale = 1.0
|
||||
assert tmpConf.getTextWidth(False) == 600
|
||||
assert tmpConf.getTextWidth(False) == 700
|
||||
assert tmpConf.getTextWidth(True) == 800
|
||||
assert tmpConf.getTextMargin() == 40
|
||||
assert tmpConf.getTabWidth() == 40
|
||||
|
||||
tmpConf.guiScale = 2.0
|
||||
assert tmpConf.getTextWidth(False) == 1200
|
||||
assert tmpConf.getTextWidth(False) == 1400
|
||||
assert tmpConf.getTextWidth(True) == 1600
|
||||
assert tmpConf.getTextMargin() == 80
|
||||
assert tmpConf.getTabWidth() == 80
|
||||
|
||||
@@ -239,8 +239,8 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
|
||||
copyfile(projFile, testFile)
|
||||
ignTuple = (
|
||||
"timestamp", "guifont", "lastnotes", "guilang", "geometry",
|
||||
"preferences", "treecols", "novelcols", "projcols", "mainpane",
|
||||
"docpane", "viewpane", "outlinepane", "textfont", "textsize"
|
||||
"preferences", "projcols", "mainpane", "docpane", "viewpane",
|
||||
"outlinepane", "textfont", "textsize"
|
||||
)
|
||||
assert cmpFiles(testFile, compFile, ignoreStart=ignTuple)
|
||||
|
||||
|
||||
@@ -24,8 +24,9 @@ import pytest
|
||||
|
||||
from tools import buildTestProject, writeFile
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
from PyQt5.QtGui import QFocusEvent
|
||||
from PyQt5.QtCore import Qt, QEvent
|
||||
from PyQt5.QtWidgets import QMessageBox, QToolTip
|
||||
|
||||
from novelwriter.enum import nwWidget, nwItemType
|
||||
from novelwriter.dialogs import GuiEditLabel
|
||||
@@ -53,8 +54,12 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
"# Jane Doe\n\n@tag: Jane\n\n"
|
||||
)
|
||||
writeFile(
|
||||
os.path.join(nwGUI.theProject.projContent, "000000000000f.nwd"),
|
||||
"### Scene One\n\n@pov: Jane\n@focus: Jane\n\n"
|
||||
os.path.join(nwGUI.theProject.projContent, "000000000000f.nwd"), (
|
||||
"### Scene One\n\n"
|
||||
"@pov: Jane\n"
|
||||
"@focus: Jane\n\n"
|
||||
"% Synopsis: This is a scene."
|
||||
)
|
||||
)
|
||||
|
||||
novelView = nwGUI.novelView
|
||||
@@ -124,12 +129,12 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
assert nwGUI.docViewer.docHandle() is None
|
||||
|
||||
scRect = novelTree.visualItemRect(scItem)
|
||||
oldData = scItem.data(novelTree.C_TITLE, Qt.UserRole)
|
||||
scItem.setData(novelTree.C_TITLE, Qt.UserRole, (None, "", ""))
|
||||
oldData = scItem.data(novelTree.C_TITLE, novelTree.D_HANDLE)
|
||||
scItem.setData(novelTree.C_TITLE, novelTree.D_HANDLE, None)
|
||||
qtbot.mouseClick(vPort, Qt.MiddleButton, pos=scRect.center(), delay=10)
|
||||
assert nwGUI.docViewer.docHandle() is None
|
||||
|
||||
scItem.setData(novelTree.C_TITLE, Qt.UserRole, oldData)
|
||||
scItem.setData(novelTree.C_TITLE, novelTree.D_HANDLE, oldData)
|
||||
qtbot.mouseClick(vPort, Qt.MiddleButton, pos=scRect.center(), delay=10)
|
||||
assert nwGUI.docViewer.docHandle() == "000000000000f"
|
||||
|
||||
@@ -137,26 +142,26 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
# ===========
|
||||
|
||||
novelBar.setLastColType(NovelTreeColumn.HIDDEN)
|
||||
assert novelTree.isColumnHidden(novelTree.C_LAST) is True
|
||||
assert novelTree.isColumnHidden(novelTree.C_EXTRA) is True
|
||||
assert novelTree.lastColType == NovelTreeColumn.HIDDEN
|
||||
assert novelTree._getLastColumnText("000000000000f", "T000001") == ("", "")
|
||||
|
||||
novelBar.setLastColType(NovelTreeColumn.POV)
|
||||
assert novelTree.isColumnHidden(novelTree.C_LAST) is False
|
||||
assert novelTree.isColumnHidden(novelTree.C_EXTRA) is False
|
||||
assert novelTree.lastColType == NovelTreeColumn.POV
|
||||
assert novelTree._getLastColumnText("000000000000f", "T000001") == (
|
||||
"Jane", "Point of View: Jane"
|
||||
)
|
||||
|
||||
novelBar.setLastColType(NovelTreeColumn.FOCUS)
|
||||
assert novelTree.isColumnHidden(novelTree.C_LAST) is False
|
||||
assert novelTree.isColumnHidden(novelTree.C_EXTRA) is False
|
||||
assert novelTree.lastColType == NovelTreeColumn.FOCUS
|
||||
assert novelTree._getLastColumnText("000000000000f", "T000001") == (
|
||||
"Jane", "Focus: Jane"
|
||||
)
|
||||
|
||||
novelBar.setLastColType(NovelTreeColumn.PLOT)
|
||||
assert novelTree.isColumnHidden(novelTree.C_LAST) is False
|
||||
assert novelTree.isColumnHidden(novelTree.C_EXTRA) is False
|
||||
assert novelTree.lastColType == NovelTreeColumn.PLOT
|
||||
assert novelTree._getLastColumnText("000000000000f", "T000001") == (
|
||||
"", "Plot: "
|
||||
@@ -165,6 +170,33 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
novelTree._lastCol = None
|
||||
assert novelTree._getLastColumnText("0000000000000", "T000000") == ("", "")
|
||||
|
||||
# Item Meta
|
||||
# =========
|
||||
|
||||
ttText = ""
|
||||
|
||||
def showText(pos, text):
|
||||
nonlocal ttText
|
||||
ttText = text
|
||||
|
||||
mIndex = novelTree.model().index(2, novelTree.C_MORE)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QToolTip, "showText", showText)
|
||||
novelTree._treeItemClicked(mIndex)
|
||||
assert ttText == (
|
||||
"<p><b>Point of View</b>: Jane<br><b>Focus</b>: Jane</p>"
|
||||
"<p><b>Synopsis</b>: This is a scene.</p>"
|
||||
)
|
||||
|
||||
# Other Checks
|
||||
# ============
|
||||
|
||||
scItem = novelTree.topLevelItem(2)
|
||||
scItem.setSelected(True)
|
||||
assert scItem.isSelected()
|
||||
novelTree.focusOutEvent(QFocusEvent(QEvent.None_, Qt.MouseFocusReason))
|
||||
assert not scItem.isSelected()
|
||||
|
||||
# Close
|
||||
# =====
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ def testGuiTheme_Main(qtbot, monkeypatch, nwMinimal, tmpDir):
|
||||
assert thePalette.window().color() == QColor(54, 54, 54)
|
||||
assert thePalette.windowText().color() == QColor(174, 174, 174)
|
||||
assert thePalette.base().color() == QColor(62, 62, 62)
|
||||
assert thePalette.alternateBase().color() == QColor(67, 67, 67)
|
||||
assert thePalette.alternateBase().color() == QColor(78, 78, 78)
|
||||
assert thePalette.text().color() == QColor(174, 174, 174)
|
||||
assert thePalette.toolTipBase().color() == QColor(255, 255, 192)
|
||||
assert thePalette.toolTipText().color() == QColor(21, 21, 13)
|
||||
|
||||
Reference in New Issue
Block a user