Merge branch 'release'

This commit is contained in:
Veronica Berglyd Olsen
2024-07-21 19:54:43 +02:00
6 changed files with 80 additions and 28 deletions
+10
View File
@@ -279,6 +279,16 @@ def simplified(text: str) -> str:
return " ".join(str(text).strip().split()) return " ".join(str(text).strip().split())
def compact(text: str) -> str:
"""Compact a string by removing spaces."""
return "".join(str(text).split())
def uniqueCompact(text: str) -> str:
"""Return a unique, compact and sorted string."""
return "".join(sorted(set(compact(text))))
def elide(text: str, length: int) -> str: def elide(text: str, length: int) -> str:
"""Elide a piece of text to a maximum length.""" """Elide a piece of text to a maximum length."""
if len(text) > (cut := max(4, length)): if len(text) > (cut := max(4, length)):
+5 -5
View File
@@ -34,7 +34,7 @@ from PyQt5.QtWidgets import (
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import describeFont from novelwriter.common import describeFont, uniqueCompact
from novelwriter.constants import nwUnicode from novelwriter.constants import nwUnicode
from novelwriter.dialogs.quotes import GuiQuoteSelect from novelwriter.dialogs.quotes import GuiQuoteSelect
from novelwriter.extensions.configlayout import NColourLabel, NScrollableForm from novelwriter.extensions.configlayout import NColourLabel, NScrollableForm
@@ -952,8 +952,8 @@ class GuiPreferences(NDialog):
# Text Highlighting # Text Highlighting
dialogueStyle = self.dialogStyle.currentData() dialogueStyle = self.dialogStyle.currentData()
allowOpenDial = self.allowOpenDial.isChecked() allowOpenDial = self.allowOpenDial.isChecked()
narratorBreak = self.narratorBreak.text() narratorBreak = self.narratorBreak.text().strip()
dialogueLine = self.dialogLine.text() dialogueLine = self.dialogLine.text().strip()
altDialogOpen = self.altDialogOpen.text() altDialogOpen = self.altDialogOpen.text()
altDialogClose = self.altDialogClose.text() altDialogClose = self.altDialogClose.text()
highlightEmph = self.highlightEmph.isChecked() highlightEmph = self.highlightEmph.isChecked()
@@ -983,8 +983,8 @@ class GuiPreferences(NDialog):
CONFIG.doReplaceDQuote = self.doReplaceDQuote.isChecked() CONFIG.doReplaceDQuote = self.doReplaceDQuote.isChecked()
CONFIG.doReplaceDash = self.doReplaceDash.isChecked() CONFIG.doReplaceDash = self.doReplaceDash.isChecked()
CONFIG.doReplaceDots = self.doReplaceDots.isChecked() CONFIG.doReplaceDots = self.doReplaceDots.isChecked()
CONFIG.fmtPadBefore = self.fmtPadBefore.text().strip() CONFIG.fmtPadBefore = uniqueCompact(self.fmtPadBefore.text())
CONFIG.fmtPadAfter = self.fmtPadAfter.text().strip() CONFIG.fmtPadAfter = uniqueCompact(self.fmtPadAfter.text())
CONFIG.fmtPadThin = self.fmtPadThin.isChecked() CONFIG.fmtPadThin = self.fmtPadThin.isChecked()
# Quotation Style # Quotation Style
+16 -15
View File
@@ -526,8 +526,14 @@ class GuiMain(QMainWindow):
self.novelView.setActiveHandle(None) self.novelView.setActiveHandle(None)
return return
def openDocument(self, tHandle: str | None, tLine: int | None = None, def openDocument(
changeFocus: bool = True, doScroll: bool = False) -> bool: self,
tHandle: str | None,
tLine: int | None = None,
sTitle: str | None = None,
changeFocus: bool = True,
doScroll: bool = False
) -> bool:
"""Open a specific document, optionally at a given line.""" """Open a specific document, optionally at a given line."""
if not SHARED.hasProject: if not SHARED.hasProject:
logger.error("No project open") logger.error("No project open")
@@ -537,9 +543,12 @@ class GuiMain(QMainWindow):
logger.debug("Requested item '%s' is not a document", tHandle) logger.debug("Requested item '%s' is not a document", tHandle)
return False return False
if sTitle and tLine is None:
if hItem := SHARED.project.index.getItemHeading(tHandle, sTitle):
tLine = hItem.line
self._changeView(nwView.EDITOR) self._changeView(nwView.EDITOR)
cHandle = self.docEditor.docHandle if tHandle == self.docEditor.docHandle:
if cHandle == tHandle:
self.docEditor.setCursorLine(tLine) self.docEditor.setCursorLine(tLine)
if changeFocus: if changeFocus:
self.docEditor.setFocus() self.docEditor.setFocus()
@@ -711,7 +720,6 @@ class GuiMain(QMainWindow):
if SHARED.hasProject: if SHARED.hasProject:
tHandle = None tHandle = None
sTitle = None sTitle = None
tLine = None
if self.projView.treeHasFocus(): if self.projView.treeHasFocus():
tHandle = self.projView.getSelectedHandle() tHandle = self.projView.getSelectedHandle()
elif self.novelView.treeHasFocus(): elif self.novelView.treeHasFocus():
@@ -722,11 +730,8 @@ class GuiMain(QMainWindow):
logger.warning("No item selected") logger.warning("No item selected")
return return
if tHandle and sTitle:
if hItem := SHARED.project.index.getItemHeading(tHandle, sTitle):
tLine = hItem.line
if tHandle: if tHandle:
self.openDocument(tHandle, tLine=tLine, changeFocus=False, doScroll=False) self.openDocument(tHandle, sTitle=sTitle, changeFocus=False, doScroll=False)
return return
@@ -1118,7 +1123,7 @@ class GuiMain(QMainWindow):
tHandle, sTitle = self._getTagSource(tag) tHandle, sTitle = self._getTagSource(tag)
if tHandle is not None: if tHandle is not None:
if mode == nwDocMode.EDIT: if mode == nwDocMode.EDIT:
self.openDocument(tHandle) self.openDocument(tHandle, sTitle=sTitle)
elif mode == nwDocMode.VIEW: elif mode == nwDocMode.VIEW:
self.viewDocument(tHandle=tHandle, sTitle=sTitle) self.viewDocument(tHandle=tHandle, sTitle=sTitle)
return return
@@ -1137,11 +1142,7 @@ class GuiMain(QMainWindow):
"""Handle an open document request.""" """Handle an open document request."""
if tHandle is not None: if tHandle is not None:
if mode == nwDocMode.EDIT: if mode == nwDocMode.EDIT:
tLine = None self.openDocument(tHandle, sTitle=sTitle, changeFocus=setFocus)
hItem = SHARED.project.index.getItemHeading(tHandle, sTitle)
if hItem is not None:
tLine = hItem.line
self.openDocument(tHandle, tLine=tLine, changeFocus=setFocus)
elif mode == nwDocMode.VIEW: elif mode == nwDocMode.VIEW:
self.viewDocument(tHandle=tHandle, sTitle=sTitle) self.viewDocument(tHandle=tHandle, sTitle=sTitle)
return return
+27 -6
View File
@@ -32,12 +32,12 @@ from PyQt5.QtGui import QColor, QDesktopServices, QFontDatabase
from novelwriter.common import ( from novelwriter.common import (
NWConfigParser, checkBool, checkFloat, checkInt, checkIntTuple, checkPath, NWConfigParser, checkBool, checkFloat, checkInt, checkIntTuple, checkPath,
checkString, checkStringNone, checkUuid, cssCol, describeFont, elide, checkString, checkStringNone, checkUuid, compact, cssCol, describeFont,
formatFileFilter, formatInt, formatTime, formatTimeStamp, formatVersion, elide, formatFileFilter, formatInt, formatTime, formatTimeStamp,
fuzzyTime, getFileSize, hexToInt, isHandle, isItemClass, isItemLayout, formatVersion, fuzzyTime, getFileSize, hexToInt, isHandle, isItemClass,
isItemType, isListInstance, isTitleTag, jsonEncode, makeFileNameSafe, isItemLayout, isItemType, isListInstance, isTitleTag, jsonEncode,
minmax, numberToRoman, openExternalPath, readTextFile, simplified, makeFileNameSafe, minmax, numberToRoman, openExternalPath, readTextFile,
transferCase, xmlIndent, yesNo simplified, transferCase, uniqueCompact, xmlIndent, yesNo
) )
from tests.mocked import causeOSError from tests.mocked import causeOSError
@@ -346,6 +346,27 @@ def testBaseCommon_simplified():
assert simplified("\tHello\n\r\tWorld") == "Hello World" assert simplified("\tHello\n\r\tWorld") == "Hello World"
@pytest.mark.base
def testBaseCommon_compact():
"""Test the compact function."""
assert compact("! ! !") == "!!!"
assert compact("1\t2\t3") == "123"
assert compact("1\n2\n3") == "123"
assert compact("1\r2\r3") == "123"
assert compact("1\u00a02\u00a03") == "123"
@pytest.mark.base
def testBaseCommon_uniqueCompact():
"""Test the uniqueCompact function."""
assert uniqueCompact("! ! !") == "!"
assert uniqueCompact("1\t2\t3") == "123"
assert uniqueCompact("1\n2\n3") == "123"
assert uniqueCompact("1\r2\r3") == "123"
assert uniqueCompact("1\u00a02\u00a03") == "123"
assert uniqueCompact("3 2 1") == "123"
@pytest.mark.base @pytest.mark.base
def testBaseCommon_elide(): def testBaseCommon_elide():
"""Test the elide function.""" """Test the elide function."""
+1 -1
View File
@@ -379,7 +379,7 @@ def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, tstPaths):
assert CONFIG.doReplaceDQuote is False assert CONFIG.doReplaceDQuote is False
assert CONFIG.doReplaceDash is False assert CONFIG.doReplaceDash is False
assert CONFIG.doReplaceDots is False assert CONFIG.doReplaceDots is False
assert CONFIG.fmtPadBefore == "!?:" assert CONFIG.fmtPadBefore == "!:?"
assert CONFIG.fmtPadAfter == "¡¿" assert CONFIG.fmtPadAfter == "¡¿"
assert CONFIG.fmtPadThin is True assert CONFIG.fmtPadThin is True
+21 -1
View File
@@ -39,6 +39,7 @@ from novelwriter.gui.outline import GuiOutlineView
from novelwriter.gui.projtree import GuiProjectTree from novelwriter.gui.projtree import GuiProjectTree
from novelwriter.tools.welcome import GuiWelcome from novelwriter.tools.welcome import GuiWelcome
from tests.mocked import causeOSError
from tests.tools import NWD_IGNORE, XML_IGNORE, C, buildTestProject, cmpFiles from tests.tools import NWD_IGNORE, XML_IGNORE, C, buildTestProject, cmpFiles
KEY_DELAY = 1 KEY_DELAY = 1
@@ -651,7 +652,7 @@ def testGuiMain_Viewing(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
@pytest.mark.gui @pytest.mark.gui
def testGuiMain_Features(qtbot, nwGUI, projPath, mockRnd): def testGuiMain_Features(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
"""Test various features of the main window.""" """Test various features of the main window."""
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
assert SHARED.focusMode is False assert SHARED.focusMode is False
@@ -672,6 +673,7 @@ def testGuiMain_Features(qtbot, nwGUI, projPath, mockRnd):
# Enable focus mode # Enable focus mode
nwGUI.toggleFocusMode() nwGUI.toggleFocusMode()
assert SHARED.focusMode is True
assert nwGUI.treePane.isVisible() is False assert nwGUI.treePane.isVisible() is False
assert nwGUI.mainStatus.isVisible() is False assert nwGUI.mainStatus.isVisible() is False
assert nwGUI.mainMenu.isVisible() is False assert nwGUI.mainMenu.isVisible() is False
@@ -680,12 +682,19 @@ def testGuiMain_Features(qtbot, nwGUI, projPath, mockRnd):
# Disable focus mode # Disable focus mode
nwGUI.toggleFocusMode() nwGUI.toggleFocusMode()
assert SHARED.focusMode is False
assert nwGUI.treePane.isVisible() is True assert nwGUI.treePane.isVisible() is True
assert nwGUI.mainStatus.isVisible() is True assert nwGUI.mainStatus.isVisible() is True
assert nwGUI.mainMenu.isVisible() is True assert nwGUI.mainMenu.isVisible() is True
assert nwGUI.sideBar.isVisible() is True assert nwGUI.sideBar.isVisible() is True
assert nwGUI.splitView.isVisible() is True assert nwGUI.splitView.isVisible() is True
# Closing editor disables focus mode
nwGUI.toggleFocusMode()
assert SHARED.focusMode is True
nwGUI.closeDocument()
assert SHARED.focusMode is False
# Full Screen Mode # Full Screen Mode
# ================ # ================
@@ -702,6 +711,17 @@ def testGuiMain_Features(qtbot, nwGUI, projPath, mockRnd):
nwGUI.sideBar.mSettings.show() nwGUI.sideBar.mSettings.show()
nwGUI.sideBar.mSettings.hide() nwGUI.sideBar.mSettings.hide()
# Document Open Errors
# ====================
# Cannot edit a folder
assert nwGUI.openDocument(C.hChapterDir) is False
# Handle I/O error
with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError)
assert nwGUI.openDocument(C.hChapterDoc) is False
# qtbot.stop() # qtbot.stop()