From be7078aa7eb852af2513f8bd73d66b628080348f Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 29 May 2024 21:26:29 +0200 Subject: [PATCH 1/3] Save editor doc before opening same in viewer (#1884) --- novelwriter/gui/docviewer.py | 9 --------- novelwriter/guimain.py | 4 ++++ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py index caee97b97..d73f0abbb 100644 --- a/novelwriter/gui/docviewer.py +++ b/novelwriter/gui/docviewer.py @@ -245,9 +245,6 @@ def loadText(self, tHandle: str, updateHistory: bool = True) -> bool: }) self.updateDocMargins() - # Since we change the content while it may still be rendering, we mark - # the document dirty again to make sure it's re-rendered properly. - self.redrawText() QApplication.restoreOverrideCursor() self.documentLoaded.emit(tHandle) @@ -259,12 +256,6 @@ def reloadText(self) -> None: self.loadText(self._docHandle, updateHistory=False) return - def redrawText(self) -> None: - """Redraw the text by marking the content as "dirty".""" - self.document().markContentsDirty(0, self.document().characterCount()) - self.updateDocMargins() - return - def docAction(self, action: nwDocAction) -> bool: """Process document actions on the current document.""" logger.debug("Requesting action: '%s'", action.name) diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index 0cfe156d4..3d6eaf005 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -616,6 +616,10 @@ def viewDocument(self, tHandle: str | None = None, sTitle: str | None = None) -> # Make sure main tab is in Editor view self._changeView(nwView.EDITOR) + # If we're loading the document in the editor, it may need to be saved + if tHandle == self.docEditor.docHandle and self.docEditor.docChanged: + self.saveDocument() + logger.debug("Viewing document with handle '%s'", tHandle) updateHistory = tHandle != self.docViewer.docHandle if self.docViewer.loadText(tHandle, updateHistory=updateHistory): From f1b7257e73328baab13be4262963367354dd9d50 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 29 May 2024 21:27:05 +0200 Subject: [PATCH 2/3] Improve test coverage --- novelwriter/gui/mainmenu.py | 2 +- novelwriter/guimain.py | 8 ----- tests/test_gui/test_gui_guimain.py | 57 ++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/novelwriter/gui/mainmenu.py b/novelwriter/gui/mainmenu.py index 6a5b1c8ac..348d7b4d7 100644 --- a/novelwriter/gui/mainmenu.py +++ b/novelwriter/gui/mainmenu.py @@ -167,7 +167,7 @@ def _buildProjectMenu(self) -> None: # Project > Edit self.aEditItem = self.projMenu.addAction(self.tr("Rename Item")) self.aEditItem.setShortcut("F2") - self.aEditItem.triggered.connect(lambda: self.mainGui.editItemLabel(None)) + self.aEditItem.triggered.connect(lambda: self.mainGui.projView.renameTreeItem(None)) # Project > Delete self.aDeleteItem = self.projMenu.addAction(self.tr("Delete Item")) diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index 3d6eaf005..37c42cb2c 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -723,14 +723,6 @@ def openSelectedItem(self) -> None: return - def editItemLabel(self, tHandle: str | None = None) -> None: - """Open the edit item dialog.""" - if SHARED.hasProject: - if tHandle is None and (self.docEditor.anyFocus() or SHARED.focusMode): - tHandle = self.docEditor.docHandle - self.projView.renameTreeItem(tHandle) - return - def rebuildTrees(self) -> None: """Rebuild the project tree.""" self.projView.populateTree() diff --git a/tests/test_gui/test_gui_guimain.py b/tests/test_gui/test_gui_guimain.py index e128503ca..09142051a 100644 --- a/tests/test_gui/test_gui_guimain.py +++ b/tests/test_gui/test_gui_guimain.py @@ -567,6 +567,63 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd): # qtbot.stop() +@pytest.mark.gui +def testGuiMain_Viewing(qtbot, monkeypatch, nwGUI, projPath, mockRnd): + """Test the document viewer.""" + buildTestProject(nwGUI, projPath) + nwGUI.closeProject() + + # View before a project is open does nothing + assert nwGUI.splitView.isVisible() is False + assert nwGUI.viewDocument(None) is False + assert nwGUI.splitView.isVisible() is False + + # Open the test project + nwGUI.openProject(projPath) + assert nwGUI.docEditor.docHandle == C.hTitlePage + + # If editor has focus, open that document + with monkeypatch.context() as mp: + mp.setattr(nwGUI.docEditor, "hasFocus", lambda *a: True) + nwGUI.viewDocument(None) + assert nwGUI.docViewer.docHandle == C.hTitlePage + nwGUI.closeDocViewer() + + # If editor does not have focus, open selected handle + with monkeypatch.context() as mp: + mp.setattr(nwGUI.docEditor, "hasFocus", lambda *a: False) + nwGUI.projView.projTree.setSelectedHandle(C.hSceneDoc) + nwGUI.viewDocument(None) + assert nwGUI.docViewer.docHandle == C.hSceneDoc + + # If there is no selection, get last selected + SHARED.project.data.setLastHandle(C.hChapterDoc, "viewer") + assert SHARED.project.data.getLastHandle("viewer") == C.hChapterDoc + with monkeypatch.context() as mp: + mp.setattr(nwGUI.docEditor, "hasFocus", lambda *a: False) + nwGUI.projView.projTree.clearSelection() + nwGUI.viewDocument(None) + assert nwGUI.docViewer.docHandle == C.hChapterDoc + + # If all fails, don't open anything + SHARED.project.data.setLastHandle(None, "viewer") + assert SHARED.project.data.getLastHandle("viewer") is None + with monkeypatch.context() as mp: + mp.setattr(nwGUI.docEditor, "hasFocus", lambda *a: False) + nwGUI.projView.projTree.clearSelection() + assert nwGUI.viewDocument(None) is False + + # If editor doc was edited and requested for the viewer, save it first + nwGUI.openDocument(C.hSceneDoc) + nwGUI.docEditor.setPlainText("### New Scene\n\nWith some stuff in it!\n\n") + assert nwGUI.docEditor.docChanged is True + + nwGUI.viewDocument(C.hSceneDoc) + assert nwGUI.docViewer.toPlainText() == "New Scene\nWith some stuff in it!" + + # qtbot.stop() + + @pytest.mark.gui def testGuiMain_Features(qtbot, nwGUI, projPath, mockRnd): """Test various features of the main window.""" From 31776a51a7e771f28cc3b2ec967e4e8624af162a Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 29 May 2024 21:37:02 +0200 Subject: [PATCH 3/3] Add some more easy coverage --- novelwriter/guimain.py | 18 ++++-------------- tests/test_gui/test_gui_guimain.py | 14 +++++++++++++- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index 37c42cb2c..14b124fa2 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -304,9 +304,6 @@ def __init__(self) -> None: self.keyEscape.setKey(Qt.Key.Key_Escape) self.keyEscape.activated.connect(self._keyPressEscape) - # Check that config loaded fine - self.reportConfErr() - # Initialise Main GUI self.initMain() self.asProjTimer.start() @@ -328,6 +325,10 @@ def initMain(self) -> None: def postLaunchTasks(self, cmdOpen: str | None) -> None: """Process tasks after the main window has been created.""" + # Check that config loaded fine + if CONFIG.hasError: + SHARED.error(CONFIG.errorText()) + if cmdOpen: QApplication.processEvents() logger.info("Command line path: %s", cmdOpen) @@ -843,15 +844,6 @@ def showDictionariesDialog(self) -> None: SHARED.error(self.tr("Could not initialise the dialog.")) return - def reportConfErr(self) -> None: - """Checks if the Config module has any errors to report, and let - the user know if this is the case. The Config module caches - errors since it is initialised before the GUI itself. - """ - if CONFIG.hasError: - SHARED.error(CONFIG.errorText()) - return - ## # Main Window Actions ## @@ -882,9 +874,7 @@ def closeMain(self) -> bool: if SHARED.hasProject: self.closeProject(True) - CONFIG.saveConfig() - self.reportConfErr() QApplication.quit() diff --git a/tests/test_gui/test_gui_guimain.py b/tests/test_gui/test_gui_guimain.py index 09142051a..4baf27581 100644 --- a/tests/test_gui/test_gui_guimain.py +++ b/tests/test_gui/test_gui_guimain.py @@ -84,6 +84,10 @@ def testGuiMain_Launch(qtbot, monkeypatch, nwGUI, projPath): # Check that latest release info updated assert CONFIG.lastNotes != "0x0" + # Set some config error + CONFIG._hasError = True + CONFIG._errData.append("Foo") + # Check that project open dialog launches nwGUI.postLaunchTasks(None) qtbot.waitUntil(lambda: SHARED.findTopLevelWidget(GuiWelcome) is not None, timeout=1000) @@ -91,6 +95,10 @@ def testGuiMain_Launch(qtbot, monkeypatch, nwGUI, projPath): welcome.show() welcome.close() + # Config errors should be cleared + assert SHARED.lastAlert == "Foo" + assert CONFIG._hasError is False + # qtbot.stop() @@ -578,7 +586,11 @@ def testGuiMain_Viewing(qtbot, monkeypatch, nwGUI, projPath, mockRnd): assert nwGUI.viewDocument(None) is False assert nwGUI.splitView.isVisible() is False - # Open the test project + # Open project requires a path + assert nwGUI.openProject(None) is False + assert SHARED.hasProject is False + + # Open the test project, properly nwGUI.openProject(projPath) assert nwGUI.docEditor.docHandle == C.hTitlePage