Switch Tokenizer to use QFont instead of font family and size

This commit is contained in:
Veronica Berglyd Olsen
2024-05-22 16:57:50 +02:00
parent c63a0c2f58
commit 7e1f1a725c
8 changed files with 81 additions and 73 deletions
+2 -4
View File
@@ -76,8 +76,7 @@ SETTINGS_TEMPLATE = {
"text.includeBodyText": (bool, True),
"text.ignoredKeywords": (str, ""),
"text.addNoteHeadings": (bool, True),
"format.textFont": (str, CONFIG.textFont.family()),
"format.textSize": (int, 12),
"format.textFont": (str, CONFIG.textFont.toString()),
"format.lineHeight": (float, 1.15, 0.75, 3.0),
"format.justifyText": (bool, False),
"format.stripUnicode": (bool, False),
@@ -125,8 +124,7 @@ SETTINGS_LABELS = {
"text.addNoteHeadings": QT_TRANSLATE_NOOP("Builds", "Add Titles for Notes"),
"format.grpFormat": QT_TRANSLATE_NOOP("Builds", "Text Format"),
"format.textFont": QT_TRANSLATE_NOOP("Builds", "Font Family"),
"format.textSize": QT_TRANSLATE_NOOP("Builds", "Font Size"),
"format.textFont": QT_TRANSLATE_NOOP("Builds", "Text Font"),
"format.lineHeight": QT_TRANSLATE_NOOP("Builds", "Line Height"),
"format.grpOptions": QT_TRANSLATE_NOOP("Builds", "Text Options"),
"format.justifyText": QT_TRANSLATE_NOOP("Builds", "Justify Text Margins"),
+4 -9
View File
@@ -28,7 +28,7 @@ import logging
from collections.abc import Iterable
from pathlib import Path
from PyQt5.QtGui import QFont, QFontInfo
from PyQt5.QtGui import QFont
from novelwriter import CONFIG
from novelwriter.constants import nwLabels
@@ -279,13 +279,9 @@ class NWBuildDocument:
def _setupBuild(self, bldObj: Tokenizer) -> dict:
"""Configure the build object."""
# Get Settings
textFont = self._build.getStr("format.textFont")
textSize = self._build.getInt("format.textSize")
fontFamily = textFont or CONFIG.textFont.family()
bldFont = QFont(fontFamily, textSize)
fontInfo = QFontInfo(bldFont)
textFixed = fontInfo.fixedPitch()
textFont = QFont(CONFIG.textFont)
textFont.fromString(self._build.getStr("format.textFont"))
bldObj.setFont(textFont)
bldObj.setTitleFormat(
self._build.getStr("headings.fmtTitle"),
@@ -324,7 +320,6 @@ class NWBuildDocument:
self._build.getBool("headings.breakScene")
)
bldObj.setFont(fontFamily, textSize, textFixed)
bldObj.setJustify(self._build.getBool("format.justifyText"))
bldObj.setLineHeight(self._build.getFloat("format.lineHeight"))
bldObj.setKeepLineBreaks(self._build.getBool("format.keepBreaks"))
+5 -7
View File
@@ -34,7 +34,9 @@ from pathlib import Path
from time import time
from PyQt5.QtCore import QCoreApplication, QRegularExpression
from PyQt5.QtGui import QFont
from novelwriter import CONFIG
from novelwriter.common import checkInt, formatTimeStamp, numberToRoman
from novelwriter.constants import (
nwHeadFmt, nwKeyWords, nwLabels, nwRegEx, nwShortcode, nwUnicode, trConst
@@ -139,9 +141,7 @@ class Tokenizer(ABC):
self._markdown: list[str] = []
# User Settings
self._textFont = "Serif" # Output text font
self._textSize = 11 # Output text size
self._textFixed = False # Fixed width text
self._textFont = CONFIG.textFont # Output text font
self._lineHeight = 1.15 # Line height in units of em
self._blockIndent = 4.00 # Block indent in units of em
self._firstIndent = False # Enable first line indent
@@ -315,11 +315,9 @@ class Tokenizer(ABC):
)
return
def setFont(self, family: str, size: int, isFixed: bool = False) -> None:
def setFont(self, font: QFont) -> None:
"""Set the build font."""
self._textFont = family
self._textSize = round(int(size))
self._textFixed = isFixed
self._textFont = font
return
def setLineHeight(self, height: float) -> None:
+6 -6
View File
@@ -183,7 +183,7 @@ class GuiPreferences(QDialog):
self.guiFont.setMinimumWidth(fontWidth)
self.guiFont.setText(describeFont(self._guiFont))
self.guiFont.setCursorPosition(0)
self.guiFontButton = NIconToolButton(self, iSz, "more")
self.guiFontButton = NIconToolButton(self, iSz, "font")
self.guiFontButton.clicked.connect(self._selectGuiFont)
self.mainForm.addRow(
self.tr("Application font"), self.guiFont,
@@ -233,7 +233,7 @@ class GuiPreferences(QDialog):
self.textFont.setMinimumWidth(fontWidth)
self.textFont.setText(describeFont(CONFIG.textFont))
self.textFont.setCursorPosition(0)
self.textFontButton = NIconToolButton(self, iSz, "more")
self.textFontButton = NIconToolButton(self, iSz, "font")
self.textFontButton.clicked.connect(self._selectTextFont)
self.mainForm.addRow(
self.tr("Document font"), self.textFont,
@@ -695,7 +695,7 @@ class GuiPreferences(QDialog):
self.quoteSym["SO"].setFixedWidth(boxFixed)
self.quoteSym["SO"].setAlignment(QtAlignCenter)
self.quoteSym["SO"].setText(CONFIG.fmtSQuoteOpen)
self.btnSingleStyleO = NIconToolButton(self, iSz, "more")
self.btnSingleStyleO = NIconToolButton(self, iSz, "quote")
self.btnSingleStyleO.clicked.connect(lambda: self._getQuote("SO"))
self.mainForm.addRow(
self.tr("Single quote open style"), self.quoteSym["SO"],
@@ -709,7 +709,7 @@ class GuiPreferences(QDialog):
self.quoteSym["SC"].setFixedWidth(boxFixed)
self.quoteSym["SC"].setAlignment(QtAlignCenter)
self.quoteSym["SC"].setText(CONFIG.fmtSQuoteClose)
self.btnSingleStyleC = NIconToolButton(self, iSz, "more")
self.btnSingleStyleC = NIconToolButton(self, iSz, "quote")
self.btnSingleStyleC.clicked.connect(lambda: self._getQuote("SC"))
self.mainForm.addRow(
self.tr("Single quote close style"), self.quoteSym["SC"],
@@ -724,7 +724,7 @@ class GuiPreferences(QDialog):
self.quoteSym["DO"].setFixedWidth(boxFixed)
self.quoteSym["DO"].setAlignment(QtAlignCenter)
self.quoteSym["DO"].setText(CONFIG.fmtDQuoteOpen)
self.btnDoubleStyleO = NIconToolButton(self, iSz, "more")
self.btnDoubleStyleO = NIconToolButton(self, iSz, "quote")
self.btnDoubleStyleO.clicked.connect(lambda: self._getQuote("DO"))
self.mainForm.addRow(
self.tr("Double quote open style"), self.quoteSym["DO"],
@@ -738,7 +738,7 @@ class GuiPreferences(QDialog):
self.quoteSym["DC"].setFixedWidth(boxFixed)
self.quoteSym["DC"].setAlignment(QtAlignCenter)
self.quoteSym["DC"].setText(CONFIG.fmtDQuoteClose)
self.btnDoubleStyleC = NIconToolButton(self, iSz, "more")
self.btnDoubleStyleC = NIconToolButton(self, iSz, "quote")
self.btnDoubleStyleC.clicked.connect(lambda: self._getQuote("DC"))
self.mainForm.addRow(
self.tr("Double quote close style"), self.quoteSym["DC"],
+10 -14
View File
@@ -31,7 +31,7 @@ from time import time
from typing import TYPE_CHECKING
from PyQt5.QtCore import Qt, QTimer, QUrl, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QCloseEvent, QColor, QCursor, QPalette, QResizeEvent
from PyQt5.QtGui import QCloseEvent, QColor, QCursor, QFont, QPalette, QResizeEvent
from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog
from PyQt5.QtWidgets import (
QAbstractItemView, QApplication, QFormLayout, QGridLayout, QHBoxLayout,
@@ -404,10 +404,10 @@ class GuiManuscript(NToolDialog):
"""Update the preview widget and set relevant values."""
self.docPreview.setContent(data)
self.docPreview.setBuildName(build.name)
self.docPreview.setTextFont(
build.getStr("format.textFont"),
build.getInt("format.textSize")
)
textFont = QFont()
textFont.fromString(build.getStr("format.textFont"))
self.docPreview.setTextFont(textFont)
self.docPreview.setJustify(
build.getBool("format.justifyText")
)
@@ -787,7 +787,7 @@ class _PreviewWidget(QTextBrowser):
self._updateDocMargins()
self._updateBuildAge()
self.setTextFont(CONFIG.textFont.family(), CONFIG.textFont.pointSize())
self.setTextFont(CONFIG.textFont)
# Age Timer
self.ageTimer = QTimer(self)
@@ -817,18 +817,14 @@ class _PreviewWidget(QTextBrowser):
self.document().setDefaultTextOption(pOptions)
return
def setTextFont(self, family: str, size: int) -> None:
def setTextFont(self, font: QFont) -> None:
"""Set the text font properties and then reset for sub-widgets.
This needs special attention since there appears to be a bug in
Qt 5.15.3. See issues #1862 and #1875.
"""
if family and size > 4:
font = self.font()
font.setFamily(family)
font.setPointSize(size)
self.setFont(font)
self.buildProgress.setFont(SHARED.theme.guiFont)
self.ageLabel.setFont(SHARED.theme.guiFontSmall)
self.setFont(font)
self.buildProgress.setFont(SHARED.theme.guiFont)
self.ageLabel.setFont(SHARED.theme.guiFontSmall)
return
##
+15 -25
View File
@@ -37,6 +37,7 @@ from PyQt5.QtWidgets import (
)
from novelwriter import CONFIG, SHARED
from novelwriter.common import describeFont
from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, trConst
from novelwriter.core.buildsettings import BuildSettings, FilterMode
from novelwriter.extensions.configlayout import (
@@ -1052,6 +1053,7 @@ class _FormatTab(NScrollableForm):
self._build = build
self._unitScale = 1.0
self._textFont = QFont(CONFIG.textFont)
iPx = SHARED.theme.baseIconHeight
iSz = SHARED.theme.baseIconSize
@@ -1063,24 +1065,16 @@ class _FormatTab(NScrollableForm):
self.addGroupLabel(self._build.getLabel("format.grpFormat"))
# Font Family
# Text Font
self.textFont = QLineEdit(self)
self.textFont.setReadOnly(True)
self.btnTextFont = NIconToolButton(self, iSz, "more")
self.btnTextFont = NIconToolButton(self, iSz, "font")
self.btnTextFont.clicked.connect(self._selectFont)
self.addRow(
self._build.getLabel("format.textFont"), self.textFont,
button=self.btnTextFont, stretch=(3, 2)
button=self.btnTextFont, stretch=(1, 1)
)
# Font Size
self.textSize = NSpinBox(self)
self.textSize.setMinimum(8)
self.textSize.setMaximum(60)
self.textSize.setSingleStep(1)
self.textSize.setMinimumWidth(spW)
self.addRow(self._build.getLabel("format.textSize"), self.textSize, unit="pt")
# Line Height
self.lineHeight = NDoubleSpinBox(self)
self.lineHeight.setFixedWidth(spW)
@@ -1175,14 +1169,13 @@ class _FormatTab(NScrollableForm):
def loadContent(self) -> None:
"""Populate the widgets."""
textFont = self._build.getStr("format.textFont")
if not textFont:
textFont = str(CONFIG.textFont.family())
self._textFont = QFont()
self._textFont.fromString(self._build.getStr("format.textFont"))
self.textFont.setText(describeFont(self._textFont))
self.textFont.setCursorPosition(0)
self.textFont.setText(textFont)
self.textSize.setValue(self._build.getInt("format.textSize"))
self.lineHeight.setValue(self._build.getFloat("format.lineHeight"))
self.justifyText.setChecked(self._build.getBool("format.justifyText"))
self.stripUnicode.setChecked(self._build.getBool("format.stripUnicode"))
self.replaceTabs.setChecked(self._build.getBool("format.replaceTabs"))
@@ -1219,8 +1212,7 @@ class _FormatTab(NScrollableForm):
def saveContent(self) -> None:
"""Save choices back into build object."""
self._build.setValue("format.textFont", self.textFont.text())
self._build.setValue("format.textSize", self.textSize.value())
self._build.setValue("format.textFont", self._textFont.toString())
self._build.setValue("format.lineHeight", self.lineHeight.value())
self._build.setValue("format.justifyText", self.justifyText.isChecked())
@@ -1249,13 +1241,11 @@ class _FormatTab(NScrollableForm):
@pyqtSlot()
def _selectFont(self) -> None:
"""Open the QFontDialog and set a font for the font style."""
currFont = QFont()
currFont.setFamily(self.textFont.text())
currFont.setPointSize(self.textSize.value())
newFont, status = QFontDialog.getFont(currFont, self)
font, status = QFontDialog.getFont(self._textFont, self)
if status:
self.textFont.setText(newFont.family())
self.textSize.setValue(newFont.pointSize())
self.textFont.setText(describeFont(font))
self.textFont.setCursorPosition(0)
self._textFont = font
return
@pyqtSlot(int)
+34 -1
View File
@@ -24,7 +24,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations
from PyQt5.QtCore import QRegularExpression, Qt
from PyQt5.QtGui import QColor, QPainter, QTextCursor, QTextFormat
from PyQt5.QtGui import QColor, QFont, QPainter, QTextCursor, QTextFormat
from PyQt5.QtWidgets import QDialogButtonBox, QSizePolicy, QStyle
# Qt Alignment Flags
@@ -105,3 +105,36 @@ QtSizeMinimumExpanding = QSizePolicy.Policy.MinimumExpanding
# Other
QRegExUnicode = QRegularExpression.PatternOption.UseUnicodePropertiesOption
# Maps
FONT_WEIGHTS: dict[int, int] = {
QFont.Weight.Thin: 100,
QFont.Weight.ExtraLight: 200,
QFont.Weight.Light: 300,
QFont.Weight.Normal: 400,
QFont.Weight.Medium: 500,
QFont.Weight.DemiBold: 600,
QFont.Weight.Bold: 700,
QFont.Weight.ExtraBold: 800,
QFont.Weight.Black: 900,
}
FONT_STRETCH: dict[int, str] = {
QFont.AnyStretch: "normal",
QFont.UltraCondensed: "ultra-condensed",
QFont.ExtraCondensed: "extra-condensed",
QFont.Condensed: "condensed",
QFont.SemiCondensed: "semi-condensed",
QFont.Unstretched: "normal",
QFont.SemiExpanded: "semi-expanded",
QFont.Expanded: "expanded",
QFont.ExtraExpanded: "extra-expanded",
QFont.UltraExpanded: "ultra-expanded",
}
FONT_STYLE: dict[int, str] = {
QFont.Style.StyleNormal: "normal",
QFont.Style.StyleItalic: "italic",
QFont.Style.StyleOblique: "oblique",
}
+5 -7
View File
@@ -24,6 +24,8 @@ import json
import pytest
from PyQt5.QtGui import QFont
from novelwriter.constants import nwHeadFmt
from novelwriter.core.project import NWProject
from novelwriter.core.tokenizer import HeadingFormatter, Tokenizer, stripEscape
@@ -50,9 +52,7 @@ def testCoreToken_Setters(mockGUI):
assert tokens._fmtScene == nwHeadFmt.TITLE
assert tokens._fmtHScene == nwHeadFmt.TITLE
assert tokens._fmtSection == nwHeadFmt.TITLE
assert tokens._textFont == "Serif"
assert tokens._textSize == 11
assert tokens._textFixed is False
assert tokens._textFont == QFont("Serif", 11)
assert tokens._lineHeight == 1.15
assert tokens._blockIndent == 4.0
assert tokens._doJustify is False
@@ -82,7 +82,7 @@ def testCoreToken_Setters(mockGUI):
tokens.setSceneFormat(f"S: {nwHeadFmt.TITLE}", True)
tokens.setHardSceneFormat(f"H: {nwHeadFmt.TITLE}", True)
tokens.setSectionFormat(f"X: {nwHeadFmt.TITLE}", True)
tokens.setFont("Monospace", 10, True)
tokens.setFont(QFont("Monospace", 10))
tokens.setLineHeight(2.0)
tokens.setBlockIndent(6.0)
tokens.setJustify(True)
@@ -106,9 +106,7 @@ def testCoreToken_Setters(mockGUI):
assert tokens._fmtScene == f"S: {nwHeadFmt.TITLE}"
assert tokens._fmtHScene == f"H: {nwHeadFmt.TITLE}"
assert tokens._fmtSection == f"X: {nwHeadFmt.TITLE}"
assert tokens._textFont == "Monospace"
assert tokens._textSize == 10
assert tokens._textFixed is True
assert tokens._textFont == QFont("Monospace", 10)
assert tokens._lineHeight == 2.0
assert tokens._blockIndent == 6.0
assert tokens._doJustify is True