From 5665efd9d7100ec5e73d9b452702fb6f9acab322 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 20 Jun 2024 23:32:02 +0200 Subject: [PATCH] Update docs and test coverage --- docs/source/more_customise.rst | 3 +- novelwriter/guimain.py | 13 ++- tests/test_gui/test_gui_guimain.py | 131 ++++++++++++++++++++++++++++- 3 files changed, 137 insertions(+), 10 deletions(-) diff --git a/docs/source/more_customise.rst b/docs/source/more_customise.rst index d0d02941..6ea7ca76 100644 --- a/docs/source/more_customise.rst +++ b/docs/source/more_customise.rst @@ -132,6 +132,7 @@ A GUI theme ``.conf`` file consists of the following settings: [GUI] helptext = 0, 0, 0 + fadedtext = 128, 128, 128 errortext = 255, 0, 0 statusnone = 120, 120, 120 statussaved = 2, 133, 37 @@ -149,7 +150,7 @@ colour values are RGB numbers on the format ``r, g, b`` where each is an integer not defined, it is computed as a colour between the ``window`` and ``windowtext`` colour. .. versionadded:: 2.5 - The ``errortext`` theme colour entry was added. + The ``fadedtext`` and ``errortext`` theme colour entries were added. Custom Syntax Theme diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index 443d3236..b7ccfedf 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -1172,16 +1172,13 @@ class GuiMain(QMainWindow): @pyqtSlot(nwDocAction) def _passDocumentAction(self, action: nwDocAction) -> None: - """Pass on a document action to the document viewer if it has - focus, or pass it to the document editor if it or any of its - child widgets have focus. If neither has focus, ignore it. + """Pass on a document action to the editor or viewer based on + which one has focus, or if neither has focus, ignore it. """ - if self.docViewer.hasFocus(): - self.docViewer.docAction(action) - elif self.docEditor.hasFocus(): + if self.docEditor.hasFocus(): self.docEditor.docAction(action) - else: - logger.debug("Action cancelled as neither editor nor viewer has focus") + elif self.docViewer.hasFocus(): + self.docViewer.docAction(action) return @pyqtSlot(str) diff --git a/tests/test_gui/test_gui_guimain.py b/tests/test_gui/test_gui_guimain.py index 683ce18f..80c3cf57 100644 --- a/tests/test_gui/test_gui_guimain.py +++ b/tests/test_gui/test_gui_guimain.py @@ -32,7 +32,7 @@ from PyQt5.QtWidgets import QInputDialog, QMenu from novelwriter import CONFIG, SHARED from novelwriter.dialogs.editlabel import GuiEditLabel -from novelwriter.enum import nwFocus, nwItemType, nwView +from novelwriter.enum import nwDocAction, nwFocus, nwItemType, nwView from novelwriter.gui.doceditor import GuiDocEditor from novelwriter.gui.noveltree import GuiNovelView from novelwriter.gui.outline import GuiOutlineView @@ -64,7 +64,21 @@ def testGuiMain_Launch(qtbot, monkeypatch, nwGUI, projPath): # Open Lipsum project nwGUI.postLaunchTasks(projPath) + assert SHARED.hasProject is True nwGUI.closeProject() + assert SHARED.hasProject is False + + # Open as if called from Welcome + nwGUI._openProjectFromWelcome(projPath) + assert SHARED.hasProject is True + nwGUI.closeProject() + assert SHARED.hasProject is False + + # Open as if called from Welcome, invalid path + with monkeypatch.context() as mp: + mp.setattr(nwGUI, "showWelcomeDialog", lambda *a: None) + nwGUI._openProjectFromWelcome(None) + assert SHARED.hasProject is False # Project open fails with monkeypatch.context() as mp: @@ -689,3 +703,118 @@ def testGuiMain_Features(qtbot, nwGUI, projPath, mockRnd): nwGUI.sideBar.mSettings.hide() # qtbot.stop() + + +@pytest.mark.gui +def testGuiMain_FocusView(qtbot, monkeypatch, nwGUI, projPath, mockRnd): + """Test switching focus and view of the main window.""" + buildTestProject(nwGUI, projPath) + + nwGUI.openDocument(C.hSceneDoc) + nwGUI.viewDocument(C.hSceneDoc) + + # Toggle Focus + # ============ + nwGUI.docEditor.setFocus() + assert nwGUI.docEditor.anyFocus() + + # Simulate focus change to viewer + nwGUI._appFocusChanged(None, nwGUI.docViewer) + assert nwGUI.docEditor.docHeader.itemTitle._state is False + assert nwGUI.docViewer.docHeader.itemTitle._state is True + + # Simulate focus change to editor + nwGUI._appFocusChanged(None, nwGUI.docEditor) + assert nwGUI.docEditor.docHeader.itemTitle._state is True + assert nwGUI.docViewer.docHeader.itemTitle._state is False + + # Focus Tree + # ========== + assert nwGUI.projStack.currentWidget() == nwGUI.projView + + # Switch from editor to project tree + nwGUI.docEditor.setFocus() + nwGUI._switchFocus(nwFocus.TREE) + assert nwGUI.projStack.currentWidget() == nwGUI.projView + + # Triggering again should switch to novel view + nwGUI._switchFocus(nwFocus.TREE) + assert nwGUI.projStack.currentWidget() == nwGUI.novelView + + # Switch from editor to novel view + nwGUI.docEditor.setFocus() + nwGUI._switchFocus(nwFocus.TREE) + assert nwGUI.projStack.currentWidget() == nwGUI.novelView + + # Triggering again should switch back to project tree + nwGUI._switchFocus(nwFocus.TREE) + assert nwGUI.projStack.currentWidget() == nwGUI.projView + + # If in search mode, should default to project tree + nwGUI._changeView(nwView.SEARCH) + nwGUI._switchFocus(nwFocus.TREE) + assert nwGUI.projStack.currentWidget() == nwGUI.projView + + # Focus Document + # ============== + nwGUI._switchFocus(nwFocus.TREE) + + def mockEmitEditorFocus(*a): + nwGUI._appFocusChanged(None, nwGUI.docEditor) + + def mockEmitViewerFocus(*a): + nwGUI._appFocusChanged(None, nwGUI.docViewer) + + # Switch to viewer + with monkeypatch.context() as mp: + mp.setattr(nwGUI.docEditor, "hasFocus", lambda *a: True) + mp.setattr(nwGUI.docViewer, "hasFocus", lambda *a: False) + mp.setattr(nwGUI.docEditor, "setFocus", mockEmitEditorFocus) + mp.setattr(nwGUI.docViewer, "setFocus", mockEmitViewerFocus) + nwGUI._switchFocus(nwFocus.DOCUMENT) + assert nwGUI.docEditor.docHeader.itemTitle._state is False + assert nwGUI.docViewer.docHeader.itemTitle._state is True + + # Call again to switch to editor + with monkeypatch.context() as mp: + mp.setattr(nwGUI.docEditor, "hasFocus", lambda *a: False) + mp.setattr(nwGUI.docViewer, "hasFocus", lambda *a: True) + mp.setattr(nwGUI.docEditor, "setFocus", mockEmitEditorFocus) + mp.setattr(nwGUI.docViewer, "setFocus", mockEmitViewerFocus) + nwGUI._switchFocus(nwFocus.DOCUMENT) + assert nwGUI.docEditor.docHeader.itemTitle._state is True + assert nwGUI.docViewer.docHeader.itemTitle._state is False + + # Default to editor + with monkeypatch.context() as mp: + mp.setattr(nwGUI.docEditor, "hasFocus", lambda *a: False) + mp.setattr(nwGUI.docViewer, "hasFocus", lambda *a: False) + mp.setattr(nwGUI.docEditor, "setFocus", mockEmitEditorFocus) + mp.setattr(nwGUI.docViewer, "setFocus", mockEmitViewerFocus) + nwGUI._switchFocus(nwFocus.DOCUMENT) + assert nwGUI.docEditor.docHeader.itemTitle._state is True + assert nwGUI.docViewer.docHeader.itemTitle._state is False + + # Focus Outline + # ============= + nwGUI._switchFocus(nwFocus.OUTLINE) + assert nwGUI.mainStack.currentWidget() == nwGUI.outlineView + + # Pass Actions + # ============ + + # Pass to editor + with monkeypatch.context() as mp: + mp.setattr(nwGUI.docEditor, "hasFocus", lambda *a: True) + mp.setattr(nwGUI.docViewer, "hasFocus", lambda *a: False) + nwGUI._passDocumentAction(nwDocAction.SEL_ALL) + assert nwGUI.docEditor.textCursor().hasSelection() is True + + # Pass to viewer + with monkeypatch.context() as mp: + mp.setattr(nwGUI.docEditor, "hasFocus", lambda *a: False) + mp.setattr(nwGUI.docViewer, "hasFocus", lambda *a: True) + nwGUI._passDocumentAction(nwDocAction.SEL_ALL) + assert nwGUI.docViewer.textCursor().hasSelection() is True + + # qtbot.stop()