From 1bf584f9bf15436306a019e971783a77d15ccd57 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 15 Aug 2021 18:58:49 +0200 Subject: [PATCH] Add [NEW PAGE] page and [VSPACE] to Insert menu (#848) * Add menu entries for inserting new page and vspace codes * Update tests --- nw/enum.py | 3 + nw/gui/doceditor.py | 65 ++++++++++++++++------ nw/gui/mainmenu.py | 32 ++++++++--- tests/conftest.py | 3 + tests/test_base/test_base_error.py | 2 + tests/test_dialogs/test_dlg_preferences.py | 1 + tests/test_gui/test_gui_mainmenu.py | 21 ++++++- tests/test_gui/test_gui_theme.py | 1 + 8 files changed, 102 insertions(+), 26 deletions(-) diff --git a/nw/enum.py b/nw/enum.py index b60f849e..4c45c2c2 100644 --- a/nw/enum.py +++ b/nw/enum.py @@ -103,6 +103,9 @@ class nwDocInsert(Enum): QUOTE_RS = 2 QUOTE_LD = 3 QUOTE_RD = 4 + NEW_PAGE = 5 + VSPACE_S = 6 + VSPACE_M = 7 # END Enum nwDocInsert diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 0b4ff215..4eb8bdd7 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -847,6 +847,8 @@ class GuiDocEditor(QTextEdit): logger.error("No document open") return False + newBlock = False + if isinstance(theInsert, str): theText = theInsert elif isinstance(theInsert, nwDocInsert): @@ -858,16 +860,59 @@ class GuiDocEditor(QTextEdit): theText = self._typDQOpen elif theInsert == nwDocInsert.QUOTE_RD: theText = self._typDQClose + elif theInsert == nwDocInsert.NEW_PAGE: + theText = "[NEW PAGE]" + newBlock = True + elif theInsert == nwDocInsert.VSPACE_S: + theText = "[VSPACE]" + newBlock = True + elif theInsert == nwDocInsert.VSPACE_M: + theText = "[VSPACE:2]" + newBlock = True else: return False else: return False + if newBlock: + self.insertNewBlock(theText, defaultAfter=False) + else: + theCursor = self.textCursor() + theCursor.beginEditBlock() + theCursor.insertText(theText) + theCursor.endEditBlock() + + return True + + def insertNewBlock(self, theText, defaultAfter=True): + """Inserts a piece of text on a blank line. + """ theCursor = self.textCursor() + theBlock = theCursor.block() + if not theBlock.isValid(): + logger.error("Not a valid text block") + return False + + sPos = theBlock.position() + sLen = theBlock.length() + theCursor.beginEditBlock() + + if sLen > 1 and defaultAfter: + theCursor.setPosition(sPos + sLen - 1) + theCursor.insertText("\n") + else: + theCursor.setPosition(sPos) + theCursor.insertText(theText) + + if sLen > 1 and not defaultAfter: + theCursor.insertText("\n") + theCursor.endEditBlock() + self.setTextCursor(theCursor) + return True def insertKeyWord(self, keyWord): @@ -879,25 +924,9 @@ class GuiDocEditor(QTextEdit): return False logger.verbose("Inserting keyword '%s'", keyWord) + theState = self.insertNewBlock("%s: " % keyWord) - theCursor = self.textCursor() - theBlock = theCursor.block() - if not theBlock.isValid(): - logger.error("Failed to insert keyword '%s'", keyWord) - return False - - theCursor.beginEditBlock() - - if theBlock.length() > 1: - theCursor.setPosition(theBlock.position() + theBlock.length() - 1) - theCursor.insertText("\n") - - theCursor.insertText("%s: " % keyWord) - theCursor.endEditBlock() - - self.setTextCursor(theCursor) - - return True + return theState def closeSearch(self): """Close the search box. diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index 9efd7a49..5d96406b 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -645,28 +645,28 @@ class GuiMainMenu(QMenuBar): self.mInsPunct.addAction(self.aInsDPrime) # Insert > White Spaces - self.mInsBreaks = self.insertMenu.addMenu(self.tr("White Spaces")) + self.mInsSpace = self.insertMenu.addMenu(self.tr("White Spaces")) # Insert > Non-Breaking Space self.aInsNBSpace = QAction(self.tr("Non-Breaking Space"), self) self.aInsNBSpace.setStatusTip(self.tr("Insert a non-breaking space")) self.aInsNBSpace.setShortcut("Ctrl+K, Space") self.aInsNBSpace.triggered.connect(lambda: self._docInsert(nwUnicode.U_NBSP)) - self.mInsBreaks.addAction(self.aInsNBSpace) + self.mInsSpace.addAction(self.aInsNBSpace) # Insert > Thin Space self.aInsThinSpace = QAction(self.tr("Thin Space"), self) self.aInsThinSpace.setStatusTip(self.tr("Insert a thin space")) self.aInsThinSpace.setShortcut("Ctrl+K, Shift+Space") self.aInsThinSpace.triggered.connect(lambda: self._docInsert(nwUnicode.U_THSP)) - self.mInsBreaks.addAction(self.aInsThinSpace) + self.mInsSpace.addAction(self.aInsThinSpace) # Insert > Thin Non-Breaking Space self.aInsThinNBSpace = QAction(self.tr("Thin Non-Breaking Space"), self) self.aInsThinNBSpace.setStatusTip(self.tr("Insert a thin non-breaking space")) self.aInsThinNBSpace.setShortcut("Ctrl+K, Ctrl+Space") self.aInsThinNBSpace.triggered.connect(lambda: self._docInsert(nwUnicode.U_THNBSP)) - self.mInsBreaks.addAction(self.aInsThinNBSpace) + self.mInsSpace.addAction(self.aInsThinNBSpace) # Insert > Symbols self.mInsSymbol = self.insertMenu.addMenu(self.tr("Other Symbols")) @@ -727,9 +727,6 @@ class GuiMainMenu(QMenuBar): self.aInsDivide.triggered.connect(lambda: self._docInsert(nwUnicode.U_DIVIDE)) self.mInsSymbol.addAction(self.aInsDivide) - # Insert > Separator - self.insertMenu.addSeparator() - # Insert > Tags and References self.mInsKeywords = self.insertMenu.addMenu(self.tr("Tags and References")) self.mInsKWItems = {} @@ -751,6 +748,27 @@ class GuiMainMenu(QMenuBar): ) self.mInsKeywords.addAction(self.mInsKWItems[keyWord][0]) + # Insert > Symbols + self.mInsBreaks = self.insertMenu.addMenu(self.tr("Page Break and Space")) + + # Insert > New Page + self.aInsNewPage = QAction(self.tr("Page Break"), self) + self.aInsNewPage.setStatusTip(self.tr("Insert a page break command")) + self.aInsNewPage.triggered.connect(lambda: self._docInsert(nwDocInsert.NEW_PAGE)) + self.mInsBreaks.addAction(self.aInsNewPage) + + # Insert > Vertical Space (Single) + self.aInsVSpaceS = QAction(self.tr("Vertical Space (Single)"), self) + self.aInsVSpaceS.setStatusTip(self.tr("Insert a vertical space equal to one pragraph")) + self.aInsVSpaceS.triggered.connect(lambda: self._docInsert(nwDocInsert.VSPACE_S)) + self.mInsBreaks.addAction(self.aInsVSpaceS) + + # Insert > Vertical Space (Multi) + self.aInsVSpaceM = QAction(self.tr("Vertical Space (Multi)"), self) + self.aInsVSpaceM.setStatusTip(self.tr("Insert a vertical space equal to n pragraphs")) + self.aInsVSpaceM.triggered.connect(lambda: self._docInsert(nwDocInsert.VSPACE_M)) + self.mInsBreaks.addAction(self.aInsVSpaceM) + return def _buildFormatMenu(self): diff --git a/tests/conftest.py b/tests/conftest.py index 6e3d50de..7a0deff6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -31,6 +31,8 @@ sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pa import nw # noqa: E402 +from PyQt5.QtWidgets import QMessageBox # noqa: E402 + from nw.config import Config # noqa: E402 @@ -153,6 +155,7 @@ def mockGUI(monkeypatch, tmpConf): def nwGUI(qtbot, monkeypatch, fncDir, fncConf): """Create an instance of the novelWriter GUI. """ + monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes) monkeypatch.setattr("nw.CONFIG", fncConf) nwGUI = nw.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir]) qtbot.addWidget(nwGUI) diff --git a/tests/test_base/test_base_error.py b/tests/test_base/test_base_error.py index d4cace65..afd9c9f3 100644 --- a/tests/test_base/test_base_error.py +++ b/tests/test_base/test_base_error.py @@ -81,6 +81,8 @@ def testBaseError_Handler(qtbot, monkeypatch, fncDir, tmpDir): checks that the error handler handles potential exceptions. The test will fail if excpetions are not handled. """ + monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes) + qApp.closeAllWindows() nwGUI = nw.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % tmpDir]) qtbot.addWidget(nwGUI) diff --git a/tests/test_dialogs/test_dlg_preferences.py b/tests/test_dialogs/test_dlg_preferences.py index 186472dd..57ec1c02 100644 --- a/tests/test_dialogs/test_dlg_preferences.py +++ b/tests/test_dialogs/test_dlg_preferences.py @@ -45,6 +45,7 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir): """Test the load project wizard. """ # Block message box + monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) diff --git a/tests/test_gui/test_gui_mainmenu.py b/tests/test_gui/test_gui_mainmenu.py index 7c10f34d..22adcbd1 100644 --- a/tests/test_gui/test_gui_mainmenu.py +++ b/tests/test_gui/test_gui_mainmenu.py @@ -464,7 +464,8 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj): """Test the Insert menu. """ # Block message box - monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) + monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) + monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) nwGUI.theProject.projTree.setSeed(42) assert nwGUI.newProject({"projPath": fncProj}) @@ -641,6 +642,24 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj): nwGUI.docEditor.clear() + ## + # Insert Break or Space + ## + + nwGUI.docEditor.setText("### Stuff\n") + nwGUI.mainMenu.aInsNewPage.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == "[NEW PAGE]\n### Stuff\n" + + nwGUI.docEditor.setText("### Stuff\n") + nwGUI.mainMenu.aInsVSpaceS.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == "[VSPACE]\n### Stuff\n" + + nwGUI.docEditor.setText("### Stuff\n") + nwGUI.mainMenu.aInsVSpaceM.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == "[VSPACE:2]\n### Stuff\n" + + nwGUI.docEditor.clear() + ## # Insert text from file ## diff --git a/tests/test_gui/test_gui_theme.py b/tests/test_gui/test_gui_theme.py index 3290be80..360fd854 100644 --- a/tests/test_gui/test_gui_theme.py +++ b/tests/test_gui/test_gui_theme.py @@ -36,6 +36,7 @@ def testGuiTheme_Main(qtbot, monkeypatch, nwMinimal, tmpDir): """ # Block message box monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) + monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes) nwGUI = nw.main(["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal]) qtbot.addWidget(nwGUI)