Improve handling of project closing

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