Improve handling of project closing
This commit is contained in:
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
# ===========
|
# ===========
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user