GUI improvements and fixes (#2089)

This commit is contained in:
Veronica Berglyd Olsen
2024-11-08 22:02:35 +01:00
committed by GitHub
7 changed files with 82 additions and 29 deletions
+22 -5
View File
@@ -58,12 +58,13 @@ logger = logging.getLogger(__name__)
class GuiDocViewer(QTextBrowser):
closeDocumentRequest = pyqtSignal()
documentLoaded = pyqtSignal(str)
loadDocumentTagRequest = pyqtSignal(str, Enum)
closeDocumentRequest = pyqtSignal()
openDocumentRequest = pyqtSignal(str, Enum, str, bool)
reloadDocumentRequest = pyqtSignal()
togglePanelVisibility = pyqtSignal()
requestProjectItemSelected = pyqtSignal(str, bool)
togglePanelVisibility = pyqtSignal()
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
@@ -640,6 +641,11 @@ class GuiDocViewHeader(QWidget):
self.forwardButton.setToolTip(self.tr("Go Forward"))
self.forwardButton.clicked.connect(self.docViewer.navForward)
self.editButton = NIconToolButton(self, iSz)
self.editButton.setVisible(False)
self.editButton.setToolTip(self.tr("Open in Editor"))
self.editButton.clicked.connect(self._editDocument)
self.refreshButton = NIconToolButton(self, iSz)
self.refreshButton.setVisible(False)
self.refreshButton.setToolTip(self.tr("Reload"))
@@ -658,7 +664,7 @@ class GuiDocViewHeader(QWidget):
self.outerBox.addSpacing(mPx)
self.outerBox.addWidget(self.itemTitle, 1)
self.outerBox.addSpacing(mPx)
self.outerBox.addSpacing(iPx)
self.outerBox.addWidget(self.editButton, 0)
self.outerBox.addWidget(self.refreshButton, 0)
self.outerBox.addWidget(self.closeButton, 0)
self.outerBox.setSpacing(0)
@@ -692,8 +698,9 @@ class GuiDocViewHeader(QWidget):
self.outlineButton.setVisible(False)
self.backButton.setVisible(False)
self.forwardButton.setVisible(False)
self.closeButton.setVisible(False)
self.editButton.setVisible(False)
self.refreshButton.setVisible(False)
self.closeButton.setVisible(False)
return
def setOutline(self, data: dict[str, tuple[str, int]]) -> None:
@@ -727,6 +734,7 @@ class GuiDocViewHeader(QWidget):
self.outlineButton.setThemeIcon("list")
self.backButton.setThemeIcon("backward")
self.forwardButton.setThemeIcon("forward")
self.editButton.setThemeIcon("edit")
self.refreshButton.setThemeIcon("refresh")
self.closeButton.setThemeIcon("close")
@@ -734,6 +742,7 @@ class GuiDocViewHeader(QWidget):
self.outlineButton.setStyleSheet(buttonStyle)
self.backButton.setStyleSheet(buttonStyle)
self.forwardButton.setStyleSheet(buttonStyle)
self.editButton.setStyleSheet(buttonStyle)
self.refreshButton.setStyleSheet(buttonStyle)
self.closeButton.setStyleSheet(buttonStyle)
@@ -776,8 +785,9 @@ class GuiDocViewHeader(QWidget):
self.backButton.setVisible(True)
self.forwardButton.setVisible(True)
self.outlineButton.setVisible(True)
self.closeButton.setVisible(True)
self.editButton.setVisible(True)
self.refreshButton.setVisible(True)
self.closeButton.setVisible(True)
return
@@ -804,6 +814,13 @@ class GuiDocViewHeader(QWidget):
self.docViewer.reloadDocumentRequest.emit()
return
@pyqtSlot()
def _editDocument(self) -> None:
"""Open the document in the editor."""
if tHandle := self._docHandle:
self.docViewer.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, "", True)
return
##
# Events
##
+12 -4
View File
@@ -141,8 +141,10 @@ class GuiOutlineView(QWidget):
def closeProjectTasks(self) -> None:
"""Run closing project tasks."""
if self.outlineTree.wasRendered:
# If the panel hasn't been drawn yet, those values are incorrect
self.outlineData.saveGuiSettings()
self.outlineTree.closeProjectTasks()
self.outlineData.saveGuiSettings()
self.outlineData.updateClasses()
self.clearOutline()
return
@@ -434,6 +436,11 @@ class GuiOutlineTree(QTreeWidget):
def hiddenColumns(self) -> dict[nwOutline, bool]:
return self._colHidden
@property
def wasRendered(self) -> bool:
"""Returns True after the Outline has been rendered once."""
return not self._firstView
##
# Methods
##
@@ -947,11 +954,12 @@ class GuiOutlineDetails(QScrollArea):
def loadGuiSettings(self) -> None:
"""Run open project tasks."""
half = self.width() // 2
parent = self.outlineView.parent() # This widget is rendered already
width = parent.width() if isinstance(parent, QWidget) else 1000
pOptions = SHARED.project.options
self.mainSplit.setSizes([
CONFIG.pxInt(pOptions.getInt("GuiOutlineDetails", "detailsWidth", half)),
CONFIG.pxInt(pOptions.getInt("GuiOutlineDetails", "tagsWidth", half))
CONFIG.pxInt(pOptions.getInt("GuiOutlineDetails", "detailsWidth", width//3)),
CONFIG.pxInt(pOptions.getInt("GuiOutlineDetails", "tagsWidth", 2*width//3))
])
return
+19 -18
View File
@@ -270,6 +270,7 @@ class GuiMain(QMainWindow):
self.docViewer.closeDocumentRequest.connect(self.closeDocViewer)
self.docViewer.documentLoaded.connect(self.docViewerPanel.updateHandle)
self.docViewer.loadDocumentTagRequest.connect(self._followTag)
self.docViewer.openDocumentRequest.connect(self._openDocument)
self.docViewer.reloadDocumentRequest.connect(self._reloadViewer)
self.docViewer.requestProjectItemSelected.connect(self.projView.setSelectedHandle)
self.docViewer.togglePanelVisibility.connect(self._toggleViewerPanelVisibility)
@@ -844,33 +845,33 @@ class GuiMain(QMainWindow):
def closeMain(self) -> bool:
"""Save everything, and close novelWriter."""
if SHARED.hasProject and SHARED.question("%s<br>%s" % (
if SHARED.hasProject and not SHARED.question("%s<br>%s" % (
self.tr("Do you want to exit novelWriter?"),
self.tr("Changes are saved automatically.")
)):
logger.info("Exiting novelWriter")
return False
if not SHARED.focusMode:
CONFIG.setMainPanePos(self.splitMain.sizes())
CONFIG.setOutlinePanePos(self.outlineView.splitSizes())
if self.docViewerPanel.isVisible():
CONFIG.setViewPanePos(self.splitView.sizes())
logger.info("Exiting novelWriter")
CONFIG.showViewerPanel = self.docViewerPanel.isVisible()
wFull = Qt.WindowState.WindowFullScreen
if self.windowState() & wFull != wFull:
# Ignore window size if in full screen mode
CONFIG.setMainWinSize(self.width(), self.height())
if not SHARED.focusMode:
CONFIG.setMainPanePos(self.splitMain.sizes())
CONFIG.setOutlinePanePos(self.outlineView.splitSizes())
if self.docViewerPanel.isVisible():
CONFIG.setViewPanePos(self.splitView.sizes())
if SHARED.hasProject:
self.closeProject(True)
CONFIG.saveConfig()
CONFIG.showViewerPanel = self.docViewerPanel.isVisible()
wFull = Qt.WindowState.WindowFullScreen
if self.windowState() & wFull != wFull:
# Ignore window size if in full screen mode
CONFIG.setMainWinSize(self.width(), self.height())
QApplication.quit()
if SHARED.hasProject:
self.closeProject(True)
CONFIG.saveConfig()
return True
QApplication.quit()
return False
return True
def closeViewerPanel(self, byUser: bool = True) -> bool:
"""Close the document view panel."""
+6
View File
@@ -65,3 +65,9 @@ exclude = ["docs/*"]
[tool.autopep8]
max_line_length = 99
ignore = ["E133", "E221", "E226", "E228", "E241", "W503"]
[tool.coverage.run]
branch = false
[tool.coverage.report]
precision = 2
+8
View File
@@ -85,6 +85,7 @@ def testBaseInit_Options(monkeypatch, fncPath):
# Defaults w/None Args
nwGUI = main()
assert nwGUI is not None
assert logger.getEffectiveLevel() == logging.WARNING
assert nwGUI.closeMain() == "closeMain"
@@ -92,6 +93,7 @@ def testBaseInit_Options(monkeypatch, fncPath):
nwGUI = main(
["--testmode", f"--config={fncPath}", f"--data={fncPath}", "--style=Fusion"]
)
assert nwGUI is not None
assert logger.getEffectiveLevel() == logging.WARNING
assert nwGUI.closeMain() == "closeMain"
@@ -99,12 +101,14 @@ def testBaseInit_Options(monkeypatch, fncPath):
nwGUI = main(
["--testmode", "--info", f"--config={fncPath}", f"--data={fncPath}"]
)
assert nwGUI is not None
assert logger.getEffectiveLevel() == logging.INFO
assert nwGUI.closeMain() == "closeMain"
nwGUI = main(
["--testmode", "--debug", f"--config={fncPath}", f"--data={fncPath}"]
)
assert nwGUI is not None
assert logger.getEffectiveLevel() == logging.DEBUG
assert nwGUI.closeMain() == "closeMain"
@@ -113,6 +117,7 @@ def testBaseInit_Options(monkeypatch, fncPath):
nwGUI = main(
["--testmode", "--help", f"--config={fncPath}", f"--data={fncPath}"]
)
assert nwGUI is not None
assert nwGUI.closeMain() == "closeMain"
assert ex.value.code == 0
@@ -120,6 +125,7 @@ def testBaseInit_Options(monkeypatch, fncPath):
nwGUI = main(
["--testmode", "--version", f"--config={fncPath}", f"--data={fncPath}"]
)
assert nwGUI is not None
assert nwGUI.closeMain() == "closeMain"
assert ex.value.code == 0
@@ -128,6 +134,7 @@ def testBaseInit_Options(monkeypatch, fncPath):
nwGUI = main(
["--testmode", "--invalid", f"--config={fncPath}", f"--data={fncPath}"]
)
assert nwGUI is not None
assert nwGUI.closeMain() == "closeMain"
assert ex.value.code == 2
@@ -135,6 +142,7 @@ def testBaseInit_Options(monkeypatch, fncPath):
nwGUI = main(
["--testmode", f"--config={fncPath}", f"--data={fncPath}", "sample/"]
)
assert nwGUI is not None
assert nwGUI.closeMain() == "closeMain"
+8 -2
View File
@@ -31,7 +31,6 @@ from PyQt5.QtWidgets import QAction, QApplication, QMenu
from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwDocAction
from novelwriter.formats.toqdoc import ToQTextDocument
from novelwriter.gui.docviewer import GuiDocViewer
from novelwriter.types import QtModNone, QtMouseLeft
from tests.mocked import causeException
@@ -42,7 +41,8 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
"""Test the document viewer."""
# Open project
assert nwGUI.openProject(prjLipsum)
docViewer: GuiDocViewer = nwGUI.docViewer
docEditor = nwGUI.docEditor
docViewer = nwGUI.docViewer
# Select a document in the project tree
nwGUI.projView.setSelectedHandle("88243afbe5ed8")
@@ -74,6 +74,12 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
docViewer.docHeader._refreshDocument()
assert docViewer.toPlainText() == origText
# Open in editor
nwGUI.closeDocument()
assert docEditor.docHandle is None
docViewer.docHeader._editDocument()
assert docEditor.docHandle == docViewer.docHandle
# Select word
cursor = docViewer.textCursor()
cursor.setPosition(100)
+7
View File
@@ -100,6 +100,13 @@ def testGuiMain_Launch(qtbot, monkeypatch, nwGUI, projPath):
assert nwGUI.openProject(projPath) is True
nwGUI.closeProject()
# Check that closes can be blocked
with monkeypatch.context() as mp:
mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.StandardButton.No)
assert nwGUI.openProject(projPath) is True
assert nwGUI.closeMain() is False
nwGUI.closeProject()
# Check that latest release info updated
assert CONFIG.lastNotes != "0x0"