Improve handling of project closing
This commit is contained in:
@@ -382,9 +382,11 @@ class NWProject(QObject):
|
||||
self._storage.runPostSaveTasks(autoSave=autoSave)
|
||||
|
||||
# Update recent projects
|
||||
CONFIG.recentProjects.update(
|
||||
self._storage.storagePath, self._data.name, sum(self._data.currCounts), saveTime
|
||||
)
|
||||
storePath = self._storage.storagePath
|
||||
if storePath:
|
||||
CONFIG.recentProjects.update(
|
||||
storePath, self._data.name, sum(self._data.currCounts), saveTime
|
||||
)
|
||||
|
||||
self._storage.writeLockFile()
|
||||
self.mainGui.setStatus(self.tr("Saved Project: {0}").format(self._data.name))
|
||||
@@ -393,7 +395,7 @@ class NWProject(QObject):
|
||||
return True
|
||||
|
||||
def closeProject(self, idleTime: float = 0.0) -> None:
|
||||
"""Close the current project and clear all meta data."""
|
||||
"""Close the project."""
|
||||
logger.info("Closing project")
|
||||
self._options.saveSettings()
|
||||
self._tree.writeToCFile()
|
||||
|
||||
@@ -129,7 +129,7 @@ class NWStatus:
|
||||
|
||||
def name(self, key: str | None) -> str:
|
||||
"""Return the name associated with a given key."""
|
||||
if key in self._store:
|
||||
if key and key in self._store:
|
||||
return self._store[key]["name"]
|
||||
elif self._default is not None:
|
||||
return self._store[self._default]["name"]
|
||||
@@ -137,7 +137,7 @@ class NWStatus:
|
||||
|
||||
def cols(self, key: str | None) -> tuple[int, int, int]:
|
||||
"""Return the colours associated with a given key."""
|
||||
if key in self._store:
|
||||
if key and key in self._store:
|
||||
return self._store[key]["cols"]
|
||||
elif self._default is not None:
|
||||
return self._store[self._default]["cols"]
|
||||
@@ -145,7 +145,7 @@ class NWStatus:
|
||||
|
||||
def count(self, key: str | None) -> int:
|
||||
"""Return the count associated with a given key."""
|
||||
if key in self._store:
|
||||
if key and key in self._store:
|
||||
return self._store[key]["count"]
|
||||
elif self._default is not None:
|
||||
return self._store[self._default]["count"]
|
||||
@@ -153,7 +153,7 @@ class NWStatus:
|
||||
|
||||
def icon(self, key: str | None) -> QIcon:
|
||||
"""Return the icon associated with a given key."""
|
||||
if key in self._store:
|
||||
if key and key in self._store:
|
||||
return self._store[key]["icon"]
|
||||
elif self._default is not None:
|
||||
return self._store[self._default]["icon"]
|
||||
@@ -186,9 +186,9 @@ class NWStatus:
|
||||
self._store[key]["count"] = 0
|
||||
return
|
||||
|
||||
def increment(self, key: str) -> None:
|
||||
def increment(self, key: str | None) -> None:
|
||||
"""Increment the counter for a given entry."""
|
||||
if key in self._store:
|
||||
if key and key in self._store:
|
||||
self._store[key]["count"] += 1
|
||||
return
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ class GuiDocEditor(QTextEdit):
|
||||
self._wordCount = 0 # Word count
|
||||
self._paraCount = 0 # Paragraph count
|
||||
self._lastEdit = 0 # Time stamp of last edit
|
||||
self._lastActive = 0 # Time stamp of last activity
|
||||
self._lastActive = 0.0 # Time stamp of last activity
|
||||
self._lastFind = None # Position of the last found search word
|
||||
self._bigDoc = False # Flag for very large document size
|
||||
self._doReplace = False # Switch to temporarily disable auto-replace
|
||||
@@ -188,6 +188,34 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
##
|
||||
|
||||
@property
|
||||
def docChanged(self) -> bool:
|
||||
"""Return the changed status of the document."""
|
||||
return self._docChanged
|
||||
|
||||
@property
|
||||
def docHandle(self) -> str | None:
|
||||
"""Return the handle of the currently open document."""
|
||||
return self._docHandle
|
||||
|
||||
@property
|
||||
def lastActive(self) -> float:
|
||||
"""Return the last active timestamp for the user."""
|
||||
return self._lastActive
|
||||
|
||||
@property
|
||||
def isEmpty(self) -> bool:
|
||||
"""Check if the current document is empty."""
|
||||
return self.document().isEmpty()
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def clearEditor(self):
|
||||
"""Clear the current document and reset all document-related
|
||||
flags and counters.
|
||||
@@ -203,7 +231,7 @@ class GuiDocEditor(QTextEdit):
|
||||
self._wordCount = 0
|
||||
self._paraCount = 0
|
||||
self._lastEdit = 0
|
||||
self._lastActive = 0
|
||||
self._lastActive = 0.0
|
||||
self._lastFind = None
|
||||
self._bigDoc = False
|
||||
self._doReplace = False
|
||||
@@ -580,31 +608,6 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
##
|
||||
|
||||
def docChanged(self):
|
||||
"""Return the changed status of the document in the editor.
|
||||
"""
|
||||
return self._docChanged
|
||||
|
||||
def docHandle(self):
|
||||
"""Return the handle of the currently open document. Return
|
||||
None if no document is open.
|
||||
"""
|
||||
return self._docHandle
|
||||
|
||||
def lastActive(self):
|
||||
"""Return the last active timestamp for the user.
|
||||
"""
|
||||
return self._lastActive
|
||||
|
||||
def isEmpty(self):
|
||||
"""Wrapper function to check if the current document is empty.
|
||||
"""
|
||||
return self.document().isEmpty()
|
||||
|
||||
##
|
||||
# Getters
|
||||
##
|
||||
|
||||
+121
-134
@@ -30,10 +30,11 @@ from __future__ import annotations
|
||||
import logging
|
||||
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from PyQt5.QtCore import Qt, QUrl, QSize, pyqtSlot, pyqtSignal
|
||||
from PyQt5.QtCore import QPoint, Qt, QUrl, QSize, pyqtSlot, pyqtSignal
|
||||
from PyQt5.QtGui import (
|
||||
QTextOption, QFont, QPalette, QColor, QTextCursor, QIcon, QCursor
|
||||
QMouseEvent, QResizeEvent, QTextOption, QFont, QPalette, QColor, QTextCursor, QIcon, QCursor
|
||||
)
|
||||
from PyQt5.QtWidgets import (
|
||||
qApp, QTextBrowser, QWidget, QScrollArea, QLabel, QHBoxLayout, QToolButton,
|
||||
@@ -46,6 +47,9 @@ from novelwriter.error import logException
|
||||
from novelwriter.constants import nwUnicode
|
||||
from novelwriter.core.tohtml import ToHtml
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from novelwriter.guimain import GuiMain
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -53,7 +57,7 @@ class GuiDocViewer(QTextBrowser):
|
||||
|
||||
loadDocumentTagRequest = pyqtSignal(str, Enum)
|
||||
|
||||
def __init__(self, mainGui):
|
||||
def __init__(self, mainGui: GuiMain) -> None:
|
||||
super().__init__(parent=mainGui)
|
||||
|
||||
logger.debug("Create: GuiDocViewer")
|
||||
@@ -90,25 +94,43 @@ class GuiDocViewer(QTextBrowser):
|
||||
|
||||
return
|
||||
|
||||
def clearViewer(self):
|
||||
"""Clear the content of the document and reset key variables.
|
||||
"""
|
||||
##
|
||||
# Properties
|
||||
##
|
||||
|
||||
@property
|
||||
def docHandle(self) -> str | None:
|
||||
"""Return the handle of the currently open document."""
|
||||
return self._docHandle
|
||||
|
||||
@property
|
||||
def scrollPosition(self) -> int:
|
||||
"""Return the scrollbar position."""
|
||||
vBar = self.verticalScrollBar()
|
||||
if vBar.isVisible():
|
||||
return vBar.value()
|
||||
return 0
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def clearViewer(self) -> None:
|
||||
"""Clear the content of the document and reset key variables."""
|
||||
self.clear()
|
||||
self.setSearchPaths([""])
|
||||
self._docHandle = None
|
||||
self.docHeader.setTitleFromHandle(self._docHandle)
|
||||
return True
|
||||
return
|
||||
|
||||
def updateTheme(self):
|
||||
"""Update theme elements.
|
||||
"""
|
||||
def updateTheme(self) -> None:
|
||||
"""Update theme elements."""
|
||||
self.docHeader.updateTheme()
|
||||
self.docFooter.updateTheme()
|
||||
return
|
||||
|
||||
def initViewer(self):
|
||||
"""Set editor settings from main config.
|
||||
"""
|
||||
def initViewer(self) -> None:
|
||||
"""Set editor settings from main config."""
|
||||
self._makeStyleSheet()
|
||||
|
||||
# Set Font
|
||||
@@ -157,11 +179,10 @@ class GuiDocViewer(QTextBrowser):
|
||||
if self._docHandle is not None:
|
||||
self.reloadText()
|
||||
|
||||
return True
|
||||
return
|
||||
|
||||
def loadText(self, tHandle, updateHistory=True):
|
||||
"""Load text into the viewer from an item handle.
|
||||
"""
|
||||
def loadText(self, tHandle: str, updateHistory: bool = True) -> bool:
|
||||
"""Load text into the viewer from an item handle."""
|
||||
if not SHARED.project.tree.checkType(tHandle, nwItemType.FILE):
|
||||
logger.warning("Item not found")
|
||||
return False
|
||||
@@ -224,23 +245,20 @@ class GuiDocViewer(QTextBrowser):
|
||||
|
||||
return True
|
||||
|
||||
def reloadText(self):
|
||||
"""Reload the text in the current document.
|
||||
"""
|
||||
self.loadText(self._docHandle, updateHistory=False)
|
||||
def reloadText(self) -> None:
|
||||
"""Reload the text in the current document."""
|
||||
if self._docHandle:
|
||||
self.loadText(self._docHandle, updateHistory=False)
|
||||
return
|
||||
|
||||
def redrawText(self):
|
||||
"""Redraw the text by marking the document content as "dirty".
|
||||
"""
|
||||
def redrawText(self) -> None:
|
||||
"""Redraw the text by marking the content as "dirty"."""
|
||||
self.document().markContentsDirty(0, self.document().characterCount())
|
||||
self.updateDocMargins()
|
||||
return
|
||||
|
||||
def docAction(self, theAction):
|
||||
"""Wrapper function for various document actions on the current
|
||||
document.
|
||||
"""
|
||||
def docAction(self, theAction: nwDocAction) -> bool:
|
||||
"""Process document actions on the current document."""
|
||||
logger.debug("Requesting action: '%s'", theAction.name)
|
||||
if self._docHandle is None:
|
||||
logger.error("No document open")
|
||||
@@ -258,9 +276,8 @@ class GuiDocViewer(QTextBrowser):
|
||||
return False
|
||||
return True
|
||||
|
||||
def navigateTo(self, tAnchor):
|
||||
"""Go to a specific #link in the document.
|
||||
"""
|
||||
def navigateTo(self, tAnchor: str) -> bool:
|
||||
"""Go to a specific #link in the document."""
|
||||
if not isinstance(tAnchor, str):
|
||||
return False
|
||||
if tAnchor.startswith("#"):
|
||||
@@ -268,27 +285,13 @@ class GuiDocViewer(QTextBrowser):
|
||||
self.setSource(QUrl(tAnchor))
|
||||
return True
|
||||
|
||||
def navBackward(self):
|
||||
"""Navigate backwards in the document view history.
|
||||
"""
|
||||
self.docHistory.backward()
|
||||
return
|
||||
|
||||
def navForward(self):
|
||||
"""Navigate forwards in the document view history.
|
||||
"""
|
||||
self.docHistory.forward()
|
||||
return
|
||||
|
||||
def clearNavHistory(self):
|
||||
"""Clear the navigation history.
|
||||
"""
|
||||
def clearNavHistory(self) -> None:
|
||||
"""Clear the navigation history."""
|
||||
self.docHistory.clear()
|
||||
return
|
||||
|
||||
def updateDocMargins(self):
|
||||
"""Automatically adjust the margins so the text is centred.
|
||||
"""
|
||||
def updateDocMargins(self) -> None:
|
||||
"""Automatically adjust the margins so the text is centred."""
|
||||
wW = self.width()
|
||||
wH = self.height()
|
||||
cM = CONFIG.getTextMargin()
|
||||
@@ -320,41 +323,20 @@ class GuiDocViewer(QTextBrowser):
|
||||
# Setters
|
||||
##
|
||||
|
||||
def setScrollPosition(self, thePos):
|
||||
"""Set the scrollbar position.
|
||||
"""
|
||||
def setScrollPosition(self, pos: int) -> None:
|
||||
"""Set the scrollbar position."""
|
||||
vBar = self.verticalScrollBar()
|
||||
if vBar.isVisible():
|
||||
vBar.setValue(thePos)
|
||||
vBar.setValue(pos)
|
||||
return
|
||||
|
||||
##
|
||||
# Getters
|
||||
##
|
||||
|
||||
def docHandle(self):
|
||||
"""Return the handle of the currently open document. Returns
|
||||
None if no document is open.
|
||||
"""
|
||||
return self._docHandle
|
||||
|
||||
def getScrollPosition(self):
|
||||
"""Get the scrollbar position. Returns 0 if no scrollbar.
|
||||
"""
|
||||
vBar = self.verticalScrollBar()
|
||||
if vBar.isVisible():
|
||||
return vBar.value()
|
||||
return 0
|
||||
|
||||
##
|
||||
# Public Slots
|
||||
##
|
||||
|
||||
@pyqtSlot(str)
|
||||
def updateDocInfo(self, tHandle):
|
||||
"""Called when an item label is changed to check if the document
|
||||
title bar needs updating,
|
||||
"""
|
||||
def updateDocInfo(self, tHandle: str) -> None:
|
||||
"""Update the header titlebar if needed."""
|
||||
if tHandle == self._docHandle:
|
||||
self.docHeader.setTitleFromHandle(self._docHandle)
|
||||
self.updateDocMargins()
|
||||
@@ -364,11 +346,22 @@ class GuiDocViewer(QTextBrowser):
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
@pyqtSlot()
|
||||
def navBackward(self) -> None:
|
||||
"""Navigate backwards in the document view history."""
|
||||
self.docHistory.backward()
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def navForward(self) -> None:
|
||||
"""Navigate forwards in the document view history."""
|
||||
self.docHistory.forward()
|
||||
return
|
||||
|
||||
@pyqtSlot("QUrl")
|
||||
def _linkClicked(self, theURL):
|
||||
"""Process a clicked link internally in the document.
|
||||
"""
|
||||
theLink = theURL.url()
|
||||
def _linkClicked(self, url: QUrl) -> None:
|
||||
"""Process a clicked link internally in the document."""
|
||||
theLink = url.url()
|
||||
logger.debug("Clicked link: '%s'", theLink)
|
||||
if len(theLink) > 0:
|
||||
theBits = theLink.split("=")
|
||||
@@ -377,9 +370,8 @@ class GuiDocViewer(QTextBrowser):
|
||||
return
|
||||
|
||||
@pyqtSlot("QPoint")
|
||||
def _openContextMenu(self, thePos):
|
||||
"""Triggered by right click to open the context menu.
|
||||
"""
|
||||
def _openContextMenu(self, point: QPoint) -> None:
|
||||
"""Open context menu at location."""
|
||||
userCursor = self.textCursor()
|
||||
userSelection = userCursor.hasSelection()
|
||||
|
||||
@@ -404,18 +396,18 @@ class GuiDocViewer(QTextBrowser):
|
||||
|
||||
mnuSelWord = QAction(self.tr("Select Word"), mnuContext)
|
||||
mnuSelWord.triggered.connect(
|
||||
lambda: self._makePosSelection(QTextCursor.WordUnderCursor, thePos)
|
||||
lambda: self._makePosSelection(QTextCursor.WordUnderCursor, point)
|
||||
)
|
||||
mnuContext.addAction(mnuSelWord)
|
||||
|
||||
mnuSelPara = QAction(self.tr("Select Paragraph"), mnuContext)
|
||||
mnuSelPara.triggered.connect(
|
||||
lambda: self._makePosSelection(QTextCursor.BlockUnderCursor, thePos)
|
||||
lambda: self._makePosSelection(QTextCursor.BlockUnderCursor, point)
|
||||
)
|
||||
mnuContext.addAction(mnuSelPara)
|
||||
|
||||
# Open the context menu
|
||||
mnuContext.exec_(self.viewport().mapToGlobal(thePos))
|
||||
mnuContext.exec_(self.viewport().mapToGlobal(point))
|
||||
|
||||
return
|
||||
|
||||
@@ -423,37 +415,33 @@ class GuiDocViewer(QTextBrowser):
|
||||
# Events
|
||||
##
|
||||
|
||||
def resizeEvent(self, theEvent):
|
||||
"""If the text editor is resized, we must make sure the document
|
||||
has its margins adjusted according to user preferences.
|
||||
"""
|
||||
def resizeEvent(self, event: QResizeEvent) -> None:
|
||||
"""Update document margins when widget is resized."""
|
||||
self.updateDocMargins()
|
||||
super().resizeEvent(theEvent)
|
||||
super().resizeEvent(event)
|
||||
return
|
||||
|
||||
def mouseReleaseEvent(self, theEvent):
|
||||
"""Capture mouse click events on the document.
|
||||
"""
|
||||
if theEvent.button() == Qt.BackButton:
|
||||
def mouseReleaseEvent(self, event: QMouseEvent) -> None:
|
||||
"""Capture mouse click events on the document."""
|
||||
if event.button() == Qt.BackButton:
|
||||
self.navBackward()
|
||||
elif theEvent.button() == Qt.ForwardButton:
|
||||
elif event.button() == Qt.ForwardButton:
|
||||
self.navForward()
|
||||
else:
|
||||
super().mouseReleaseEvent(theEvent)
|
||||
super().mouseReleaseEvent(event)
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _makeSelection(self, selMode):
|
||||
"""Wrapper function to select text based on a selection mode.
|
||||
"""
|
||||
def _makeSelection(self, selType: QTextCursor.SelectionType) -> None:
|
||||
"""Handle select of text based on a selection mode."""
|
||||
theCursor = self.textCursor()
|
||||
theCursor.clearSelection()
|
||||
theCursor.select(selMode)
|
||||
theCursor.select(selType)
|
||||
|
||||
if selMode == QTextCursor.BlockUnderCursor:
|
||||
if selType == QTextCursor.BlockUnderCursor:
|
||||
# This selection mode also selects the preceding paragraph
|
||||
# separator, which we want to avoid.
|
||||
posS = theCursor.selectionStart()
|
||||
@@ -467,19 +455,18 @@ class GuiDocViewer(QTextBrowser):
|
||||
|
||||
return
|
||||
|
||||
def _makePosSelection(self, selMode, thePos):
|
||||
"""Wrapper function to select text based on selection mode, but
|
||||
first move cursor to given position.
|
||||
"""
|
||||
theCursor = self.cursorForPosition(thePos)
|
||||
def _makePosSelection(self, selType: QTextCursor.SelectionType, pos: QPoint) -> None:
|
||||
"""Handle text selection at a given location."""
|
||||
theCursor = self.cursorForPosition(pos)
|
||||
self.setTextCursor(theCursor)
|
||||
self._makeSelection(selMode)
|
||||
self._makeSelection(selType)
|
||||
return
|
||||
|
||||
def _makeStyleSheet(self):
|
||||
def _makeStyleSheet(self) -> None:
|
||||
"""Generate an appropriate style sheet for the document viewer,
|
||||
based on the current syntax highlighter theme,
|
||||
"""
|
||||
pTheme = SHARED.theme
|
||||
styleSheet = (
|
||||
"body {{"
|
||||
" color: rgb({tColR}, {tColG}, {tColB});"
|
||||
@@ -506,31 +493,31 @@ class GuiDocViewer(QTextBrowser):
|
||||
" text-align: center;"
|
||||
"}}\n"
|
||||
).format(
|
||||
tColR=SHARED.theme.colText[0],
|
||||
tColG=SHARED.theme.colText[1],
|
||||
tColB=SHARED.theme.colText[2],
|
||||
hColR=SHARED.theme.colHead[0],
|
||||
hColG=SHARED.theme.colHead[1],
|
||||
hColB=SHARED.theme.colHead[2],
|
||||
aColR=SHARED.theme.colVal[0],
|
||||
aColG=SHARED.theme.colVal[1],
|
||||
aColB=SHARED.theme.colVal[2],
|
||||
eColR=SHARED.theme.colEmph[0],
|
||||
eColG=SHARED.theme.colEmph[1],
|
||||
eColB=SHARED.theme.colEmph[2],
|
||||
kColR=SHARED.theme.colKey[0],
|
||||
kColG=SHARED.theme.colKey[1],
|
||||
kColB=SHARED.theme.colKey[2],
|
||||
cColR=SHARED.theme.colHidden[0],
|
||||
cColG=SHARED.theme.colHidden[1],
|
||||
cColB=SHARED.theme.colHidden[2],
|
||||
mColR=SHARED.theme.colMod[0],
|
||||
mColG=SHARED.theme.colMod[1],
|
||||
mColB=SHARED.theme.colMod[2],
|
||||
tColR=pTheme.colText[0],
|
||||
tColG=pTheme.colText[1],
|
||||
tColB=pTheme.colText[2],
|
||||
hColR=pTheme.colHead[0],
|
||||
hColG=pTheme.colHead[1],
|
||||
hColB=pTheme.colHead[2],
|
||||
aColR=pTheme.colVal[0],
|
||||
aColG=pTheme.colVal[1],
|
||||
aColB=pTheme.colVal[2],
|
||||
eColR=pTheme.colEmph[0],
|
||||
eColG=pTheme.colEmph[1],
|
||||
eColB=pTheme.colEmph[2],
|
||||
kColR=pTheme.colKey[0],
|
||||
kColG=pTheme.colKey[1],
|
||||
kColB=pTheme.colKey[2],
|
||||
cColR=pTheme.colHidden[0],
|
||||
cColG=pTheme.colHidden[1],
|
||||
cColB=pTheme.colHidden[2],
|
||||
mColR=pTheme.colMod[0],
|
||||
mColG=pTheme.colMod[1],
|
||||
mColB=pTheme.colMod[2],
|
||||
)
|
||||
self.document().setDefaultStyleSheet(styleSheet)
|
||||
|
||||
return True
|
||||
return
|
||||
|
||||
# END Class GuiDocViewer
|
||||
|
||||
@@ -628,7 +615,7 @@ class GuiDocViewHistory:
|
||||
"""Update the scrollbar position of the previous entry.
|
||||
"""
|
||||
if self._prevPos >= 0 and self._prevPos < len(self._posHistory):
|
||||
self._posHistory[self._prevPos] = self.docViewer.getScrollPosition()
|
||||
self._posHistory[self._prevPos] = self.docViewer.scrollPosition
|
||||
return
|
||||
|
||||
def _updateNavButtons(self):
|
||||
@@ -864,7 +851,7 @@ class GuiDocViewHeader(QWidget):
|
||||
def _refreshDocument(self):
|
||||
"""Reload the content of the document.
|
||||
"""
|
||||
if self.docViewer.docHandle() == self.mainGui.docEditor.docHandle():
|
||||
if self.docViewer.docHandle == self.mainGui.docEditor.docHandle:
|
||||
self.mainGui.saveDocument()
|
||||
self.docViewer.reloadText()
|
||||
return
|
||||
@@ -1102,8 +1089,8 @@ class GuiDocViewFooter(QWidget):
|
||||
"""
|
||||
logger.debug("Reference sticky is %s", str(theState))
|
||||
self.docViewer.stickyRef = theState
|
||||
if not theState and self.docViewer.docHandle() is not None:
|
||||
self.viewMeta.refreshReferences(self.docViewer.docHandle())
|
||||
if not theState and self.docViewer.docHandle is not None:
|
||||
self.viewMeta.refreshReferences(self.docViewer.docHandle)
|
||||
return
|
||||
|
||||
@pyqtSlot(bool)
|
||||
|
||||
@@ -25,19 +25,24 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from PyQt5.QtGui import QFont
|
||||
from PyQt5.QtCore import Qt, pyqtSlot
|
||||
from PyQt5.QtGui import QFont, QPixmap
|
||||
from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.constants import trConst, nwLabels
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from novelwriter.guimain import GuiMain
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GuiItemDetails(QWidget):
|
||||
|
||||
def __init__(self, mainGui):
|
||||
def __init__(self, mainGui: GuiMain) -> None:
|
||||
super().__init__(parent=mainGui)
|
||||
|
||||
logger.debug("Create: GuiItemDetails")
|
||||
@@ -187,35 +192,28 @@ class GuiItemDetails(QWidget):
|
||||
# Class Methods
|
||||
##
|
||||
|
||||
def clearDetails(self):
|
||||
"""Clear all the data values.
|
||||
"""
|
||||
def clearDetails(self) -> None:
|
||||
"""Clear all the data values."""
|
||||
self._itemHandle = None
|
||||
|
||||
self.labelIcon.setPixmap(QPixmap(1, 1))
|
||||
self.statusIcon.setPixmap(QPixmap(1, 1))
|
||||
self.classIcon.setText("")
|
||||
self.usageIcon.setText("")
|
||||
|
||||
self.labelData.setText("–")
|
||||
self.statusData.setText("–")
|
||||
self.classData.setText("–")
|
||||
self.usageData.setText("–")
|
||||
|
||||
self.cCountData.setText("–")
|
||||
self.wCountData.setText("–")
|
||||
self.pCountData.setText("–")
|
||||
|
||||
self.labelIcon.clear()
|
||||
self.labelData.clear()
|
||||
self.statusIcon.clear()
|
||||
self.statusData.clear()
|
||||
self.classIcon.clear()
|
||||
self.classData.clear()
|
||||
self.usageIcon.clear()
|
||||
self.usageData.clear()
|
||||
self.cCountData.clear()
|
||||
self.wCountData.clear()
|
||||
self.pCountData.clear()
|
||||
return
|
||||
|
||||
def refreshDetails(self):
|
||||
"""Reload the content of the details panel.
|
||||
"""
|
||||
def refreshDetails(self) -> None:
|
||||
"""Reload the content of the details panel."""
|
||||
self.updateViewBox(self._itemHandle)
|
||||
|
||||
def updateTheme(self):
|
||||
"""Update theme elements.
|
||||
"""
|
||||
def updateTheme(self) -> None:
|
||||
"""Update theme elements."""
|
||||
self.updateViewBox(self._itemHandle)
|
||||
return
|
||||
|
||||
@@ -224,9 +222,8 @@ class GuiItemDetails(QWidget):
|
||||
##
|
||||
|
||||
@pyqtSlot(str)
|
||||
def updateViewBox(self, tHandle):
|
||||
"""Populate the details box from a given handle.
|
||||
"""
|
||||
def updateViewBox(self, tHandle: str) -> None:
|
||||
"""Populate the details box from a given handle."""
|
||||
if tHandle is None:
|
||||
self.clearDetails()
|
||||
return
|
||||
@@ -294,7 +291,7 @@ class GuiItemDetails(QWidget):
|
||||
return
|
||||
|
||||
@pyqtSlot(str, int, int, int)
|
||||
def updateCounts(self, tHandle, cC, wC, pC):
|
||||
def updateCounts(self, tHandle: str, cC: int, wC: int, pC: int) -> None:
|
||||
"""Update the counts if the handle is the same as the one we're
|
||||
already showing. Otherwise, do nothing.
|
||||
"""
|
||||
@@ -302,7 +299,6 @@ class GuiItemDetails(QWidget):
|
||||
self.cCountData.setText(f"{cC:n}")
|
||||
self.wCountData.setText(f"{wC:n}")
|
||||
self.pCountData.setText(f"{pC:n}")
|
||||
|
||||
return
|
||||
|
||||
# END Class GuiItemDetails
|
||||
|
||||
@@ -349,13 +349,13 @@ class GuiMainMenu(QMenuBar):
|
||||
# View > Go Backward
|
||||
self.aViewPrev = QAction(self.tr("Navigate Backward"), self)
|
||||
self.aViewPrev.setShortcut("Alt+Left")
|
||||
self.aViewPrev.triggered.connect(lambda: self.mainGui.docViewer.navBackward())
|
||||
self.aViewPrev.triggered.connect(self.mainGui.docViewer.navBackward)
|
||||
self.viewMenu.addAction(self.aViewPrev)
|
||||
|
||||
# View > Go Forward
|
||||
self.aViewNext = QAction(self.tr("Navigate Forward"), self)
|
||||
self.aViewNext.setShortcut("Alt+Right")
|
||||
self.aViewNext.triggered.connect(lambda: self.mainGui.docViewer.navForward())
|
||||
self.aViewNext.triggered.connect(self.mainGui.docViewer.navForward)
|
||||
self.viewMenu.addAction(self.aViewNext)
|
||||
|
||||
# View > Separator
|
||||
|
||||
@@ -106,7 +106,7 @@ class GuiNovelView(QWidget):
|
||||
self.novelTree.initSettings()
|
||||
return
|
||||
|
||||
def clearProject(self):
|
||||
def clearNovelView(self):
|
||||
"""Clear project-related GUI content.
|
||||
"""
|
||||
self.novelTree.clearContent()
|
||||
@@ -130,8 +130,7 @@ class GuiNovelView(QWidget):
|
||||
"GuiNovelView", "lastColSize", 25
|
||||
)
|
||||
|
||||
self.clearProject()
|
||||
|
||||
self.clearNovelView()
|
||||
self.novelBar.buildNovelRootMenu()
|
||||
self.novelBar.setLastColType(lastCol, doRefresh=False)
|
||||
self.novelBar.setCurrentRoot(lastNovel)
|
||||
@@ -142,13 +141,13 @@ class GuiNovelView(QWidget):
|
||||
return
|
||||
|
||||
def closeProjectTasks(self):
|
||||
"""Run closing project tasks.
|
||||
"""
|
||||
"""Run closing project tasks."""
|
||||
lastColType = self.novelTree.lastColType
|
||||
lastColSize = self.novelTree.lastColSize
|
||||
pOptions = SHARED.project.options
|
||||
pOptions.setValue("GuiNovelView", "lastCol", lastColType)
|
||||
pOptions.setValue("GuiNovelView", "lastColSize", lastColSize)
|
||||
self.clearNovelView()
|
||||
return
|
||||
|
||||
def setTreeFocus(self):
|
||||
|
||||
@@ -118,7 +118,7 @@ class GuiOutlineView(QWidget):
|
||||
self.outlineTree.refreshTree(rootHandle=SHARED.project.data.getLastHandle("outline"))
|
||||
return
|
||||
|
||||
def clearProject(self):
|
||||
def clearOutline(self):
|
||||
"""Clear project-related GUI content.
|
||||
"""
|
||||
self.outlineData.clearDetails()
|
||||
@@ -134,7 +134,7 @@ class GuiOutlineView(QWidget):
|
||||
|
||||
logger.debug("Setting outline tree to root item '%s'", lastOutline)
|
||||
|
||||
self.clearProject()
|
||||
self.clearOutline()
|
||||
self.outlineBar.populateNovelList()
|
||||
self.outlineBar.setCurrentRoot(lastOutline)
|
||||
self.outlineBar.setEnabled(True)
|
||||
@@ -144,6 +144,7 @@ class GuiOutlineView(QWidget):
|
||||
def closeProjectTasks(self):
|
||||
self.outlineTree.closeProjectTasks()
|
||||
self.outlineData.updateClasses()
|
||||
self.clearOutline()
|
||||
return
|
||||
|
||||
def splitSizes(self):
|
||||
|
||||
@@ -165,7 +165,7 @@ class GuiProjectView(QWidget):
|
||||
self.projTree.initSettings()
|
||||
return
|
||||
|
||||
def clearProject(self) -> None:
|
||||
def clearProjectView(self) -> None:
|
||||
"""Clear project-related GUI content."""
|
||||
self.projBar.clearContent()
|
||||
self.projBar.setEnabled(False)
|
||||
@@ -964,7 +964,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
trItemP.takeChild(tIndex)
|
||||
|
||||
for dHandle in reversed(self.getTreeFromHandle(tHandle)):
|
||||
if self.mainGui.docEditor.docHandle() == dHandle:
|
||||
if self.mainGui.docEditor.docHandle == dHandle:
|
||||
self.mainGui.closeDocument()
|
||||
SHARED.project.removeItem(dHandle)
|
||||
self._treeMap.pop(dHandle, None)
|
||||
|
||||
+19
-32
@@ -236,7 +236,7 @@ class GuiMain(QMainWindow):
|
||||
# Connect Signals
|
||||
# ===============
|
||||
|
||||
SHARED.project.projectStatusChanged.connect(self.mainStatus.doUpdateProjectStatus)
|
||||
SHARED.projectStatusChanged.connect(self.mainStatus.doUpdateProjectStatus)
|
||||
|
||||
self.viewsBar.viewChangeRequested.connect(self._changeView)
|
||||
|
||||
@@ -337,25 +337,6 @@ class GuiMain(QMainWindow):
|
||||
|
||||
return
|
||||
|
||||
def clearGUI(self) -> None:
|
||||
"""Clear all sub-elements of the main GUI."""
|
||||
# Project Area
|
||||
self.projView.clearProject()
|
||||
self.novelView.clearProject()
|
||||
self.itemDetails.clearDetails()
|
||||
|
||||
# Work Area
|
||||
self.docEditor.clearEditor()
|
||||
self.docEditor.setDictionaries()
|
||||
self.closeDocViewer(byUser=False)
|
||||
self.outlineView.clearProject()
|
||||
|
||||
# General
|
||||
self.mainStatus.clearStatus()
|
||||
self._updateWindowTitle()
|
||||
|
||||
return
|
||||
|
||||
def initMain(self) -> None:
|
||||
"""Initialise elements that depend on user settings."""
|
||||
self.asProjTimer.setInterval(int(CONFIG.autoSaveProj*1000))
|
||||
@@ -446,7 +427,7 @@ class GuiMain(QMainWindow):
|
||||
if not msgYes:
|
||||
return False
|
||||
|
||||
if self.docEditor.docChanged():
|
||||
if self.docEditor.docChanged:
|
||||
self.saveDocument()
|
||||
|
||||
saveOK = self.saveProject()
|
||||
@@ -462,14 +443,20 @@ class GuiMain(QMainWindow):
|
||||
if saveOK:
|
||||
self.closeDocument()
|
||||
self.docViewer.clearNavHistory()
|
||||
self.closeDocViewer(byUser=False)
|
||||
|
||||
self.outlineView.closeProjectTasks()
|
||||
self.novelView.closeProjectTasks()
|
||||
self.projView.clearProjectView()
|
||||
self.itemDetails.clearDetails()
|
||||
self.mainStatus.clearStatus()
|
||||
|
||||
SHARED.closeProject(self.idleTime)
|
||||
self.idleRefTime = time()
|
||||
self.idleTime = 0.0
|
||||
|
||||
self.clearGUI()
|
||||
self.docEditor.setDictionaries()
|
||||
self._updateWindowTitle()
|
||||
self._changeView(nwView.PROJECT)
|
||||
|
||||
return saveOK
|
||||
@@ -594,7 +581,7 @@ class GuiMain(QMainWindow):
|
||||
self.toggleFocusMode()
|
||||
|
||||
self.docEditor.saveCursorPosition()
|
||||
if self.docEditor.docChanged():
|
||||
if self.docEditor.docChanged:
|
||||
self.saveDocument()
|
||||
self.docEditor.clearEditor()
|
||||
if not beforeOpen:
|
||||
@@ -614,7 +601,7 @@ class GuiMain(QMainWindow):
|
||||
return False
|
||||
|
||||
self._changeView(nwView.EDITOR)
|
||||
cHandle = self.docEditor.docHandle()
|
||||
cHandle = self.docEditor.docHandle
|
||||
if cHandle == tHandle:
|
||||
self.docEditor.setCursorLine(tLine)
|
||||
if changeFocus:
|
||||
@@ -682,7 +669,7 @@ class GuiMain(QMainWindow):
|
||||
logger.debug("Viewing document, but no handle provided")
|
||||
|
||||
if self.docEditor.hasFocus():
|
||||
tHandle = self.docEditor.docHandle()
|
||||
tHandle = self.docEditor.docHandle
|
||||
|
||||
if tHandle is not None:
|
||||
self.saveDocument()
|
||||
@@ -750,13 +737,13 @@ class GuiMain(QMainWindow):
|
||||
), level=nwAlert.ERROR, exc=exc)
|
||||
return False
|
||||
|
||||
if self.docEditor.docHandle() is None:
|
||||
if self.docEditor.docHandle is None:
|
||||
self.makeAlert(self.tr(
|
||||
"Please open a document to import the text file into."
|
||||
), level=nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
if not self.docEditor.isEmpty():
|
||||
if not self.docEditor.isEmpty:
|
||||
msgYes = self.askQuestion(self.tr(
|
||||
"Importing the file will overwrite the current content of "
|
||||
"the document. Do you want to proceed?"
|
||||
@@ -825,7 +812,7 @@ class GuiMain(QMainWindow):
|
||||
return False
|
||||
|
||||
if tHandle is None and (self.docEditor.anyFocus() or self.isFocusMode):
|
||||
tHandle = self.docEditor.docHandle()
|
||||
tHandle = self.docEditor.docHandle
|
||||
self.projView.renameTreeItem(tHandle)
|
||||
|
||||
return True
|
||||
@@ -1219,7 +1206,7 @@ class GuiMain(QMainWindow):
|
||||
"""Handle toggle focus mode. The Main GUI Focus Mode hides tree,
|
||||
view, statusbar and menu.
|
||||
"""
|
||||
if self.docEditor.docHandle() is None:
|
||||
if self.docEditor.docHandle is None:
|
||||
logger.error("No document open, so not activating Focus Mode")
|
||||
return False
|
||||
|
||||
@@ -1242,7 +1229,7 @@ class GuiMain(QMainWindow):
|
||||
|
||||
if self.splitView.isVisible():
|
||||
self.splitView.setVisible(False)
|
||||
elif self.docViewer.docHandle() is not None:
|
||||
elif self.docViewer.docHandle is not None:
|
||||
self.splitView.setVisible(True)
|
||||
|
||||
return True
|
||||
@@ -1474,7 +1461,7 @@ class GuiMain(QMainWindow):
|
||||
return
|
||||
|
||||
currTime = time()
|
||||
editIdle = currTime - self.docEditor.lastActive() > CONFIG.userIdleTime
|
||||
editIdle = currTime - self.docEditor.lastActive > CONFIG.userIdleTime
|
||||
userIdle = qApp.applicationState() != Qt.ApplicationActive
|
||||
|
||||
if editIdle or userIdle:
|
||||
@@ -1502,7 +1489,7 @@ class GuiMain(QMainWindow):
|
||||
@pyqtSlot()
|
||||
def _autoSaveDocument(self) -> None:
|
||||
"""Autosave of the document. This is a timer-activated slot."""
|
||||
if SHARED.hasProject and self.docEditor.docChanged():
|
||||
if SHARED.hasProject and self.docEditor.docChanged:
|
||||
logger.debug("Autosaving document")
|
||||
self.saveDocument()
|
||||
return
|
||||
|
||||
+18
-1
@@ -28,6 +28,8 @@ import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from pathlib import Path
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from novelwriter.guimain import GuiMain
|
||||
from novelwriter.gui.theme import GuiTheme
|
||||
@@ -36,9 +38,12 @@ if TYPE_CHECKING: # pragma: no cover
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SharedData:
|
||||
class SharedData(QObject):
|
||||
|
||||
projectStatusChanged = pyqtSignal(bool)
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._gui = None
|
||||
self._theme = None
|
||||
self._project = None
|
||||
@@ -121,6 +126,16 @@ class SharedData:
|
||||
"""Remove the project lock."""
|
||||
return self.project.storage.clearLockFile()
|
||||
|
||||
##
|
||||
# Internal Slots
|
||||
##
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def _processProjectStatusChange(self, state: bool) -> None:
|
||||
"""Forward the project status slot."""
|
||||
self.projectStatusChanged.emit(state)
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
@@ -129,8 +144,10 @@ class SharedData:
|
||||
"""Create a new project instance."""
|
||||
from novelwriter.core.project import NWProject
|
||||
if isinstance(self._project, NWProject):
|
||||
self._project.projectStatusChanged.disconnect()
|
||||
self._project.deleteLater()
|
||||
self._project = NWProject(self.mainGui)
|
||||
self._project.projectStatusChanged.connect(self._processProjectStatusChange)
|
||||
return
|
||||
|
||||
# END Class SharedData
|
||||
|
||||
@@ -194,10 +194,10 @@ def testGuiEditor_MetaData(qtbot, nwGUI, projPath, mockRnd):
|
||||
assert nwGUI.docEditor.getText() == "### New Scene\n\nSome\ntext.\nMore\u00a0text.\n"
|
||||
|
||||
# Check Propertoes
|
||||
assert nwGUI.docEditor.docChanged() is True
|
||||
assert nwGUI.docEditor.docHandle() == C.hSceneDoc
|
||||
assert nwGUI.docEditor.lastActive() > 0.0
|
||||
assert nwGUI.docEditor.isEmpty() is False
|
||||
assert nwGUI.docEditor.docChanged is True
|
||||
assert nwGUI.docEditor.docHandle == C.hSceneDoc
|
||||
assert nwGUI.docEditor.lastActive > 0.0
|
||||
assert nwGUI.docEditor.isEmpty is False
|
||||
|
||||
# Cursor Position
|
||||
assert nwGUI.docEditor.setCursorPosition(None) is False
|
||||
@@ -1361,7 +1361,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
|
||||
# Next match
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docHandle() == "2426c6f0ca922" # Next document
|
||||
assert nwGUI.docEditor.docHandle == "2426c6f0ca922" # Next document
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert abs(nwGUI.docEditor.getCursorPosition() - 620) < 3
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
@@ -1371,11 +1371,11 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
assert nwGUI.docEditor.docSearch.doNextFile is True
|
||||
assert nwGUI.docEditor.docSearch.setSearchText("abcdef")
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docHandle() != "2426c6f0ca922"
|
||||
assert nwGUI.docEditor.docHandle() == "04468803b92e1"
|
||||
assert nwGUI.docEditor.docHandle != "2426c6f0ca922"
|
||||
assert nwGUI.docEditor.docHandle == "04468803b92e1"
|
||||
nwGUI.mainMenu.aFindNext.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.docHandle() != "04468803b92e1"
|
||||
assert nwGUI.docEditor.docHandle() == "7a992350f3eb6"
|
||||
assert nwGUI.docEditor.docHandle != "04468803b92e1"
|
||||
assert nwGUI.docEditor.docHandle == "7a992350f3eb6"
|
||||
|
||||
# Toggle Replace
|
||||
nwGUI.docEditor.beginReplace()
|
||||
|
||||
@@ -50,7 +50,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
theItem = nwGUI.projView.projTree._getTreeItem("88243afbe5ed8")
|
||||
theRect = nwGUI.projView.projTree.visualItemRect(theItem)
|
||||
qtbot.mouseClick(nwGUI.projView.projTree.viewport(), Qt.MidButton, pos=theRect.center())
|
||||
assert nwGUI.docViewer.docHandle() == "88243afbe5ed8"
|
||||
assert nwGUI.docViewer.docHandle == "88243afbe5ed8"
|
||||
|
||||
# Reload the text
|
||||
origText = nwGUI.docViewer.toPlainText()
|
||||
@@ -97,7 +97,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
|
||||
# Close document
|
||||
nwGUI.docViewer.docHeader._closeDocument()
|
||||
assert nwGUI.docViewer.docHandle() is None
|
||||
assert nwGUI.docViewer.docHandle is None
|
||||
|
||||
# Action on no document
|
||||
assert nwGUI.docViewer.docAction(nwDocAction.COPY) is False
|
||||
@@ -114,17 +114,17 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
theRect = nwGUI.docViewer.cursorRect()
|
||||
# qtbot.mouseClick(nwGUI.docViewer.viewport(), Qt.LeftButton, pos=theRect.center(), delay=100)
|
||||
nwGUI.docViewer._linkClicked(QUrl("#char=Bod"))
|
||||
assert nwGUI.docViewer.docHandle() == "4c4f28287af27"
|
||||
assert nwGUI.docViewer.docHandle == "4c4f28287af27"
|
||||
|
||||
# Click mouse nav buttons
|
||||
qtbot.mouseClick(nwGUI.docViewer.viewport(), Qt.BackButton, pos=theRect.center(), delay=100)
|
||||
assert nwGUI.docViewer.docHandle() == "88243afbe5ed8"
|
||||
assert nwGUI.docViewer.docHandle == "88243afbe5ed8"
|
||||
qtbot.mouseClick(nwGUI.docViewer.viewport(), Qt.ForwardButton, pos=theRect.center(), delay=100)
|
||||
assert nwGUI.docViewer.docHandle() == "4c4f28287af27"
|
||||
assert nwGUI.docViewer.docHandle == "4c4f28287af27"
|
||||
|
||||
# Scroll bar default on empty document
|
||||
nwGUI.docViewer.clear()
|
||||
assert nwGUI.docViewer.getScrollPosition() == 0
|
||||
assert nwGUI.docViewer.scrollPosition == 0
|
||||
nwGUI.docViewer.reloadText()
|
||||
|
||||
# Change document title
|
||||
|
||||
@@ -153,10 +153,10 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
nwGUI.projStack.setCurrentIndex(0)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
||||
assert nwGUI.docEditor.docHandle() is None
|
||||
assert nwGUI.docEditor.docHandle is None
|
||||
nwGUI.projView.projTree._getTreeItem(sHandle).setSelected(True)
|
||||
nwGUI._keyPressReturn()
|
||||
assert nwGUI.docEditor.docHandle() == sHandle
|
||||
assert nwGUI.docEditor.docHandle == sHandle
|
||||
assert nwGUI.closeDocument() is True
|
||||
|
||||
# Novel Tree has focus
|
||||
@@ -164,11 +164,11 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
nwGUI.novelView.novelTree.refreshTree(rootHandle=None, overRide=True)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiNovelView, "treeHasFocus", lambda *a: True)
|
||||
assert nwGUI.docEditor.docHandle() is None
|
||||
assert nwGUI.docEditor.docHandle is None
|
||||
selItem = nwGUI.novelView.novelTree.topLevelItem(2)
|
||||
nwGUI.novelView.novelTree.setCurrentItem(selItem)
|
||||
nwGUI._keyPressReturn()
|
||||
assert nwGUI.docEditor.docHandle() == sHandle
|
||||
assert nwGUI.docEditor.docHandle == sHandle
|
||||
assert nwGUI.closeDocument() is True
|
||||
|
||||
# Project Outline has focus
|
||||
@@ -176,11 +176,11 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
nwGUI.switchFocus(nwWidget.OUTLINE)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiOutlineView, "treeHasFocus", lambda *a: True)
|
||||
assert nwGUI.docEditor.docHandle() is None
|
||||
assert nwGUI.docEditor.docHandle is None
|
||||
selItem = nwGUI.outlineView.outlineTree.topLevelItem(2)
|
||||
nwGUI.outlineView.outlineTree.setCurrentItem(selItem)
|
||||
nwGUI._keyPressReturn()
|
||||
assert nwGUI.docEditor.docHandle() == sHandle
|
||||
assert nwGUI.docEditor.docHandle == sHandle
|
||||
assert nwGUI.closeDocument() is True
|
||||
|
||||
# qtbot.stop()
|
||||
@@ -506,9 +506,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
||||
nwGUI.docEditor.wCounterDoc.run()
|
||||
|
||||
# Save the document
|
||||
assert nwGUI.docEditor.docChanged()
|
||||
assert nwGUI.docEditor.docChanged
|
||||
assert nwGUI.saveDocument()
|
||||
assert not nwGUI.docEditor.docChanged()
|
||||
assert not nwGUI.docEditor.docChanged
|
||||
nwGUI.rebuildIndex()
|
||||
|
||||
# Open and view the edited document
|
||||
|
||||
@@ -206,7 +206,7 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
|
||||
# Clear the Text
|
||||
nwGUI.docEditor.clear()
|
||||
assert nwGUI.docEditor.isEmpty()
|
||||
assert nwGUI.docEditor.isEmpty
|
||||
|
||||
# Alignment & Indent
|
||||
# ==================
|
||||
@@ -403,17 +403,17 @@ def testGuiMenu_ContextMenus(qtbot, nwGUI, prjLipsum):
|
||||
|
||||
# Navigation History
|
||||
assert nwGUI.viewDocument("04468803b92e1")
|
||||
assert nwGUI.docViewer.docHandle() == "04468803b92e1"
|
||||
assert nwGUI.docViewer.docHandle == "04468803b92e1"
|
||||
assert nwGUI.docViewer.docHeader.backButton.isEnabled()
|
||||
assert not nwGUI.docViewer.docHeader.forwardButton.isEnabled()
|
||||
|
||||
qtbot.mouseClick(nwGUI.docViewer.docHeader.backButton, Qt.LeftButton)
|
||||
assert nwGUI.docViewer.docHandle() == "4c4f28287af27"
|
||||
assert nwGUI.docViewer.docHandle == "4c4f28287af27"
|
||||
assert not nwGUI.docViewer.docHeader.backButton.isEnabled()
|
||||
assert nwGUI.docViewer.docHeader.forwardButton.isEnabled()
|
||||
|
||||
qtbot.mouseClick(nwGUI.docViewer.docHeader.forwardButton, Qt.LeftButton)
|
||||
assert nwGUI.docViewer.docHandle() == "04468803b92e1"
|
||||
assert nwGUI.docViewer.docHandle == "04468803b92e1"
|
||||
assert nwGUI.docViewer.docHeader.backButton.isEnabled()
|
||||
assert not nwGUI.docViewer.docHeader.forwardButton.isEnabled()
|
||||
|
||||
@@ -438,10 +438,10 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockRnd):
|
||||
nwGUI.docEditor.clear()
|
||||
|
||||
assert nwGUI.docEditor.insertText(nwDocInsert.NO_INSERT) is False
|
||||
assert nwGUI.docEditor.isEmpty()
|
||||
assert nwGUI.docEditor.isEmpty
|
||||
|
||||
assert nwGUI.docEditor.insertText(None) is False
|
||||
assert nwGUI.docEditor.isEmpty()
|
||||
assert nwGUI.docEditor.isEmpty
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
|
||||
@@ -118,26 +118,26 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
# Double-click item
|
||||
scItem.setSelected(True)
|
||||
assert scItem.isSelected()
|
||||
assert nwGUI.docEditor.docHandle() is None
|
||||
assert nwGUI.docEditor.docHandle is None
|
||||
novelTree._treeDoubleClick(scItem, 0)
|
||||
assert nwGUI.docEditor.docHandle() == C.hSceneDoc
|
||||
assert nwGUI.docEditor.docHandle == C.hSceneDoc
|
||||
|
||||
# Open item with middle mouse button
|
||||
scItem.setSelected(True)
|
||||
assert scItem.isSelected()
|
||||
assert nwGUI.docViewer.docHandle() is None
|
||||
assert nwGUI.docViewer.docHandle is None
|
||||
qtbot.mouseClick(vPort, Qt.MiddleButton, pos=vPort.rect().center(), delay=10)
|
||||
assert nwGUI.docViewer.docHandle() is None
|
||||
assert nwGUI.docViewer.docHandle is None
|
||||
|
||||
scRect = novelTree.visualItemRect(scItem)
|
||||
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
|
||||
assert nwGUI.docViewer.docHandle is None
|
||||
|
||||
scItem.setData(novelTree.C_TITLE, novelTree.D_HANDLE, oldData)
|
||||
qtbot.mouseClick(vPort, Qt.MiddleButton, pos=scRect.center(), delay=10)
|
||||
assert nwGUI.docViewer.docHandle() == C.hSceneDoc
|
||||
assert nwGUI.docViewer.docHandle == C.hSceneDoc
|
||||
|
||||
# Last Column
|
||||
# ===========
|
||||
|
||||
@@ -246,7 +246,7 @@ def testGuiOutline_Content(qtbot, nwGUI, prjLipsum):
|
||||
# Click POV Link
|
||||
assert outlineData.povKeyValue.text() == "<a href='Bod'>Bod</a>"
|
||||
outlineView._tagClicked("Bod")
|
||||
assert nwGUI.docViewer.docHandle() == "4c4f28287af27"
|
||||
assert nwGUI.docViewer.docHandle == "4c4f28287af27"
|
||||
|
||||
# Scene One, Section Two
|
||||
selItem = outlineTree.topLevelItem(5)
|
||||
@@ -262,7 +262,7 @@ def testGuiOutline_Content(qtbot, nwGUI, prjLipsum):
|
||||
assert outlineData.itemValue.text() == "Finished"
|
||||
|
||||
outlineTree._treeDoubleClick(selItem, 0)
|
||||
assert nwGUI.docEditor.docHandle() == "88243afbe5ed8"
|
||||
assert nwGUI.docEditor.docHandle == "88243afbe5ed8"
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
|
||||
@@ -450,10 +450,10 @@ def testGuiProjTree_PermanentlyDeleteItem(qtbot, caplog, monkeypatch, nwGUI, pro
|
||||
|
||||
# Deleting file is OK, and if it is open, it should close
|
||||
assert nwGUI.openDocument(C.hTitlePage) is True
|
||||
assert nwGUI.docEditor.docHandle() == C.hTitlePage
|
||||
assert nwGUI.docEditor.docHandle == C.hTitlePage
|
||||
assert projTree.permDeleteItem(C.hTitlePage) is True
|
||||
assert C.hTitlePage not in theProject.tree
|
||||
assert nwGUI.docEditor.docHandle() is None
|
||||
assert nwGUI.docEditor.docHandle is None
|
||||
|
||||
# Deleting folder + files recursively is ok
|
||||
assert projTree.permDeleteItem(C.hChapterDir) is True
|
||||
@@ -969,25 +969,25 @@ def testGuiProjTree_Other(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mockRnd)
|
||||
# Try to open a file with nothings selected
|
||||
projTree.clearSelection()
|
||||
projTree._treeDoubleClick(QTreeWidgetItem(), 0)
|
||||
assert nwGUI.docEditor.docHandle() is None
|
||||
assert nwGUI.docEditor.docHandle is None
|
||||
|
||||
# When the item cannot be found
|
||||
projTree._getTreeItem(C.hTitlePage).setSelected(True) # type: ignore
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.tree.NWTree.__getitem__", lambda *a: None)
|
||||
projTree._treeDoubleClick(QTreeWidgetItem(), 0)
|
||||
assert nwGUI.docEditor.docHandle() is None
|
||||
assert nwGUI.docEditor.docHandle is None
|
||||
|
||||
# Successfully open a file
|
||||
projTree._treeDoubleClick(projTree._getTreeItem(C.hTitlePage), 0)
|
||||
assert nwGUI.docEditor.docHandle() == C.hTitlePage
|
||||
assert nwGUI.docEditor.docHandle == C.hTitlePage
|
||||
projTree._getTreeItem(C.hTitlePage).setSelected(False) # type: ignore
|
||||
|
||||
# A non-file item should be expanded instead
|
||||
projTree._getTreeItem(C.hNovelRoot).setExpanded(False) # type: ignore
|
||||
projTree._getTreeItem(C.hNovelRoot).setSelected(True) # type: ignore
|
||||
projTree._treeDoubleClick(projTree._getTreeItem(C.hNovelRoot), 1)
|
||||
assert nwGUI.docEditor.docHandle() == C.hTitlePage
|
||||
assert nwGUI.docEditor.docHandle == C.hTitlePage
|
||||
assert projTree._getTreeItem(C.hNovelRoot).isExpanded() is True # type: ignore
|
||||
|
||||
# Navigate the Tree
|
||||
|
||||
Reference in New Issue
Block a user