Remove syntax theme parsing and add light and dark theme user settings
This commit is contained in:
@@ -31,6 +31,7 @@ import xml.etree.ElementTree as ET
|
||||
|
||||
from configparser import ConfigParser
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Literal, TypeGuard, TypeVar
|
||||
from urllib.parse import urljoin
|
||||
@@ -676,6 +677,9 @@ def openExternalPath(path: Path) -> bool:
|
||||
# Classes
|
||||
##
|
||||
|
||||
_T_Enum = TypeVar("_T_Enum", bound=Enum)
|
||||
|
||||
|
||||
class NWConfigParser(ConfigParser):
|
||||
"""Common: Adapted Config Parser
|
||||
|
||||
@@ -736,3 +740,10 @@ class NWConfigParser(ConfigParser):
|
||||
for i in range(min(len(data), len(result))):
|
||||
result[i] = checkInt(data[i].strip(), result[i])
|
||||
return result
|
||||
|
||||
def rdEnum(self, section: str, option: str, default: _T_Enum) -> _T_Enum:
|
||||
"""Read enum value."""
|
||||
if self.has_option(section, option):
|
||||
data = self.get(section, option, fallback="")
|
||||
return type(default).__members__.get(data.upper(), default)
|
||||
return default
|
||||
|
||||
+22
-18
@@ -45,6 +45,7 @@ from novelwriter.common import (
|
||||
formatTimeStamp, processDialogSymbols, simplified
|
||||
)
|
||||
from novelwriter.constants import nwFiles, nwHtmlUnicode, nwQuotes, nwUnicode
|
||||
from novelwriter.enum import nwTheme
|
||||
from novelwriter.error import formatException, logException
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -55,8 +56,8 @@ if TYPE_CHECKING:
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
DEF_GUI = "default"
|
||||
DEF_SYNTAX = "default_light"
|
||||
DEF_GUI_DARK = "default_dark"
|
||||
DEF_GUI_LIGHT = "default_light"
|
||||
DEF_ICONS = "material_rounded_normal"
|
||||
DEF_TREECOL = "theme"
|
||||
|
||||
@@ -69,22 +70,22 @@ class Config:
|
||||
"_manuals", "_nwLangPath", "_qLocale", "_qtLangPath", "_qtTrans", "_recentPaths",
|
||||
"_recentProjects", "_splash", "allowOpenDial", "altDialogClose", "altDialogOpen",
|
||||
"appHandle", "appName", "askBeforeBackup", "askBeforeExit", "autoSaveDoc", "autoSaveProj",
|
||||
"autoScroll", "autoScrollPos", "autoSelect", "backupOnClose", "cursorWidth", "dialogLine",
|
||||
"dialogStyle", "doJustify", "doReplace", "doReplaceDQuote", "doReplaceDash",
|
||||
"autoScroll", "autoScrollPos", "autoSelect", "backupOnClose", "cursorWidth", "darkTheme",
|
||||
"dialogLine", "dialogStyle", "doJustify", "doReplace", "doReplaceDQuote", "doReplaceDash",
|
||||
"doReplaceDots", "doReplaceSQuote", "emphLabels", "fmtApostrophe", "fmtDQuoteClose",
|
||||
"fmtDQuoteOpen", "fmtPadAfter", "fmtPadBefore", "fmtPadThin", "fmtSQuoteClose",
|
||||
"fmtSQuoteOpen", "focusWidth", "guiFont", "guiLocale", "guiSyntax", "guiTheme",
|
||||
"hasEnchant", "hideFocusFooter", "hideHScroll", "hideVScroll", "highlightEmph", "hostName",
|
||||
"iconColDocs", "iconColTree", "iconTheme", "incNotesWCount", "isDebug", "kernelVer",
|
||||
"lastNotes", "mainPanePos", "mainWinSize", "memInfo", "narratorBreak", "narratorDialog",
|
||||
"nativeFont", "osDarwin", "osLinux", "osType", "osUnknown", "osWindows", "outlinePanePos",
|
||||
"fmtSQuoteOpen", "focusWidth", "guiFont", "guiLocale", "hasEnchant", "hideFocusFooter",
|
||||
"hideHScroll", "hideVScroll", "highlightEmph", "hostName", "iconColDocs", "iconColTree",
|
||||
"iconTheme", "incNotesWCount", "isDebug", "kernelVer", "lastNotes", "lightTheme",
|
||||
"mainPanePos", "mainWinSize", "memInfo", "narratorBreak", "narratorDialog", "nativeFont",
|
||||
"osDarwin", "osLinux", "osType", "osUnknown", "osWindows", "outlinePanePos",
|
||||
"prefsWinSize", "scrollPastEnd", "searchCase", "searchLoop", "searchMatchCap",
|
||||
"searchNextFile", "searchProjCase", "searchProjRegEx", "searchProjWord", "searchRegEx",
|
||||
"searchWord", "showEditToolBar", "showFullPath", "showLineEndings", "showMultiSpaces",
|
||||
"showSessionTime", "showTabsNSpaces", "showViewerPanel", "spellLanguage", "stopWhenIdle",
|
||||
"tabWidth", "textFont", "textMargin", "textWidth", "useCharCount", "userIdleTime",
|
||||
"verPyQtString", "verPyQtValue", "verPyString", "verQtString", "verQtValue",
|
||||
"viewComments", "viewPanePos", "viewSynopsis", "welcomeWinSize",
|
||||
"tabWidth", "textFont", "textMargin", "textWidth", "themeMode", "useCharCount",
|
||||
"userIdleTime", "verPyQtString", "verPyQtValue", "verPyString", "verQtString",
|
||||
"verQtValue", "viewComments", "viewPanePos", "viewSynopsis", "welcomeWinSize",
|
||||
)
|
||||
|
||||
LANG_NW = 1
|
||||
@@ -153,8 +154,9 @@ class Config:
|
||||
|
||||
# General GUI Settings
|
||||
self.guiLocale = self._qLocale.name()
|
||||
self.guiTheme = DEF_GUI # GUI theme
|
||||
self.guiSyntax = DEF_SYNTAX # Syntax theme
|
||||
self.lightTheme = DEF_GUI_LIGHT # Light GUI theme
|
||||
self.darkTheme = DEF_GUI_DARK # Dark GUI theme
|
||||
self.themeMode = nwTheme.AUTO # Colour theme mode
|
||||
self.guiFont = QFont() # Main GUI font
|
||||
self.hideVScroll = False # Hide vertical scroll bars on main widgets
|
||||
self.hideHScroll = False # Hide horizontal scroll bars on main widgets
|
||||
@@ -626,8 +628,9 @@ class Config:
|
||||
# Main
|
||||
sec = "Main"
|
||||
self.setGuiFont(conf.rdStr(sec, "font", ""))
|
||||
self.guiTheme = conf.rdStr(sec, "theme", self.guiTheme)
|
||||
self.guiSyntax = conf.rdStr(sec, "syntax", self.guiSyntax)
|
||||
self.lightTheme = conf.rdStr(sec, "lighttheme", self.lightTheme)
|
||||
self.darkTheme = conf.rdStr(sec, "darktheme", self.darkTheme)
|
||||
self.themeMode = conf.rdEnum(sec, "thememode", self.themeMode)
|
||||
self.iconTheme = conf.rdStr(sec, "icons", self.iconTheme)
|
||||
self.iconColTree = conf.rdStr(sec, "iconcoltree", self.iconColTree)
|
||||
self.iconColDocs = conf.rdBool(sec, "iconcoldocs", self.iconColDocs)
|
||||
@@ -751,8 +754,9 @@ class Config:
|
||||
|
||||
conf["Main"] = {
|
||||
"font": self.guiFont.toString(),
|
||||
"theme": str(self.guiTheme),
|
||||
"syntax": str(self.guiSyntax),
|
||||
"lighttheme": str(self.lightTheme),
|
||||
"darktheme": str(self.darkTheme),
|
||||
"thememode": self.themeMode.name,
|
||||
"icons": str(self.iconTheme),
|
||||
"iconcoltree": str(self.iconColTree),
|
||||
"iconcoldocs": str(self.iconColDocs),
|
||||
|
||||
@@ -35,7 +35,7 @@ from PyQt6.QtWidgets import (
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.common import compact, describeFont, processDialogSymbols, uniqueCompact
|
||||
from novelwriter.config import DEF_GUI, DEF_ICONS, DEF_SYNTAX, DEF_TREECOL
|
||||
from novelwriter.config import DEF_GUI_DARK, DEF_GUI_LIGHT, DEF_ICONS, DEF_TREECOL
|
||||
from novelwriter.constants import nwLabels, nwQuotes, nwUnicode, trConst
|
||||
from novelwriter.dialogs.quotes import GuiQuoteSelect
|
||||
from novelwriter.extensions.configlayout import NColorLabel, NScrollableForm
|
||||
@@ -165,14 +165,24 @@ class GuiPreferences(NDialog):
|
||||
)
|
||||
|
||||
# Colour Theme
|
||||
self.guiTheme = NComboBox(self)
|
||||
self.guiTheme.setMinimumWidth(200)
|
||||
for theme, name in SHARED.theme.listThemes():
|
||||
self.guiTheme.addItem(name, theme)
|
||||
self.guiTheme.setCurrentData(CONFIG.guiTheme, DEF_GUI)
|
||||
self.lightTheme = NComboBox(self)
|
||||
self.lightTheme.setMinimumWidth(200)
|
||||
self.darkTheme = NComboBox(self)
|
||||
self.darkTheme.setMinimumWidth(200)
|
||||
for theme, name, dark in SHARED.theme.listThemes():
|
||||
if dark:
|
||||
self.darkTheme.addItem(name, theme)
|
||||
else:
|
||||
self.lightTheme.addItem(name, theme)
|
||||
self.lightTheme.setCurrentData(CONFIG.lightTheme, DEF_GUI_LIGHT)
|
||||
self.darkTheme.setCurrentData(CONFIG.darkTheme, DEF_GUI_DARK)
|
||||
|
||||
self.mainForm.addRow(
|
||||
self.tr("Colour theme"), self.guiTheme,
|
||||
self.tr("Light colour theme"), self.lightTheme,
|
||||
self.tr("User interface colour theme."), stretch=(3, 2)
|
||||
)
|
||||
self.mainForm.addRow(
|
||||
self.tr("Dark colour theme"), self.darkTheme,
|
||||
self.tr("User interface colour theme."), stretch=(3, 2)
|
||||
)
|
||||
|
||||
@@ -242,18 +252,6 @@ class GuiPreferences(NDialog):
|
||||
self.sidebar.addButton(title, section)
|
||||
self.mainForm.addGroupLabel(title, section)
|
||||
|
||||
# Document Colour Theme
|
||||
self.guiSyntax = NComboBox(self)
|
||||
self.guiSyntax.setMinimumWidth(200)
|
||||
for syntax, name in SHARED.theme.listSyntax():
|
||||
self.guiSyntax.addItem(name, syntax)
|
||||
self.guiSyntax.setCurrentData(CONFIG.guiSyntax, DEF_SYNTAX)
|
||||
|
||||
self.mainForm.addRow(
|
||||
self.tr("Document colour theme"), self.guiSyntax,
|
||||
self.tr("Colour theme for the editor and viewer."), stretch=(3, 2)
|
||||
)
|
||||
|
||||
# Document Font Family
|
||||
self.textFont = QLineEdit(self)
|
||||
self.textFont.setReadOnly(True)
|
||||
@@ -966,18 +964,23 @@ class GuiPreferences(NDialog):
|
||||
|
||||
# Appearance
|
||||
guiLocale = self.guiLocale.currentData()
|
||||
guiTheme = self.guiTheme.currentData()
|
||||
lightTheme = self.lightTheme.currentData()
|
||||
darkTheme = self.darkTheme.currentData()
|
||||
iconTheme = self.iconTheme.currentData()
|
||||
useCharCount = self.useCharCount.isChecked()
|
||||
|
||||
updateTheme |= CONFIG.guiTheme != guiTheme
|
||||
updateTheme |= CONFIG.lightTheme != lightTheme
|
||||
updateTheme |= CONFIG.darkTheme != darkTheme
|
||||
updateTheme |= CONFIG.iconTheme != iconTheme
|
||||
needsRestart |= CONFIG.guiLocale != guiLocale
|
||||
needsRestart |= CONFIG.guiFont != self._guiFont
|
||||
refreshTree |= CONFIG.useCharCount != useCharCount
|
||||
updateSyntax |= CONFIG.lightTheme != lightTheme
|
||||
updateSyntax |= CONFIG.darkTheme != darkTheme
|
||||
|
||||
CONFIG.guiLocale = guiLocale
|
||||
CONFIG.guiTheme = guiTheme
|
||||
CONFIG.lightTheme = lightTheme
|
||||
CONFIG.darkTheme = darkTheme
|
||||
CONFIG.iconTheme = iconTheme
|
||||
CONFIG.hideVScroll = self.hideVScroll.isChecked()
|
||||
CONFIG.hideHScroll = self.hideHScroll.isChecked()
|
||||
@@ -986,11 +989,6 @@ class GuiPreferences(NDialog):
|
||||
CONFIG.setGuiFont(self._guiFont)
|
||||
|
||||
# Document Style
|
||||
guiSyntax = self.guiSyntax.currentData()
|
||||
|
||||
updateSyntax |= CONFIG.guiSyntax != guiSyntax
|
||||
|
||||
CONFIG.guiSyntax = guiSyntax
|
||||
CONFIG.showFullPath = self.showFullPath.isChecked()
|
||||
CONFIG.incNotesWCount = self.incNotesWCount.isChecked()
|
||||
CONFIG.setTextFont(self._textFont)
|
||||
|
||||
@@ -156,6 +156,13 @@ class nwFocus(Enum):
|
||||
OUTLINE = 3
|
||||
|
||||
|
||||
class nwTheme(Enum):
|
||||
|
||||
AUTO = 0
|
||||
LIGHT = 1
|
||||
DARK = 2
|
||||
|
||||
|
||||
class nwOutline(Enum):
|
||||
|
||||
TITLE = 0
|
||||
|
||||
+77
-111
@@ -31,16 +31,16 @@ from typing import TYPE_CHECKING, Final
|
||||
|
||||
from PyQt6.QtCore import QSize, Qt
|
||||
from PyQt6.QtGui import (
|
||||
QColor, QFont, QFontDatabase, QFontMetrics, QIcon, QPainter, QPainterPath,
|
||||
QPalette, QPixmap
|
||||
QColor, QFont, QFontDatabase, QFontMetrics, QGuiApplication, QIcon,
|
||||
QPainter, QPainterPath, QPalette, QPixmap
|
||||
)
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
|
||||
from novelwriter import CONFIG
|
||||
from novelwriter.common import NWConfigParser, minmax
|
||||
from novelwriter.config import DEF_GUI, DEF_ICONS, DEF_SYNTAX
|
||||
from novelwriter.config import DEF_GUI_DARK, DEF_GUI_LIGHT, DEF_ICONS
|
||||
from novelwriter.constants import nwLabels
|
||||
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
||||
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType, nwTheme
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.types import QtBlack, QtHexArgb, QtPaintAntiAlias, QtTransparent
|
||||
|
||||
@@ -49,6 +49,8 @@ if TYPE_CHECKING:
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
T_ThemeEntry = tuple[str, str, bool]
|
||||
|
||||
STYLES_FLAT_TABS = "flatTabWidget"
|
||||
STYLES_MIN_TOOLBUTTON = "minimalToolButton"
|
||||
STYLES_BIG_TOOLBUTTON = "bigToolButton"
|
||||
@@ -57,6 +59,7 @@ STYLES_BIG_TOOLBUTTON = "bigToolButton"
|
||||
class ThemeMeta:
|
||||
|
||||
name: str = ""
|
||||
mode: str = ""
|
||||
description: str = ""
|
||||
author: str = ""
|
||||
credit: str = ""
|
||||
@@ -96,13 +99,13 @@ class GuiTheme:
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"_availSyntax", "_availThemes", "_guiPalette", "_styleSheets", "_syntaxList", "_themeList",
|
||||
"baseButtonHeight", "baseIconHeight", "baseIconSize", "buttonIconSize", "errorText",
|
||||
"fadedText", "fontPixelSize", "fontPointSize", "getDecoration", "getHeaderDecoration",
|
||||
"getHeaderDecorationNarrow", "getIcon", "getIconColor", "getItemIcon", "getPixmap",
|
||||
"getToggleIcon", "guiFont", "guiFontB", "guiFontBU", "guiFontFixed", "guiFontSmall",
|
||||
"helpText", "iconCache", "isDarkTheme", "syntaxMeta", "syntaxTheme", "textNHeight",
|
||||
"textNWidth", "themeMeta",
|
||||
"_availSyntax", "_availThemes", "_darkThemes", "_guiPalette", "_lightThemes",
|
||||
"_styleSheets", "_syntaxList", "_themeList", "baseButtonHeight", "baseIconHeight",
|
||||
"baseIconSize", "buttonIconSize", "errorText", "fadedText", "fontPixelSize",
|
||||
"fontPointSize", "getDecoration", "getHeaderDecoration", "getHeaderDecorationNarrow",
|
||||
"getIcon", "getIconColor", "getItemIcon", "getPixmap", "getToggleIcon", "guiFont",
|
||||
"guiFontB", "guiFontBU", "guiFontFixed", "guiFontSmall", "helpText", "iconCache",
|
||||
"isDarkTheme", "syntaxMeta", "syntaxTheme", "textNHeight", "textNWidth", "themeMeta",
|
||||
)
|
||||
|
||||
def __init__(self) -> None:
|
||||
@@ -124,8 +127,7 @@ class GuiTheme:
|
||||
|
||||
# Load Themes
|
||||
self._guiPalette = QPalette()
|
||||
self._themeList: list[tuple[str, str]] = []
|
||||
self._syntaxList: list[tuple[str, str]] = []
|
||||
self._themeList: list[T_ThemeEntry] = []
|
||||
self._availThemes: dict[str, Path] = {}
|
||||
self._availSyntax: dict[str, Path] = {}
|
||||
self._styleSheets: dict[str, str] = {}
|
||||
@@ -179,13 +181,10 @@ class GuiTheme:
|
||||
logger.debug("Text 'N' Width: %d", self.textNWidth)
|
||||
|
||||
# Process Themes
|
||||
_listConf(self._availSyntax, CONFIG.assetPath("syntax"), ".conf")
|
||||
_listConf(self._availThemes, CONFIG.assetPath("themes"), ".conf")
|
||||
_listConf(self._availSyntax, CONFIG.dataPath("syntax"), ".conf")
|
||||
_listConf(self._availThemes, CONFIG.dataPath("themes"), ".conf")
|
||||
|
||||
self.loadTheme()
|
||||
self.loadSyntax()
|
||||
|
||||
return
|
||||
|
||||
@@ -207,13 +206,35 @@ class GuiTheme:
|
||||
# Theme Methods
|
||||
##
|
||||
|
||||
def isDesktopDarkMode(self) -> bool:
|
||||
"""Check if the desktop is in dark mode."""
|
||||
if CONFIG.verQtValue >= 0x060500 and (hint := QGuiApplication.styleHints()):
|
||||
return hint.colorScheme() == Qt.ColorScheme.Dark
|
||||
|
||||
palette = QPalette()
|
||||
text = palette.color(QPalette.ColorRole.WindowText)
|
||||
window = palette.color(QPalette.ColorRole.Window)
|
||||
return text.lightnessF() > window.lightnessF()
|
||||
|
||||
def loadTheme(self) -> bool:
|
||||
"""Load the currently specified GUI theme."""
|
||||
theme = CONFIG.guiTheme
|
||||
match CONFIG.themeMode:
|
||||
case nwTheme.LIGHT:
|
||||
darkMode = False
|
||||
case nwTheme.DARK:
|
||||
darkMode = True
|
||||
case _:
|
||||
darkMode = self.isDesktopDarkMode()
|
||||
|
||||
theme = CONFIG.darkTheme if darkMode else CONFIG.lightTheme
|
||||
if theme not in self._availThemes:
|
||||
logger.error("Could not find GUI theme '%s'", theme)
|
||||
theme = DEF_GUI
|
||||
CONFIG.guiTheme = theme
|
||||
if darkMode:
|
||||
theme = DEF_GUI_DARK
|
||||
CONFIG.darkTheme = DEF_GUI_DARK
|
||||
else:
|
||||
theme = DEF_GUI_LIGHT
|
||||
CONFIG.lightTheme = DEF_GUI_LIGHT
|
||||
|
||||
if not (file := self._availThemes.get(theme)):
|
||||
logger.error("Could not load GUI theme")
|
||||
@@ -238,6 +259,7 @@ class GuiTheme:
|
||||
meta = ThemeMeta()
|
||||
if parser.has_section(sec):
|
||||
meta.name = parser.rdStr(sec, "name", "")
|
||||
meta.mode = parser.rdStr(sec, "mode", "light")
|
||||
meta.description = parser.rdStr(sec, "description", "N/A")
|
||||
meta.author = parser.rdStr(sec, "author", "N/A")
|
||||
meta.credit = parser.rdStr(sec, "credit", "N/A")
|
||||
@@ -296,6 +318,31 @@ class GuiTheme:
|
||||
self.fadedText = self._parseColor(parser, sec, "fadedtext")
|
||||
self.errorText = self._parseColor(parser, sec, "errortext")
|
||||
|
||||
# Syntax
|
||||
sec = "Syntax"
|
||||
self.syntaxTheme = SyntaxColors()
|
||||
if parser.has_section(sec):
|
||||
self.syntaxTheme.back = self._parseColor(parser, sec, "background")
|
||||
self.syntaxTheme.text = self._parseColor(parser, sec, "text")
|
||||
self.syntaxTheme.link = self._parseColor(parser, sec, "link")
|
||||
self.syntaxTheme.head = self._parseColor(parser, sec, "headertext")
|
||||
self.syntaxTheme.headH = self._parseColor(parser, sec, "headertag")
|
||||
self.syntaxTheme.emph = self._parseColor(parser, sec, "emphasis")
|
||||
self.syntaxTheme.dialN = self._parseColor(parser, sec, "dialog")
|
||||
self.syntaxTheme.dialA = self._parseColor(parser, sec, "altdialog")
|
||||
self.syntaxTheme.hidden = self._parseColor(parser, sec, "hidden")
|
||||
self.syntaxTheme.note = self._parseColor(parser, sec, "note")
|
||||
self.syntaxTheme.code = self._parseColor(parser, sec, "shortcode")
|
||||
self.syntaxTheme.key = self._parseColor(parser, sec, "keyword")
|
||||
self.syntaxTheme.tag = self._parseColor(parser, sec, "tag")
|
||||
self.syntaxTheme.val = self._parseColor(parser, sec, "value")
|
||||
self.syntaxTheme.opt = self._parseColor(parser, sec, "optional")
|
||||
self.syntaxTheme.spell = self._parseColor(parser, sec, "spellcheckline")
|
||||
self.syntaxTheme.error = self._parseColor(parser, sec, "errorline")
|
||||
self.syntaxTheme.repTag = self._parseColor(parser, sec, "replacetag")
|
||||
self.syntaxTheme.mod = self._parseColor(parser, sec, "modifier")
|
||||
self.syntaxTheme.mark = self._parseColor(parser, sec, "texthighlight")
|
||||
|
||||
# Update Dependant Colours
|
||||
# Based on: https://github.com/qt/qtbase/blob/dev/src/gui/kernel/qplatformtheme.cpp
|
||||
text = self._guiPalette.text().color()
|
||||
@@ -363,105 +410,22 @@ class GuiTheme:
|
||||
|
||||
return True
|
||||
|
||||
def loadSyntax(self) -> bool:
|
||||
"""Load the currently specified syntax highlighter theme."""
|
||||
theme = CONFIG.guiSyntax
|
||||
if theme not in self._availSyntax:
|
||||
logger.error("Could not find syntax theme '%s'", theme)
|
||||
theme = DEF_SYNTAX
|
||||
CONFIG.guiSyntax = theme
|
||||
|
||||
if not (file := self._availSyntax.get(theme)):
|
||||
logger.error("Could not load syntax theme")
|
||||
return False
|
||||
|
||||
CONFIG.splashMessage("Loading syntax theme ...")
|
||||
logger.info("Loading syntax theme '%s'", theme)
|
||||
parser = NWConfigParser()
|
||||
try:
|
||||
with open(file, mode="r", encoding="utf-8") as fo:
|
||||
parser.read_file(fo)
|
||||
except Exception:
|
||||
logger.error("Could not read file: %s", file)
|
||||
logException()
|
||||
return False
|
||||
|
||||
# Main
|
||||
sec = "Main"
|
||||
meta = ThemeMeta()
|
||||
if parser.has_section(sec):
|
||||
meta.name = parser.rdStr(sec, "name", "")
|
||||
meta.description = parser.rdStr(sec, "description", "N/A")
|
||||
meta.author = parser.rdStr(sec, "author", "N/A")
|
||||
meta.credit = parser.rdStr(sec, "credit", "N/A")
|
||||
meta.url = parser.rdStr(sec, "url", "")
|
||||
meta.license = parser.rdStr(sec, "license", "N/A")
|
||||
meta.licenseUrl = parser.rdStr(sec, "licenseurl", "")
|
||||
|
||||
# Syntax
|
||||
sec = "Syntax"
|
||||
syntax = SyntaxColors()
|
||||
if parser.has_section(sec):
|
||||
syntax.back = self._parseColor(parser, sec, "background")
|
||||
syntax.text = self._parseColor(parser, sec, "text")
|
||||
syntax.link = self._parseColor(parser, sec, "link")
|
||||
syntax.head = self._parseColor(parser, sec, "headertext")
|
||||
syntax.headH = self._parseColor(parser, sec, "headertag")
|
||||
syntax.emph = self._parseColor(parser, sec, "emphasis")
|
||||
syntax.dialN = self._parseColor(parser, sec, "dialog")
|
||||
syntax.dialA = self._parseColor(parser, sec, "altdialog")
|
||||
syntax.hidden = self._parseColor(parser, sec, "hidden")
|
||||
syntax.note = self._parseColor(parser, sec, "note")
|
||||
syntax.code = self._parseColor(parser, sec, "shortcode")
|
||||
syntax.key = self._parseColor(parser, sec, "keyword")
|
||||
syntax.tag = self._parseColor(parser, sec, "tag")
|
||||
syntax.val = self._parseColor(parser, sec, "value")
|
||||
syntax.opt = self._parseColor(parser, sec, "optional")
|
||||
syntax.spell = self._parseColor(parser, sec, "spellcheckline")
|
||||
syntax.error = self._parseColor(parser, sec, "errorline")
|
||||
syntax.repTag = self._parseColor(parser, sec, "replacetag")
|
||||
syntax.mod = self._parseColor(parser, sec, "modifier")
|
||||
syntax.mark = self._parseColor(parser, sec, "texthighlight")
|
||||
|
||||
CONFIG.splashMessage(f"Loaded syntax theme: {meta.name}")
|
||||
|
||||
self.syntaxMeta = meta
|
||||
self.syntaxTheme = syntax
|
||||
|
||||
return True
|
||||
|
||||
def listThemes(self) -> list[tuple[str, str]]:
|
||||
def listThemes(self) -> list[T_ThemeEntry]:
|
||||
"""Scan the GUI themes folder and list all themes."""
|
||||
if self._themeList:
|
||||
return self._themeList
|
||||
|
||||
themes = []
|
||||
themes: list[T_ThemeEntry] = []
|
||||
parser = NWConfigParser()
|
||||
for key, path in self._availThemes.items():
|
||||
logger.debug("Checking theme config '%s'", key)
|
||||
if name := _loadInternalName(parser, path):
|
||||
themes.append((key, name))
|
||||
if meta := _loadInternalName(parser, path):
|
||||
themes.append((key, meta[0], meta[1]))
|
||||
|
||||
self._themeList = sorted(themes, key=_sortTheme)
|
||||
|
||||
return self._themeList
|
||||
|
||||
def listSyntax(self) -> list[tuple[str, str]]:
|
||||
"""Scan the syntax themes folder and list all themes."""
|
||||
if self._syntaxList:
|
||||
return self._syntaxList
|
||||
|
||||
themes = []
|
||||
parser = NWConfigParser()
|
||||
for key, path in self._availSyntax.items():
|
||||
logger.debug("Checking theme syntax '%s'", key)
|
||||
if name := _loadInternalName(parser, path):
|
||||
themes.append((key, name))
|
||||
|
||||
self._syntaxList = sorted(themes, key=_sortTheme)
|
||||
|
||||
return self._syntaxList
|
||||
|
||||
def getStyleSheet(self, name: str) -> str:
|
||||
"""Load a standard style sheet."""
|
||||
return self._styleSheets.get(name, "")
|
||||
@@ -889,22 +853,24 @@ def _listConf(target: dict, path: Path, extension: str) -> None:
|
||||
return
|
||||
|
||||
|
||||
def _sortTheme(data: tuple[str, str]) -> str:
|
||||
def _sortTheme(data: tuple) -> str:
|
||||
"""Key function for theme sorting."""
|
||||
key, name = data
|
||||
key, name = data[:2]
|
||||
return f"*{name}" if key.startswith("default_") else name
|
||||
|
||||
|
||||
def _loadInternalName(parser: NWConfigParser, path: str | Path) -> str:
|
||||
def _loadInternalName(parser: NWConfigParser, path: str | Path) -> tuple[str, bool]:
|
||||
"""Open a conf file and read the 'name' setting."""
|
||||
try:
|
||||
with open(path, mode="r", encoding="utf-8") as inFile:
|
||||
parser.read_file(inFile)
|
||||
return parser.rdStr("Main", "name", "")
|
||||
name = parser.rdStr("Main", "name", "")
|
||||
dark = parser.rdStr("Main", "mode", "light").lower() == "dark"
|
||||
return name, dark
|
||||
except Exception:
|
||||
logger.error("Could not read file: %s", path)
|
||||
logException()
|
||||
return ""
|
||||
return "", False
|
||||
|
||||
|
||||
def _loadIconName(path: Path) -> str:
|
||||
|
||||
@@ -1069,7 +1069,6 @@ class GuiMain(QMainWindow):
|
||||
SHARED.project.tree.refreshAllItems()
|
||||
|
||||
if syntax:
|
||||
SHARED.theme.loadSyntax()
|
||||
self.docEditor.updateSyntaxColors()
|
||||
|
||||
self.docEditor.initEditor()
|
||||
|
||||
Reference in New Issue
Block a user