Allow full font style settings for GUI and editor (#1881)

This commit is contained in:
Veronica Berglyd Olsen
2024-05-20 18:37:42 +02:00
committed by GitHub
20 changed files with 193 additions and 232 deletions
+19 -20
View File
@@ -216,29 +216,28 @@ def main(sysArgs: list | None = None) -> GuiMain | None:
# Import GUI (after dependency checks), and launch # Import GUI (after dependency checks), and launch
from novelwriter.guimain import GuiMain from novelwriter.guimain import GuiMain
if testMode: if testMode:
nwGUI = GuiMain() # Only used for testing where the test framework creates the app
return nwGUI CONFIG.loadConfig()
return GuiMain()
else: app = QApplication([CONFIG.appName, (f"-style={qtStyle}")])
nwApp = QApplication([CONFIG.appName, (f"-style={qtStyle}")]) app.setApplicationName(CONFIG.appName)
nwApp.setApplicationName(CONFIG.appName) app.setApplicationVersion(__version__)
nwApp.setApplicationVersion(__version__) app.setOrganizationDomain(__domain__)
nwApp.setOrganizationDomain(__domain__) app.setOrganizationName(__domain__)
nwApp.setOrganizationName(__domain__) app.setDesktopFileName(CONFIG.appName)
nwApp.setDesktopFileName(CONFIG.appName)
# Connect the exception handler before making the main GUI # Connect the exception handler before making the main GUI
sys.excepthook = exceptionHandler sys.excepthook = exceptionHandler
# Run Config steps that require the QApplication # Run Config steps that require the QApplication
CONFIG.initLocalisation(nwApp) CONFIG.loadConfig()
CONFIG.setTextFont(CONFIG.textFont, CONFIG.textSize) # Makes sure these are valid CONFIG.initLocalisation(app)
# Launch main GUI # Launch main GUI
nwGUI = GuiMain() nwGUI = GuiMain()
nwGUI.postLaunchTasks(cmdOpen) nwGUI.postLaunchTasks(cmdOpen)
sys.exit(nwApp.exec()) sys.exit(app.exec())
return None
+9 -1
View File
@@ -37,7 +37,7 @@ from urllib.parse import urljoin
from urllib.request import pathname2url from urllib.request import pathname2url
from PyQt5.QtCore import QCoreApplication, QUrl from PyQt5.QtCore import QCoreApplication, QUrl
from PyQt5.QtGui import QColor, QDesktopServices from PyQt5.QtGui import QColor, QDesktopServices, QFont, QFontInfo
from novelwriter.constants import nwConst, nwLabels, nwUnicode, trConst from novelwriter.constants import nwConst, nwLabels, nwUnicode, trConst
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
@@ -401,6 +401,14 @@ def cssCol(col: QColor, alpha: int | None = None) -> str:
return f"rgba({col.red()}, {col.green()}, {col.blue()}, {alpha or col.alpha()})" return f"rgba({col.red()}, {col.green()}, {col.blue()}, {alpha or col.alpha()})"
def describeFont(font: QFont) -> str:
"""Describe a font in a way that can be displayed on the GUI."""
if isinstance(font, QFont):
info = QFontInfo(font)
return f"{font.family()} {info.styleName()} @ {font.pointSize()} pt"
return "Error"
## ##
# Encoder Functions # Encoder Functions
## ##
+95 -69
View File
@@ -36,10 +36,10 @@ from PyQt5.QtCore import (
PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QLibraryInfo, PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QLibraryInfo,
QLocale, QStandardPaths, QSysInfo, QTranslator QLocale, QStandardPaths, QSysInfo, QTranslator
) )
from PyQt5.QtGui import QFontDatabase from PyQt5.QtGui import QFont, QFontDatabase
from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QApplication
from novelwriter.common import NWConfigParser, checkInt, checkPath, formatTimeStamp from novelwriter.common import NWConfigParser, checkInt, checkPath, describeFont, formatTimeStamp
from novelwriter.constants import nwFiles, nwUnicode from novelwriter.constants import nwFiles, nwUnicode
from novelwriter.error import formatException, logException from novelwriter.error import formatException, logException
@@ -106,15 +106,14 @@ class Config:
self._recentObj = RecentProjects(self) self._recentObj = RecentProjects(self)
# General GUI Settings # General GUI Settings
self.guiLocale = self._qLocale.name() self.guiLocale = self._qLocale.name()
self.guiTheme = "default" # GUI theme self.guiTheme = "default" # GUI theme
self.guiSyntax = "default_light" # Syntax theme self.guiSyntax = "default_light" # Syntax theme
self.guiFont = "" # Defaults to system default font in theme class self.guiFont = QFont() # Main GUI font
self.guiFontSize = 11 # Is overridden if system default is loaded self.guiScale = 1.0 # Set automatically by Theme class
self.guiScale = 1.0 # Set automatically by Theme class self.hideVScroll = False # Hide vertical scroll bars on main widgets
self.hideVScroll = False # Hide vertical scroll bars on main widgets self.hideHScroll = False # Hide horizontal scroll bars on main widgets
self.hideHScroll = False # Hide horizontal scroll bars on main widgets self.lastNotes = "0x0" # The latest release notes that have been shown
self.lastNotes = "0x0" # The latest release notes that have been shown
# Size Settings # Size Settings
self._mainWinSize = [1200, 650] # Last size of the main GUI window self._mainWinSize = [1200, 650] # Last size of the main GUI window
@@ -132,43 +131,42 @@ class Config:
self.askBeforeBackup = True # Flag for asking before running automatic backup self.askBeforeBackup = True # Flag for asking before running automatic backup
# Text Editor Settings # Text Editor Settings
self.textFont = "" # Editor font self.textFont = QFont() # Editor font
self.textSize = 12 # Editor font size self.textWidth = 700 # Editor text width
self.textWidth = 700 # Editor text width self.textMargin = 40 # Editor/viewer text margin
self.textMargin = 40 # Editor/viewer text margin self.tabWidth = 40 # Editor tabulator width
self.tabWidth = 40 # Editor tabulator width
self.focusWidth = 800 # Focus Mode text width self.focusWidth = 800 # Focus Mode text width
self.hideFocusFooter = False # Hide document footer in Focus Mode self.hideFocusFooter = False # Hide document footer in Focus Mode
self.showFullPath = True # Show full document path in editor header self.showFullPath = True # Show full document path in editor header
self.autoSelect = True # Auto-select word when applying format with no selection self.autoSelect = True # Auto-select word when applying format with no selection
self.doJustify = False # Justify text self.doJustify = False # Justify text
self.showTabsNSpaces = False # Show tabs and spaces in editor self.showTabsNSpaces = False # Show tabs and spaces in editor
self.showLineEndings = False # Show line endings in editor self.showLineEndings = False # Show line endings in editor
self.showMultiSpaces = True # Highlight multiple spaces in the text self.showMultiSpaces = True # Highlight multiple spaces in the text
self.doReplace = True # Enable auto-replace as you type self.doReplace = True # Enable auto-replace as you type
self.doReplaceSQuote = True # Smart single quotes self.doReplaceSQuote = True # Smart single quotes
self.doReplaceDQuote = True # Smart double quotes self.doReplaceDQuote = True # Smart double quotes
self.doReplaceDash = True # Replace multiple hyphens with dashes self.doReplaceDash = True # Replace multiple hyphens with dashes
self.doReplaceDots = True # Replace three dots with ellipsis self.doReplaceDots = True # Replace three dots with ellipsis
self.autoScroll = False # Typewriter-like scrolling self.autoScroll = False # Typewriter-like scrolling
self.autoScrollPos = 30 # Start point for typewriter-like scrolling self.autoScrollPos = 30 # Start point for typewriter-like scrolling
self.scrollPastEnd = True # Scroll past end of document, and centre cursor self.scrollPastEnd = True # Scroll past end of document, and centre cursor
self.dialogStyle = 2 # Quote type to use for dialogue self.dialogStyle = 2 # Quote type to use for dialogue
self.allowOpenDial = True # Allow open-ended dialogue quotes self.allowOpenDial = True # Allow open-ended dialogue quotes
self.narratorBreak = "" # Symbol to use for narrator break self.narratorBreak = "" # Symbol to use for narrator break
self.dialogLine = "" # Symbol to use for dialogue line self.dialogLine = "" # Symbol to use for dialogue line
self.altDialogOpen = "" # Alternative dialog symbol, open self.altDialogOpen = "" # Alternative dialog symbol, open
self.altDialogClose = "" # Alternative dialog symbol, close self.altDialogClose = "" # Alternative dialog symbol, close
self.highlightEmph = True # Add colour to text emphasis self.highlightEmph = True # Add colour to text emphasis
self.stopWhenIdle = True # Stop the status bar clock when the user is idle self.stopWhenIdle = True # Stop the status bar clock when the user is idle
self.userIdleTime = 300 # Time of inactivity to consider user idle self.userIdleTime = 300 # Time of inactivity to consider user idle
self.incNotesWCount = True # The status bar word count includes notes self.incNotesWCount = True # The status bar word count includes notes
# User-Selected Symbol Settings # User-Selected Symbol Settings
self.fmtApostrophe = nwUnicode.U_RSQUO self.fmtApostrophe = nwUnicode.U_RSQUO
@@ -362,23 +360,53 @@ class Config:
self._backupPath = checkPath(path, self._backPath) self._backupPath = checkPath(path, self._backPath)
return return
def setTextFont(self, family: str | None, pointSize: int = 12) -> None: def setGuiFont(self, value: QFont | str | None) -> None:
"""Update the GUI's font style from settings."""
if isinstance(value, QFont):
self.guiFont = value
elif value and isinstance(value, str):
self.guiFont = QFont()
self.guiFont.fromString(value)
else:
font = QFont()
fontDB = QFontDatabase()
if self.osWindows and "Arial" in fontDB.families():
# On Windows we default to Arial if possible
font.setFamily("Arial")
font.setPointSize(10)
else:
font = fontDB.systemFont(QFontDatabase.SystemFont.GeneralFont)
self.guiFont = font
logger.debug("GUI font set to: %s", describeFont(font))
QApplication.setFont(self.guiFont)
return
def setTextFont(self, value: QFont | str | None) -> None:
"""Set the text font if it exists. If it doesn't, or is None, """Set the text font if it exists. If it doesn't, or is None,
set to default font. set to default font.
""" """
fontDB = QFontDatabase() if isinstance(value, QFont):
fontFam = fontDB.families() self.textFont = value
self.textSize = pointSize elif value and isinstance(value, str):
if family is None or family not in fontFam: self.textFont = QFont()
logger.warning("Unknown font '%s'", family) self.textFont.fromString(value)
if self.osWindows and "Arial" in fontFam:
self.textFont = "Arial"
elif self.osDarwin and "Helvetica" in fontFam:
self.textFont = "Helvetica"
else:
self.textFont = fontDB.systemFont(QFontDatabase.SystemFont.GeneralFont).family()
else: else:
self.textFont = family fontDB = QFontDatabase()
fontFam = fontDB.families()
if self.osWindows and "Arial" in fontFam:
font = QFont()
font.setFamily("Arial")
font.setPointSize(12)
elif self.osDarwin and "Helvetica" in fontFam:
font = QFont()
font.setFamily("Helvetica")
font.setPointSize(12)
else:
font = fontDB.systemFont(QFontDatabase.SystemFont.GeneralFont)
self.textFont = font
logger.debug("Text font set to: %s", describeFont(font))
return return
## ##
@@ -502,12 +530,6 @@ class Config:
(self._dataPath / "syntax").mkdir(exist_ok=True) (self._dataPath / "syntax").mkdir(exist_ok=True)
(self._dataPath / "themes").mkdir(exist_ok=True) (self._dataPath / "themes").mkdir(exist_ok=True)
# Check if config file exists, and load it. If not, we save defaults
if (self._confPath / nwFiles.CONF_FILE).is_file():
self.loadConfig()
else:
self.saveConfig()
self._recentObj.loadCache() self._recentObj.loadCache()
self._checkOptionalPackages() self._checkOptionalPackages()
@@ -548,6 +570,14 @@ class Config:
conf = NWConfigParser() conf = NWConfigParser()
cnfPath = self._confPath / nwFiles.CONF_FILE cnfPath = self._confPath / nwFiles.CONF_FILE
if not cnfPath.exists():
# Initial file, so we just create one from defaults
self.setGuiFont(None)
self.setTextFont(None)
self.saveConfig()
return True
try: try:
with open(cnfPath, mode="r", encoding="utf-8") as inFile: with open(cnfPath, mode="r", encoding="utf-8") as inFile:
conf.read_file(inFile) conf.read_file(inFile)
@@ -561,10 +591,9 @@ class Config:
# Main # Main
sec = "Main" sec = "Main"
self.setGuiFont(conf.rdStr(sec, "font", ""))
self.guiTheme = conf.rdStr(sec, "theme", self.guiTheme) self.guiTheme = conf.rdStr(sec, "theme", self.guiTheme)
self.guiSyntax = conf.rdStr(sec, "syntax", self.guiSyntax) self.guiSyntax = conf.rdStr(sec, "syntax", self.guiSyntax)
self.guiFont = conf.rdStr(sec, "font", self.guiFont)
self.guiFontSize = conf.rdInt(sec, "fontsize", self.guiFontSize)
self.guiLocale = conf.rdStr(sec, "localisation", self.guiLocale) self.guiLocale = conf.rdStr(sec, "localisation", self.guiLocale)
self.hideVScroll = conf.rdBool(sec, "hidevscroll", self.hideVScroll) self.hideVScroll = conf.rdBool(sec, "hidevscroll", self.hideVScroll)
self.hideHScroll = conf.rdBool(sec, "hidehscroll", self.hideHScroll) self.hideHScroll = conf.rdBool(sec, "hidehscroll", self.hideHScroll)
@@ -591,8 +620,7 @@ class Config:
# Editor # Editor
sec = "Editor" sec = "Editor"
self.textFont = conf.rdStr(sec, "textfont", self.textFont) self.setTextFont(conf.rdStr(sec, "textfont", ""))
self.textSize = conf.rdInt(sec, "textsize", self.textSize)
self.textWidth = conf.rdInt(sec, "width", self.textWidth) self.textWidth = conf.rdInt(sec, "width", self.textWidth)
self.textMargin = conf.rdInt(sec, "margin", self.textMargin) self.textMargin = conf.rdInt(sec, "margin", self.textMargin)
self.tabWidth = conf.rdInt(sec, "tabwidth", self.tabWidth) self.tabWidth = conf.rdInt(sec, "tabwidth", self.tabWidth)
@@ -672,10 +700,9 @@ class Config:
} }
conf["Main"] = { conf["Main"] = {
"font": self.guiFont.toString(),
"theme": str(self.guiTheme), "theme": str(self.guiTheme),
"syntax": str(self.guiSyntax), "syntax": str(self.guiSyntax),
"font": str(self.guiFont),
"fontsize": str(self.guiFontSize),
"localisation": str(self.guiLocale), "localisation": str(self.guiLocale),
"hidevscroll": str(self.hideVScroll), "hidevscroll": str(self.hideVScroll),
"hidehscroll": str(self.hideHScroll), "hidehscroll": str(self.hideHScroll),
@@ -702,8 +729,7 @@ class Config:
} }
conf["Editor"] = { conf["Editor"] = {
"textfont": str(self.textFont), "textfont": self.textFont.toString(),
"textsize": str(self.textSize),
"width": str(self.textWidth), "width": str(self.textWidth),
"margin": str(self.textMargin), "margin": str(self.textMargin),
"tabwidth": str(self.tabWidth), "tabwidth": str(self.tabWidth),
+1 -1
View File
@@ -76,7 +76,7 @@ SETTINGS_TEMPLATE = {
"text.includeBodyText": (bool, True), "text.includeBodyText": (bool, True),
"text.ignoredKeywords": (str, ""), "text.ignoredKeywords": (str, ""),
"text.addNoteHeadings": (bool, True), "text.addNoteHeadings": (bool, True),
"format.textFont": (str, CONFIG.textFont), "format.textFont": (str, CONFIG.textFont.family()),
"format.textSize": (int, 12), "format.textSize": (int, 12),
"format.lineHeight": (float, 1.15, 0.75, 3.0), "format.lineHeight": (float, 1.15, 0.75, 3.0),
"format.justifyText": (bool, False), "format.justifyText": (bool, False),
+1 -1
View File
@@ -288,7 +288,7 @@ class NWBuildDocument:
textFont = self._build.getStr("format.textFont") textFont = self._build.getStr("format.textFont")
textSize = self._build.getInt("format.textSize") textSize = self._build.getInt("format.textSize")
fontFamily = textFont or CONFIG.textFont fontFamily = textFont or CONFIG.textFont.family()
bldFont = QFont(fontFamily, textSize) bldFont = QFont(fontFamily, textSize)
fontInfo = QFontInfo(bldFont) fontInfo = QFontInfo(bldFont)
textFixed = fontInfo.fixedPitch() textFixed = fontInfo.fixedPitch()
+26 -48
View File
@@ -27,7 +27,7 @@ from __future__ import annotations
import logging import logging
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QCloseEvent, QFont, QKeyEvent, QKeySequence from PyQt5.QtGui import QCloseEvent, QKeyEvent, QKeySequence
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAbstractButton, QApplication, QCompleter, QDialog, QDialogButtonBox, QAbstractButton, QApplication, QCompleter, QDialog, QDialogButtonBox,
QFileDialog, QFontDialog, QHBoxLayout, QLineEdit, QPushButton, QVBoxLayout, QFileDialog, QFontDialog, QHBoxLayout, QLineEdit, QPushButton, QVBoxLayout,
@@ -35,6 +35,7 @@ from PyQt5.QtWidgets import (
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import describeFont
from novelwriter.constants import nwConst, nwUnicode from novelwriter.constants import nwConst, 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
@@ -135,6 +136,11 @@ class GuiPreferences(QDialog):
iSz = SHARED.theme.baseIconSize iSz = SHARED.theme.baseIconSize
boxFixed = 5*SHARED.theme.textNWidth boxFixed = 5*SHARED.theme.textNWidth
minWidth = CONFIG.pxInt(200) minWidth = CONFIG.pxInt(200)
fontWidth = CONFIG.pxInt(162)
# Temporary Variables
self._guiFont = CONFIG.guiFont
self._textFont = CONFIG.textFont
# Label # Label
self.sidebar.addLabel(self.tr("General")) self.sidebar.addLabel(self.tr("General"))
@@ -174,27 +180,17 @@ class GuiPreferences(QDialog):
# Application Font Family # Application Font Family
self.guiFont = QLineEdit(self) self.guiFont = QLineEdit(self)
self.guiFont.setReadOnly(True) self.guiFont.setReadOnly(True)
self.guiFont.setMinimumWidth(CONFIG.pxInt(162)) self.guiFont.setMinimumWidth(fontWidth)
self.guiFont.setText(CONFIG.guiFont) self.guiFont.setText(describeFont(self._guiFont))
self.guiFont.setCursorPosition(0)
self.guiFontButton = NIconToolButton(self, iSz, "more") self.guiFontButton = NIconToolButton(self, iSz, "more")
self.guiFontButton.clicked.connect(self._selectGuiFont) self.guiFontButton.clicked.connect(self._selectGuiFont)
self.mainForm.addRow( self.mainForm.addRow(
self.tr("Application font family"), self.guiFont, self.tr("Application font"), self.guiFont,
self.tr("Requires restart to take effect."), stretch=(3, 2), self.tr("Requires restart to take effect."), stretch=(3, 2),
button=self.guiFontButton button=self.guiFontButton
) )
# Application Font Size
self.guiFontSize = NSpinBox(self)
self.guiFontSize.setMinimum(8)
self.guiFontSize.setMaximum(60)
self.guiFontSize.setSingleStep(1)
self.guiFontSize.setValue(CONFIG.guiFontSize)
self.mainForm.addRow(
self.tr("Application font size"), self.guiFontSize,
self.tr("Requires restart to take effect."), unit=self.tr("pt")
)
# Vertical Scrollbars # Vertical Scrollbars
self.hideVScroll = NSwitch(self) self.hideVScroll = NSwitch(self)
self.hideVScroll.setChecked(CONFIG.hideVScroll) self.hideVScroll.setChecked(CONFIG.hideVScroll)
@@ -234,27 +230,17 @@ class GuiPreferences(QDialog):
# Document Font Family # Document Font Family
self.textFont = QLineEdit(self) self.textFont = QLineEdit(self)
self.textFont.setReadOnly(True) self.textFont.setReadOnly(True)
self.textFont.setMinimumWidth(CONFIG.pxInt(162)) self.textFont.setMinimumWidth(fontWidth)
self.textFont.setText(CONFIG.textFont) self.textFont.setText(describeFont(CONFIG.textFont))
self.textFont.setCursorPosition(0)
self.textFontButton = NIconToolButton(self, iSz, "more") self.textFontButton = NIconToolButton(self, iSz, "more")
self.textFontButton.clicked.connect(self._selectTextFont) self.textFontButton.clicked.connect(self._selectTextFont)
self.mainForm.addRow( self.mainForm.addRow(
self.tr("Document font family"), self.textFont, self.tr("Document font"), self.textFont,
self.tr("Applies to both document editor and viewer."), stretch=(3, 2), self.tr("Applies to both document editor and viewer."), stretch=(3, 2),
button=self.textFontButton button=self.textFontButton
) )
# Document Font Size
self.textSize = NSpinBox(self)
self.textSize.setMinimum(8)
self.textSize.setMaximum(60)
self.textSize.setSingleStep(1)
self.textSize.setValue(CONFIG.textSize)
self.mainForm.addRow(
self.tr("Document font size"), self.textSize,
self.tr("Applies to both document editor and viewer."), unit=self.tr("pt")
)
# Emphasise Labels # Emphasise Labels
self.emphLabels = NSwitch(self) self.emphLabels = NSwitch(self)
self.emphLabels.setChecked(CONFIG.emphLabels) self.emphLabels.setChecked(CONFIG.emphLabels)
@@ -818,25 +804,21 @@ class GuiPreferences(QDialog):
@pyqtSlot() @pyqtSlot()
def _selectGuiFont(self) -> None: def _selectGuiFont(self) -> None:
"""Open the QFontDialog and set a font for the font style.""" """Open the QFontDialog and set a font for the font style."""
current = QFont() font, status = QFontDialog.getFont(self._guiFont, self)
current.setFamily(CONFIG.guiFont)
current.setPointSize(CONFIG.guiFontSize)
font, status = QFontDialog.getFont(current, self)
if status: if status:
self.guiFont.setText(font.family()) self.guiFont.setText(describeFont(font))
self.guiFontSize.setValue(font.pointSize()) self.guiFont.setCursorPosition(0)
self._guiFont = font
return return
@pyqtSlot() @pyqtSlot()
def _selectTextFont(self) -> None: def _selectTextFont(self) -> None:
"""Open the QFontDialog and set a font for the font style.""" """Open the QFontDialog and set a font for the font style."""
current = QFont() font, status = QFontDialog.getFont(CONFIG.textFont, self)
current.setFamily(CONFIG.textFont)
current.setPointSize(CONFIG.textSize)
font, status = QFontDialog.getFont(current, self)
if status: if status:
self.textFont.setText(font.family()) self.textFont.setText(describeFont(font))
self.textSize.setValue(font.pointSize()) self.textFont.setCursorPosition(0)
self._textFont = font
return return
@pyqtSlot() @pyqtSlot()
@@ -892,20 +874,16 @@ class GuiPreferences(QDialog):
# Appearance # Appearance
guiLocale = self.guiLocale.currentData() guiLocale = self.guiLocale.currentData()
guiTheme = self.guiTheme.currentData() guiTheme = self.guiTheme.currentData()
guiFont = self.guiFont.text()
guiFontSize = self.guiFontSize.value()
updateTheme |= CONFIG.guiTheme != guiTheme updateTheme |= CONFIG.guiTheme != guiTheme
needsRestart |= CONFIG.guiLocale != guiLocale needsRestart |= CONFIG.guiLocale != guiLocale
needsRestart |= CONFIG.guiFont != guiFont needsRestart |= CONFIG.guiFont != self._guiFont
needsRestart |= CONFIG.guiFontSize != guiFontSize
CONFIG.guiLocale = guiLocale CONFIG.guiLocale = guiLocale
CONFIG.guiTheme = guiTheme CONFIG.guiTheme = guiTheme
CONFIG.guiFont = guiFont
CONFIG.guiFontSize = guiFontSize
CONFIG.hideVScroll = self.hideVScroll.isChecked() CONFIG.hideVScroll = self.hideVScroll.isChecked()
CONFIG.hideHScroll = self.hideHScroll.isChecked() CONFIG.hideHScroll = self.hideHScroll.isChecked()
CONFIG.setGuiFont(self._guiFont)
# Document Style # Document Style
guiSyntax = self.guiSyntax.currentData() guiSyntax = self.guiSyntax.currentData()
@@ -918,7 +896,7 @@ class GuiPreferences(QDialog):
CONFIG.emphLabels = emphLabels CONFIG.emphLabels = emphLabels
CONFIG.showFullPath = self.showFullPath.isChecked() CONFIG.showFullPath = self.showFullPath.isChecked()
CONFIG.incNotesWCount = self.incNotesWCount.isChecked() CONFIG.incNotesWCount = self.incNotesWCount.isChecked()
CONFIG.setTextFont(self.textFont.text(), self.textSize.value()) CONFIG.setTextFont(self._textFont)
# Auto Save # Auto Save
CONFIG.autoSaveDoc = self.autoSaveDoc.value() CONFIG.autoSaveDoc = self.autoSaveDoc.value()
+1 -7
View File
@@ -377,16 +377,10 @@ class GuiDocEditor(QPlainTextEdit):
special attention since there appears to be a bug in Qt 5.15.3. special attention since there appears to be a bug in Qt 5.15.3.
See issues #1862 and #1875. See issues #1862 and #1875.
""" """
font = self.font() self.setFont(CONFIG.textFont)
font.setFamily(CONFIG.textFont)
font.setPointSize(CONFIG.textSize)
self.setFont(font)
# Reset sub-widget font to GUI font
self.docHeader.updateFont() self.docHeader.updateFont()
self.docFooter.updateFont() self.docFooter.updateFont()
self.docSearch.updateFont() self.docSearch.updateFont()
return return
def loadText(self, tHandle: str, tLine: int | None = None) -> bool: def loadText(self, tHandle: str, tLine: int | None = None) -> bool:
+1 -1
View File
@@ -474,7 +474,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
charFormat.setBackground(QBrush(color, Qt.BrushStyle.SolidPattern)) charFormat.setBackground(QBrush(color, Qt.BrushStyle.SolidPattern))
if size: if size:
charFormat.setFontPointSize(round(size*CONFIG.textSize)) charFormat.setFontPointSize(round(size*CONFIG.textFont.pointSize()))
self._hStyles[name] = charFormat self._hStyles[name] = charFormat
+1 -7
View File
@@ -186,15 +186,9 @@ class GuiDocViewer(QTextBrowser):
special attention since there appears to be a bug in Qt 5.15.3. special attention since there appears to be a bug in Qt 5.15.3.
See issues #1862 and #1875. See issues #1862 and #1875.
""" """
font = self.font() self.setFont(CONFIG.textFont)
font.setFamily(CONFIG.textFont)
font.setPointSize(CONFIG.textSize)
self.setFont(font)
# Reset sub-widget font to GUI font
self.docHeader.updateFont() self.docHeader.updateFont()
self.docFooter.updateFont() self.docFooter.updateFont()
return return
def loadText(self, tHandle: str, updateHistory: bool = True) -> bool: def loadText(self, tHandle: str, updateHistory: bool = True) -> bool:
-24
View File
@@ -114,9 +114,6 @@ class GuiTheme:
# Class Setup # Class Setup
# =========== # ===========
# Init GUI Font
self._setGuiFont()
# Load Themes # Load Themes
self._guiPalette = QPalette() self._guiPalette = QPalette()
self._themeList: list[tuple[str, str]] = [] self._themeList: list[tuple[str, str]] = []
@@ -411,27 +408,6 @@ class GuiTheme:
self.errorText = QColor(255, 0, 0) self.errorText = QColor(255, 0, 0)
return return
def _setGuiFont(self) -> None:
"""Update the GUI's font style from settings."""
font = QFont()
fontDB = QFontDatabase()
if CONFIG.guiFont not in fontDB.families():
if CONFIG.osWindows and "Arial" in fontDB.families():
# On Windows we default to Arial if possible
font.setFamily("Arial")
font.setPointSize(10)
else:
font = fontDB.systemFont(QFontDatabase.SystemFont.GeneralFont)
CONFIG.guiFont = font.family()
CONFIG.guiFontSize = font.pointSize()
else:
font.setFamily(CONFIG.guiFont)
font.setPointSize(CONFIG.guiFontSize)
QApplication.setFont(font)
return
def _listConf(self, targetDict: dict, checkDir: Path) -> bool: def _listConf(self, targetDict: dict, checkDir: Path) -> bool:
"""Scan for theme config files and populate the dictionary.""" """Scan for theme config files and populate the dictionary."""
if not checkDir.is_dir(): if not checkDir.is_dir():
+1 -1
View File
@@ -787,7 +787,7 @@ class _PreviewWidget(QTextBrowser):
self._updateDocMargins() self._updateDocMargins()
self._updateBuildAge() self._updateBuildAge()
self.setTextFont(CONFIG.textFont, CONFIG.textSize) self.setTextFont(CONFIG.textFont.family(), CONFIG.textFont.pointSize())
# Age Timer # Age Timer
self.ageTimer = QTimer(self) self.ageTimer = QTimer(self)
+1 -1
View File
@@ -1175,7 +1175,7 @@ class _FormatTab(NScrollableForm):
"""Populate the widgets.""" """Populate the widgets."""
textFont = self._build.getStr("format.textFont") textFont = self._build.getStr("format.textFont")
if not textFont: if not textFont:
textFont = str(CONFIG.textFont) textFont = str(CONFIG.textFont.family())
self.textFont.setText(textFont) self.textFont.setText(textFont)
self.textSize.setValue(self._build.getInt("format.textSize")) self.textSize.setValue(self._build.getInt("format.textSize"))
+1
View File
@@ -52,6 +52,7 @@ def resetConfigVars():
""" """
CONFIG.setLastPath(_TMP_ROOT) CONFIG.setLastPath(_TMP_ROOT)
CONFIG.setBackupPath(_TMP_ROOT) CONFIG.setBackupPath(_TMP_ROOT)
CONFIG.setGuiFont(None)
CONFIG.setTextFont(None) CONFIG.setTextFont(None)
CONFIG._homePath = _TMP_ROOT CONFIG._homePath = _TMP_ROOT
CONFIG.guiLocale = "en_GB" CONFIG.guiLocale = "en_GB"
+2 -4
View File
@@ -1,11 +1,10 @@
[Meta] [Meta]
timestamp = 2024-05-13 15:59:59 timestamp = 2024-05-20 16:48:20
[Main] [Main]
font =
theme = default theme = default
syntax = default_light syntax = default_light
font =
fontsize = 11
localisation = en_GB localisation = en_GB
hidevscroll = False hidevscroll = False
hidehscroll = False hidehscroll = False
@@ -30,7 +29,6 @@ askbeforebackup = True
[Editor] [Editor]
textfont = textfont =
textsize = 12
width = 700 width = 700
margin = 40 margin = 40
tabwidth = 40 tabwidth = 40
+16 -7
View File
@@ -28,16 +28,16 @@ from xml.etree import ElementTree as ET
import pytest import pytest
from PyQt5.QtCore import QUrl from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QColor, QDesktopServices 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, elide, formatFileFilter, checkString, checkStringNone, checkUuid, cssCol, describeFont, elide,
formatInt, formatTime, formatTimeStamp, formatVersion, fuzzyTime, formatFileFilter, formatInt, formatTime, formatTimeStamp, formatVersion,
getFileSize, hexToInt, isHandle, isItemClass, isItemLayout, isItemType, fuzzyTime, getFileSize, hexToInt, isHandle, isItemClass, isItemLayout,
isListInstance, isTitleTag, jsonEncode, makeFileNameSafe, minmax, isItemType, isListInstance, isTitleTag, jsonEncode, makeFileNameSafe,
numberToRoman, openExternalPath, readTextFile, simplified, transferCase, minmax, numberToRoman, openExternalPath, readTextFile, simplified,
xmlIndent, yesNo transferCase, xmlIndent, yesNo
) )
from tests.mocked import causeOSError from tests.mocked import causeOSError
@@ -487,6 +487,15 @@ def testBaseCommon_cssCol():
assert cssCol(QColor(10, 20, 30, 40)) == "rgba(10, 20, 30, 40)" assert cssCol(QColor(10, 20, 30, 40)) == "rgba(10, 20, 30, 40)"
@pytest.mark.base
def testBaseCommon_describeFont():
"""Test the describeFont function."""
fontDB = QFontDatabase()
font = fontDB.systemFont(QFontDatabase.SystemFont.GeneralFont)
assert font.family() in describeFont(font)
assert describeFont(None) == "Error" # type: ignore
@pytest.mark.base @pytest.mark.base
def testBaseCommon_jsonEncode(): def testBaseCommon_jsonEncode():
"""Test the jsonEncode function.""" """Test the jsonEncode function."""
+7 -2
View File
@@ -103,15 +103,19 @@ def testBaseConfig_InitLoadSave(monkeypatch, fncPath, tstPaths):
if confFile.is_file(): if confFile.is_file():
confFile.unlink() confFile.unlink()
# Running init against a new oath should write a new config file # Running init + load against a new path should write a new config file
tstConf.initConfig(confPath=fncPath, dataPath=fncPath) tstConf.initConfig(confPath=fncPath, dataPath=fncPath)
assert tstConf._confPath == fncPath assert tstConf._confPath == fncPath
assert tstConf._dataPath == fncPath assert tstConf._dataPath == fncPath
tstConf.loadConfig()
assert confFile.exists() assert confFile.exists()
# Check that we have a default file # Check that we have a default file
copyfile(confFile, testFile) copyfile(confFile, testFile)
ignore = ("timestamp", "lastnotes", "localisation", "lastpath", "backuppath") ignore = (
"timestamp", "lastnotes", "localisation",
"lastpath", "backuppath", "font", "textfont"
)
assert cmpFiles(testFile, compFile, ignoreStart=ignore) assert cmpFiles(testFile, compFile, ignoreStart=ignore)
tstConf.errorText() # This clears the error cache tstConf.errorText() # This clears the error cache
@@ -136,6 +140,7 @@ def testBaseConfig_InitLoadSave(monkeypatch, fncPath, tstPaths):
newConf = Config() newConf = Config()
newConf.initConfig(confPath=fncPath, dataPath=fncPath) newConf.initConfig(confPath=fncPath, dataPath=fncPath)
newConf.loadConfig()
assert newConf.guiTheme == "foo" assert newConf.guiTheme == "foo"
assert newConf.guiSyntax == "bar" assert newConf.guiSyntax == "bar"
+7 -13
View File
@@ -23,7 +23,7 @@ from __future__ import annotations
import pytest import pytest
from PyQt5.QtCore import QEvent, Qt from PyQt5.QtCore import QEvent, Qt
from PyQt5.QtGui import QFontDatabase, QKeyEvent from PyQt5.QtGui import QFont, QFontDatabase, QKeyEvent
from PyQt5.QtWidgets import QAction, QFileDialog, QFontDialog from PyQt5.QtWidgets import QAction, QFileDialog, QFontDialog
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
@@ -173,32 +173,28 @@ def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, tstPaths):
prefs.guiLocale.setCurrentIndex(prefs.guiLocale.findData("en_US")) prefs.guiLocale.setCurrentIndex(prefs.guiLocale.findData("en_US"))
prefs.guiTheme.setCurrentIndex(prefs.guiTheme.findData("default_dark")) prefs.guiTheme.setCurrentIndex(prefs.guiTheme.findData("default_dark"))
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(QFontDialog, "getFont", lambda *a: (MockFont(), True)) mp.setattr(QFontDialog, "getFont", lambda *a: (QFont(), True))
prefs.guiFontButton.click() prefs.guiFontButton.click()
prefs.guiFontSize.stepDown() # Should change it to 41
prefs.hideVScroll.setChecked(True) prefs.hideVScroll.setChecked(True)
prefs.hideHScroll.setChecked(True) prefs.hideHScroll.setChecked(True)
assert CONFIG.guiLocale != "en_US" assert CONFIG.guiLocale != "en_US"
assert CONFIG.guiTheme != "default_dark" assert CONFIG.guiTheme != "default_dark"
assert CONFIG.guiFont != "TestFont" assert CONFIG.guiFont.family() != ""
assert CONFIG.guiFontSize < 42
assert CONFIG.hideVScroll is False assert CONFIG.hideVScroll is False
assert CONFIG.hideHScroll is False assert CONFIG.hideHScroll is False
# Document Style # Document Style
prefs.guiSyntax.setCurrentIndex(prefs.guiSyntax.findData("default_dark")) prefs.guiSyntax.setCurrentIndex(prefs.guiSyntax.findData("default_dark"))
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(QFontDialog, "getFont", lambda *a: (MockFont(), True)) mp.setattr(QFontDialog, "getFont", lambda *a: (QFont(), True))
prefs.textFontButton.click() prefs.textFontButton.click()
prefs.textSize.stepDown() # Should change it to 41
prefs.emphLabels.setChecked(False) prefs.emphLabels.setChecked(False)
prefs.showFullPath.setChecked(False) prefs.showFullPath.setChecked(False)
prefs.incNotesWCount.setChecked(False) prefs.incNotesWCount.setChecked(False)
assert CONFIG.guiSyntax != "default_dark" assert CONFIG.guiSyntax != "default_dark"
assert CONFIG.textFont != "testFont" assert CONFIG.textFont.family() != ""
assert CONFIG.textSize < 42
assert CONFIG.emphLabels is True assert CONFIG.emphLabels is True
assert CONFIG.showFullPath is True assert CONFIG.showFullPath is True
assert CONFIG.incNotesWCount is True assert CONFIG.incNotesWCount is True
@@ -341,15 +337,13 @@ def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, tstPaths):
# Appearance # Appearance
assert CONFIG.guiLocale == "en_US" assert CONFIG.guiLocale == "en_US"
assert CONFIG.guiTheme == "default_dark" assert CONFIG.guiTheme == "default_dark"
assert CONFIG.guiFont == "TestFont" assert CONFIG.guiFont == QFont()
assert CONFIG.guiFontSize == 41
assert CONFIG.hideVScroll is True assert CONFIG.hideVScroll is True
assert CONFIG.hideHScroll is True assert CONFIG.hideHScroll is True
# Document Style # Document Style
assert CONFIG.guiSyntax == "default_dark" assert CONFIG.guiSyntax == "default_dark"
assert CONFIG.textFont == "TestFont" assert CONFIG.textFont == QFont()
assert CONFIG.textSize == 41
assert CONFIG.emphLabels is False assert CONFIG.emphLabels is False
assert CONFIG.showFullPath is False assert CONFIG.showFullPath is False
assert CONFIG.incNotesWCount is False assert CONFIG.incNotesWCount is False
+3 -3
View File
@@ -23,7 +23,7 @@ from __future__ import annotations
import pytest import pytest
from PyQt5.QtCore import QEvent, Qt, QThreadPool from PyQt5.QtCore import QEvent, Qt, QThreadPool
from PyQt5.QtGui import QClipboard, QMouseEvent, QTextBlock, QTextCursor, QTextOption from PyQt5.QtGui import QClipboard, QFont, QMouseEvent, QTextBlock, QTextCursor, QTextOption
from PyQt5.QtWidgets import QAction, QApplication, QMenu from PyQt5.QtWidgets import QAction, QApplication, QMenu
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
@@ -84,7 +84,7 @@ def testGuiEditor_Init(qtbot, nwGUI, projPath, ipsumText, mockRnd):
assert docEditor.docHeader._docOutline == {0: "### New Scene"} assert docEditor.docHeader._docOutline == {0: "### New Scene"}
# Check that editor handles settings # Check that editor handles settings
CONFIG.textFont = "" CONFIG.textFont = QFont()
CONFIG.doJustify = True CONFIG.doJustify = True
CONFIG.showTabsNSpaces = True CONFIG.showTabsNSpaces = True
CONFIG.showLineEndings = True CONFIG.showLineEndings = True
@@ -96,7 +96,7 @@ def testGuiEditor_Init(qtbot, nwGUI, projPath, ipsumText, mockRnd):
docEditor.initEditor() docEditor.initEditor()
qDoc = docEditor.document() qDoc = docEditor.document()
assert CONFIG.textFont == qDoc.defaultFont().family() assert CONFIG.textFont == qDoc.defaultFont()
assert qDoc.defaultTextOption().alignment() == QtAlignJustify assert qDoc.defaultTextOption().alignment() == QtAlignJustify
assert qDoc.defaultTextOption().flags() & QTextOption.ShowTabsAndSpaces assert qDoc.defaultTextOption().flags() & QTextOption.ShowTabsAndSpaces
assert qDoc.defaultTextOption().flags() & QTextOption.ShowLineAndParagraphSeparators assert qDoc.defaultTextOption().flags() & QTextOption.ShowLineAndParagraphSeparators
-21
View File
@@ -48,27 +48,6 @@ def testGuiTheme_Main(qtbot, nwGUI, tstPaths):
assert mSize > 0 assert mSize > 0
assert mainTheme.getTextWidth("m", mainTheme.guiFont) == mSize assert mainTheme.getTextWidth("m", mainTheme.guiFont) == mSize
# Init Fonts
# ==========
# The defaults should be set
defaultFont = CONFIG.guiFont
defaultSize = CONFIG.guiFontSize
# CHange them to nonsense values
CONFIG.guiFont = "notafont"
CONFIG.guiFontSize = 99
# Let the theme class set them back to default
mainTheme._setGuiFont()
assert CONFIG.guiFont == defaultFont
assert CONFIG.guiFontSize == defaultSize
# A second call should just restore the defaults again
mainTheme._setGuiFont()
assert CONFIG.guiFont == defaultFont
assert CONFIG.guiFontSize == defaultSize
# Scan for Themes # Scan for Themes
# =============== # ===============
+1 -1
View File
@@ -547,7 +547,7 @@ def testBuildSettings_Format(monkeypatch, qtbot, nwGUI):
"""Test the Format Tab of the GuiBuildSettings dialog.""" """Test the Format Tab of the GuiBuildSettings dialog."""
build = BuildSettings() build = BuildSettings()
textFont = str(CONFIG.textFont) textFont = str(CONFIG.textFont.family())
build.setValue("format.buildLang", "en_US") build.setValue("format.buildLang", "en_US")
build.setValue("format.textFont", "") # Will fall back to config value build.setValue("format.textFont", "") # Will fall back to config value