From 43d95462e02eefe63462c0e01459712bf031a974 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Tue, 26 Nov 2024 17:07:02 +0100 Subject: [PATCH] Optimise a few details and add some tests --- novelwriter/common.py | 11 +++++-- novelwriter/core/itemmodel.py | 6 ++-- novelwriter/gui/doceditor.py | 6 ++-- tests/test_base/test_base_common.py | 47 +++++++++++++++++++++++------ 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/novelwriter/common.py b/novelwriter/common.py index 8e946cee..1dd2c64c 100644 --- a/novelwriter/common.py +++ b/novelwriter/common.py @@ -436,9 +436,9 @@ def describeFont(font: QFont) -> str: def fontMatcher(font: QFont) -> QFont: """Make sure the font is the correct family, if possible. This - ensures that Qt doesn't reuse another font under the hood. The + ensures that Qt doesn't re-use another font under the hood. The default Qt5 font matching algorithm doesn't handle well changing - fonts at runtime. + application fonts at runtime. """ info = QFontInfo(font) if (famRequest := font.family()) != (famActual := info.family()): @@ -448,6 +448,7 @@ def fontMatcher(font: QFont) -> QFont: styleRequest, sizeRequest = font.styleName(), font.pointSize() logger.info("Lookup: %s, %s, %d pt", famRequest, styleRequest, sizeRequest) temp = db.font(famRequest, styleRequest, sizeRequest) + temp.setPointSize(sizeRequest) # Make sure it isn't changed famFound, styleFound, sizeFound = temp.family(), temp.styleName(), temp.pointSize() if famFound == famRequest: logger.info("Found: %s, %s, %d pt", famFound, styleFound, sizeFound) @@ -464,6 +465,12 @@ def qtLambda(func: Callable, *args: Any, **kwargs: Any) -> Callable: return wrapper +def encodeMimeHandles(mimeData: QMimeData, handles: list[str]) -> None: + """Encode handles into a mime data object.""" + mimeData.setData(nwConst.MIME_HANDLE, b"|".join(h.encode() for h in handles)) + return + + def decodeMimeHandles(mimeData: QMimeData) -> list[str]: """Decode and split a mime data object with handles.""" return mimeData.data(nwConst.MIME_HANDLE).data().decode().split("|") diff --git a/novelwriter/core/itemmodel.py b/novelwriter/core/itemmodel.py index df4d33c1..f8784ac2 100644 --- a/novelwriter/core/itemmodel.py +++ b/novelwriter/core/itemmodel.py @@ -31,7 +31,7 @@ from typing import TYPE_CHECKING from PyQt5.QtCore import QAbstractItemModel, QMimeData, QModelIndex, Qt from PyQt5.QtGui import QFont, QIcon -from novelwriter.common import decodeMimeHandles, minmax +from novelwriter.common import decodeMimeHandles, encodeMimeHandles, minmax from novelwriter.constants import nwConst from novelwriter.core.item import NWItem from novelwriter.enum import nwItemClass @@ -367,11 +367,11 @@ class ProjectModel(QAbstractItemModel): def mimeData(self, indices: list[QModelIndex]) -> QMimeData: """Encode mime data about a selection.""" handles = [ - i.internalPointer().item.itemHandle.encode() + i.internalPointer().item.itemHandle for i in indices if i.isValid() and i.column() == 0 ] mime = QMimeData() - mime.setData(nwConst.MIME_HANDLE, b"|".join(handles)) + encodeMimeHandles(mime, handles) return mime def canDropMimeData( diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index b5f76cd2..1e53c7f7 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -1279,11 +1279,13 @@ class GuiDocEditor(QPlainTextEdit): """Process the word counter's finished signal.""" if self._docHandle and self._nwItem: logger.debug("Updating word count") + needsRefresh = wCount != self._nwItem.wordCount self._nwItem.setCharCount(cCount) self._nwItem.setWordCount(wCount) self._nwItem.setParaCount(pCount) - self._nwItem.notifyToRefresh() - self.docFooter.updateWordCount(wCount, False) + if needsRefresh: + self._nwItem.notifyToRefresh() + self.docFooter.updateWordCount(wCount, False) return @pyqtSlot() diff --git a/tests/test_base/test_base_common.py b/tests/test_base/test_base_common.py index 8eae1ba0..ead951c8 100644 --- a/tests/test_base/test_base_common.py +++ b/tests/test_base/test_base_common.py @@ -27,18 +27,19 @@ from xml.etree import ElementTree as ET import pytest -from PyQt5.QtCore import QUrl -from PyQt5.QtGui import QColor, QDesktopServices, QFontDatabase +from PyQt5.QtCore import QMimeData, QUrl +from PyQt5.QtGui import QColor, QDesktopServices, QFont, QFontDatabase, QFontInfo from novelwriter.common import ( NWConfigParser, checkBool, checkFloat, checkInt, checkIntTuple, checkPath, - checkString, checkStringNone, checkUuid, compact, cssCol, describeFont, - elide, firstFloat, formatFileFilter, formatInt, formatTime, - formatTimeStamp, formatVersion, fuzzyTime, getFileSize, hexToInt, isHandle, - isItemClass, isItemLayout, isItemType, isListInstance, isTitleTag, - jsonEncode, makeFileNameSafe, minmax, numberToRoman, openExternalPath, - readTextFile, simplified, transferCase, uniqueCompact, xmlElement, - xmlIndent, xmlSubElem, yesNo + checkString, checkStringNone, checkUuid, compact, cssCol, + decodeMimeHandles, describeFont, elide, encodeMimeHandles, firstFloat, + fontMatcher, formatFileFilter, formatInt, formatTime, formatTimeStamp, + formatVersion, fuzzyTime, getFileSize, hexToInt, isHandle, isItemClass, + isItemLayout, isItemType, isListInstance, isTitleTag, jsonEncode, + makeFileNameSafe, minmax, numberToRoman, openExternalPath, readTextFile, + simplified, transferCase, uniqueCompact, xmlElement, xmlIndent, xmlSubElem, + yesNo ) from tests.mocked import causeOSError @@ -528,6 +529,34 @@ def testBaseCommon_describeFont(): assert describeFont(None) == "Error" # type: ignore +@pytest.mark.base +def testBaseCommon_fontMatcher(monkeypatch): + """Test the fontMatcher function.""" + # Nonsense font is just returned + nonsense = QFont("nonesense", 10) + assert fontMatcher(nonsense) is nonsense + + # General font + fontDB = QFontDatabase() + if len(fontDB.families()) > 1: + fontOne = QFont(fontDB.families()[0]) + fontTwo = QFont(fontDB.families()[1]) + check = QFont(fontOne) + check.setFamily(fontTwo.family()) + with monkeypatch.context() as mp: + mp.setattr(QFontInfo, "family", lambda *a: "nonesense") + assert fontMatcher(check).family() == fontTwo.family() + + +@pytest.mark.base +def testBaseCommon_encodeDecodeMimeHandles(monkeypatch): + """Test the encodeMimeHandles and decodeMimeHandles functions.""" + handles = ["0123456789abc", "123456789abcd", "23456789abcde"] + mimeData = QMimeData() + encodeMimeHandles(mimeData, handles) + assert decodeMimeHandles(mimeData) == handles + + @pytest.mark.base def testBaseCommon_jsonEncode(): """Test the jsonEncode function."""