Scan themes on startup instead

This commit is contained in:
Veronica Berglyd Olsen
2025-06-03 17:31:52 +02:00
parent df7a721626
commit 638f93499b
8 changed files with 168 additions and 150 deletions
+1 -1
View File
@@ -49,7 +49,7 @@ __license__ = "GPLv3"
__author__ = "Veronica Berglyd Olsen" __author__ = "Veronica Berglyd Olsen"
__maintainer__ = "Veronica Berglyd Olsen" __maintainer__ = "Veronica Berglyd Olsen"
__email__ = "code@vkbo.net" __email__ = "code@vkbo.net"
__version__ = "2.8 Alpha 0" __version__ = "2.8a0"
__hexversion__ = "0x020800a0" __hexversion__ = "0x020800a0"
__date__ = "2025-06-01" __date__ = "2025-06-01"
__status__ = "Stable" __status__ = "Stable"
+2 -2
View File
@@ -405,7 +405,7 @@ class Config:
else: else:
font = QFontDatabase.systemFont(QFontDatabase.SystemFont.GeneralFont) font = QFontDatabase.systemFont(QFontDatabase.SystemFont.GeneralFont)
self.guiFont = fontMatcher(font) self.guiFont = fontMatcher(font)
logger.debug("GUI font set to: %s", describeFont(font)) logger.debug("Main font set to: %s", describeFont(font))
QApplication.setFont(self.guiFont) QApplication.setFont(self.guiFont)
return return
@@ -723,7 +723,7 @@ class Config:
# Check Values # Check Values
# ============ # ============
self._prepareFont(self.guiFont, "GUI") self._prepareFont(self.guiFont, "main")
self._prepareFont(self.textFont, "document") self._prepareFont(self.textFont, "document")
# If we're using straight quotes, disable auto-replace # If we're using straight quotes, disable auto-replace
+8 -6
View File
@@ -169,11 +169,12 @@ class GuiPreferences(NDialog):
self.lightTheme.setMinimumWidth(200) self.lightTheme.setMinimumWidth(200)
self.darkTheme = NComboBox(self) self.darkTheme = NComboBox(self)
self.darkTheme.setMinimumWidth(200) self.darkTheme.setMinimumWidth(200)
for theme, name, dark in SHARED.theme.listThemes(): for key, theme in SHARED.theme.colourThemes.items():
if dark: if theme.dark:
self.darkTheme.addItem(name, theme) self.darkTheme.addItem(theme.name, key)
else: else:
self.lightTheme.addItem(name, theme) self.lightTheme.addItem(theme.name, key)
self.lightTheme.setCurrentData(CONFIG.lightTheme, DEF_GUI_LIGHT) self.lightTheme.setCurrentData(CONFIG.lightTheme, DEF_GUI_LIGHT)
self.darkTheme.setCurrentData(CONFIG.darkTheme, DEF_GUI_DARK) self.darkTheme.setCurrentData(CONFIG.darkTheme, DEF_GUI_DARK)
@@ -189,8 +190,9 @@ class GuiPreferences(NDialog):
# Icon Theme # Icon Theme
self.iconTheme = NComboBox(self) self.iconTheme = NComboBox(self)
self.iconTheme.setMinimumWidth(200) self.iconTheme.setMinimumWidth(200)
for theme, name in SHARED.theme.iconCache.listThemes(): for key, theme in SHARED.theme.iconCache.iconThemes.items():
self.iconTheme.addItem(name, theme) self.iconTheme.addItem(theme.name, key)
self.iconTheme.setCurrentData(CONFIG.iconTheme, DEF_ICONS) self.iconTheme.setCurrentData(CONFIG.iconTheme, DEF_ICONS)
self.mainForm.addRow( self.mainForm.addRow(
+142 -125
View File
@@ -27,6 +27,7 @@ from __future__ import annotations
import logging import logging
from configparser import ConfigParser from configparser import ConfigParser
from dataclasses import dataclass
from math import ceil from math import ceil
from typing import TYPE_CHECKING, Final from typing import TYPE_CHECKING, Final
@@ -50,13 +51,19 @@ if TYPE_CHECKING:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
T_ThemeEntry = tuple[str, str, bool]
STYLES_FLAT_TABS = "flatTabWidget" STYLES_FLAT_TABS = "flatTabWidget"
STYLES_MIN_TOOLBUTTON = "minimalToolButton" STYLES_MIN_TOOLBUTTON = "minimalToolButton"
STYLES_BIG_TOOLBUTTON = "bigToolButton" STYLES_BIG_TOOLBUTTON = "bigToolButton"
@dataclass
class ThemeEntry:
name: str
dark: bool
path: Path
class ThemeMeta: class ThemeMeta:
name: str = "" name: str = ""
@@ -100,20 +107,18 @@ class GuiTheme:
""" """
__slots__ = ( __slots__ = (
"_availSyntax", "_availThemes", "_currentTheme", "_darkThemes", "_guiPalette", "_allThemes", "_currentTheme", "_darkThemes", "_guiPalette", "_lightThemes", "_meta",
"_lightThemes", "_qColors", "_styleSheets", "_svgColors", "_syntaxList", "_themeList", "_qColors", "_styleSheets", "_svgColors", "_syntaxList", "baseButtonHeight",
"baseButtonHeight", "baseIconHeight", "baseIconSize", "buttonIconSize", "errorText", "baseIconHeight", "baseIconSize", "buttonIconSize", "errorText", "fadedText",
"fadedText", "fontPixelSize", "fontPointSize", "getDecoration", "getHeaderDecoration", "fontPixelSize", "fontPointSize", "getDecoration", "getHeaderDecoration",
"getHeaderDecorationNarrow", "getIcon", "getItemIcon", "getPixmap", "getToggleIcon", "getHeaderDecorationNarrow", "getIcon", "getItemIcon", "getPixmap", "getToggleIcon",
"guiFont", "guiFontB", "guiFontBU", "guiFontFixed", "guiFontSmall", "helpText", "guiFont", "guiFontB", "guiFontBU", "guiFontFixed", "guiFontSmall", "helpText",
"iconCache", "isDarkTheme", "syntaxTheme", "textNHeight", "textNWidth", "iconCache", "isDarkTheme", "syntaxTheme", "textNHeight", "textNWidth",
"themeMeta",
) )
def __init__(self) -> None: def __init__(self) -> None:
# Theme Objects # Theme Objects
self.themeMeta = ThemeMeta()
self.iconCache = GuiIcons(self) self.iconCache = GuiIcons(self)
self.syntaxTheme = SyntaxColors() self.syntaxTheme = SyntaxColors()
self.isDarkTheme = False self.isDarkTheme = False
@@ -123,12 +128,11 @@ class GuiTheme:
self.fadedText = QColor(0, 0, 0) self.fadedText = QColor(0, 0, 0)
self.errorText = QColor(255, 0, 0) self.errorText = QColor(255, 0, 0)
# Load Themes # Theme Data
self._meta = ThemeMeta()
self._currentTheme = "" self._currentTheme = ""
self._guiPalette = QPalette() self._guiPalette = QPalette()
self._themeList: list[T_ThemeEntry] = [] self._allThemes: dict[str, ThemeEntry] = {}
self._availThemes: dict[str, Path] = {}
self._availSyntax: dict[str, Path] = {}
self._styleSheets: dict[str, str] = {} self._styleSheets: dict[str, str] = {}
self._svgColors: dict[str, bytes] = {} self._svgColors: dict[str, bytes] = {}
self._qColors: dict[str, QColor] = {} self._qColors: dict[str, QColor] = {}
@@ -180,16 +184,19 @@ class GuiTheme:
logger.debug("Text 'N' Height: %d", self.textNHeight) logger.debug("Text 'N' Height: %d", self.textNHeight)
logger.debug("Text 'N' Width: %d", self.textNWidth) logger.debug("Text 'N' Width: %d", self.textNWidth)
# Process Themes
_listConf(self._availThemes, CONFIG.assetPath("themes"), ".conf")
_listConf(self._availThemes, CONFIG.dataPath("themes"), ".conf")
self.loadTheme()
return return
## ##
# Methods # Properties
##
@property
def colourThemes(self) -> dict[str, ThemeEntry]:
"""Return a dictionary of all themes."""
return self._allThemes
##
# Getters
## ##
def getTextWidth(self, text: str, font: QFont | None = None) -> int: def getTextWidth(self, text: str, font: QFont | None = None) -> int:
@@ -214,6 +221,19 @@ class GuiTheme:
# Theme Methods # Theme Methods
## ##
def initThemes(self) -> None:
"""Initialise themes."""
CONFIG.splashMessage("Scanning for colour themes ...")
themes: list[Path] = []
_listContent(themes, CONFIG.assetPath("themes"), ".conf")
_listContent(themes, CONFIG.dataPath("themes"), ".conf")
self._scanThemes(themes)
self.iconCache.initIcons()
self.loadTheme()
return
def isDesktopDarkMode(self) -> bool: def isDesktopDarkMode(self) -> bool:
"""Check if the desktop is in dark mode.""" """Check if the desktop is in dark mode."""
if CONFIG.verQtValue >= 0x060500 and (hint := QGuiApplication.styleHints()): if CONFIG.verQtValue >= 0x060500 and (hint := QGuiApplication.styleHints()):
@@ -261,8 +281,8 @@ class GuiTheme:
darkMode = self.isDesktopDarkMode() darkMode = self.isDesktopDarkMode()
theme = CONFIG.darkTheme if darkMode else CONFIG.lightTheme theme = CONFIG.darkTheme if darkMode else CONFIG.lightTheme
if theme not in self._availThemes: if theme not in self._allThemes:
logger.error("Could not find GUI theme '%s'", theme) logger.error("Could not find theme for key '%s'", theme)
if darkMode: if darkMode:
theme = DEF_GUI_DARK theme = DEF_GUI_DARK
CONFIG.darkTheme = DEF_GUI_DARK CONFIG.darkTheme = DEF_GUI_DARK
@@ -274,18 +294,19 @@ class GuiTheme:
logger.info("Theme '%s' is already loaded", theme) logger.info("Theme '%s' is already loaded", theme)
return False return False
if not (file := self._availThemes.get(theme)): entry = self._allThemes.get(theme)
if not entry:
logger.error("Could not load GUI theme") logger.error("Could not load GUI theme")
return False return False
CONFIG.splashMessage("Loading GUI theme ...") CONFIG.splashMessage(f"Loading colour theme: {entry.name}")
logger.info("Loading GUI theme '%s'", theme) logger.info("Loading GUI theme '%s'", theme)
parser = ConfigParser() parser = ConfigParser()
try: try:
with open(file, mode="r", encoding="utf-8") as fo: with open(entry.path, mode="r", encoding="utf-8") as fo:
parser.read_file(fo) parser.read_file(fo)
except Exception: except Exception:
logger.error("Could not read file: %s", file) logger.error("Could not read file: %s", entry.path)
logException() logException()
return False return False
@@ -305,9 +326,9 @@ class GuiTheme:
meta.license = parser.get(sec, "license", fallback="N/A") meta.license = parser.get(sec, "license", fallback="N/A")
meta.licenseUrl = parser.get(sec, "licenseurl", fallback="") meta.licenseUrl = parser.get(sec, "licenseurl", fallback="")
self.themeMeta = meta self._meta = meta
# Icons # Base
sec = "Base" sec = "Base"
if parser.has_section(sec): if parser.has_section(sec):
self._setBaseColor("default", self._readColor(parser, sec, "default")) self._setBaseColor("default", self._readColor(parser, sec, "default"))
@@ -447,35 +468,18 @@ class GuiTheme:
self._svgColors["scene"] = color self._svgColors["scene"] = color
self._svgColors["note"] = color self._svgColors["note"] = color
self.isDarkTheme = darkMode
self._currentTheme = theme
# Load icons after the theme is parsed # Load icons after the theme is parsed
self.iconCache.loadTheme(CONFIG.iconTheme) self.iconCache.loadTheme(CONFIG.iconTheme)
# Finalise # Finalise
self.isDarkTheme = darkMode
QApplication.setPalette(self._guiPalette) QApplication.setPalette(self._guiPalette)
self._buildStyleSheets(self._guiPalette) self._buildStyleSheets(self._guiPalette)
self._currentTheme = theme
CONFIG.splashMessage(f"Loaded GUI theme: {meta.name}")
return True return True
def listThemes(self) -> list[T_ThemeEntry]:
"""Scan the GUI themes folder and list all themes."""
if self._themeList:
return self._themeList
themes: list[T_ThemeEntry] = []
parser = ConfigParser()
for key, path in self._availThemes.items():
logger.debug("Checking theme config '%s'", key)
if meta := _loadInternalName(parser, path):
themes.append((key, meta[0], meta[1]))
self._themeList = sorted(themes, key=_sortTheme)
return self._themeList
def getStyleSheet(self, name: str) -> str: def getStyleSheet(self, name: str) -> str:
"""Load a standard style sheet.""" """Load a standard style sheet."""
return self._styleSheets.get(name, "") return self._styleSheets.get(name, "")
@@ -493,12 +497,10 @@ class GuiTheme:
def _resetTheme(self) -> None: def _resetTheme(self) -> None:
"""Reset GUI colours to default values.""" """Reset GUI colours to default values."""
palette = QPalette() palette = QPalette()
isDark = self.isDesktopDarkMode()
text = palette.color(QPalette.ColorRole.Text)
window = palette.color(QPalette.ColorRole.Window)
isDark = text.lightnessF() > window.lightnessF()
# Reset GUI Palette # Reset GUI Palette
default = palette.color(QPalette.ColorRole.Text)
faded = QColor(128, 128, 128) faded = QColor(128, 128, 128)
dimmed = QColor(130, 130, 130) if isDark else QColor(190, 190, 190) dimmed = QColor(130, 130, 130) if isDark else QColor(190, 190, 190)
red = QColor(242, 119, 122) if isDark else QColor(240, 40, 41) red = QColor(242, 119, 122) if isDark else QColor(240, 40, 41)
@@ -520,7 +522,7 @@ class GuiTheme:
self.iconCache.clear() self.iconCache.clear()
self._svgColors = {} self._svgColors = {}
self._qColors = {} self._qColors = {}
self._setBaseColor("default", text) self._setBaseColor("default", default)
self._setBaseColor("faded", faded) self._setBaseColor("faded", faded)
self._setBaseColor("red", red) self._setBaseColor("red", red)
self._setBaseColor("orange", orange) self._setBaseColor("orange", orange)
@@ -531,7 +533,7 @@ class GuiTheme:
self._setBaseColor("purple", purple) self._setBaseColor("purple", purple)
self._setBaseColor("root", blue) self._setBaseColor("root", blue)
self._setBaseColor("folder", yellow) self._setBaseColor("folder", yellow)
self._setBaseColor("file", text) self._setBaseColor("file", default)
self._setBaseColor("title", green) self._setBaseColor("title", green)
self._setBaseColor("chapter", red) self._setBaseColor("chapter", red)
self._setBaseColor("scene", blue) self._setBaseColor("scene", blue)
@@ -582,6 +584,35 @@ class GuiTheme:
return return
def _scanThemes(self, files: list[Path]) -> None:
"""Scan the GUI themes folder and list all themes."""
parser = ConfigParser()
data: dict[str, tuple[str, str, bool, Path]] = {}
keys = []
for file in files:
try:
parser.clear()
parser.read(file, encoding="utf-8")
name = parser.get("Main", "name", fallback="")
dark = parser.get("Main", "mode", fallback="light").lower() == "dark"
if name:
key = file.stem
prefix = "*" if key.startswith("default") else ""
lookup = f"{prefix}{name} {key}"
keys.append(lookup)
data[lookup] = (file.stem, name, dark, file)
except Exception: # noqa: PERF203
logger.error("Could not read file: %s", file)
logException()
self._allThemes = {}
for lookup in sorted(keys):
key, name, dark, item = data[lookup]
logger.debug("Checking theme config '%s'", key)
self._allThemes[key] = ThemeEntry(name, dark, item)
return
class GuiIcons: class GuiIcons:
"""The icon class manages the content of the assets/icons folder, """The icon class manages the content of the assets/icons folder,
@@ -593,8 +624,8 @@ class GuiIcons:
""" """
__slots__ = ( __slots__ = (
"_availThemes", "_headerDec", "_headerDecNarrow", "_meta", "_noIcon", "_allThemes", "_headerDec", "_headerDecNarrow", "_meta",
"_qIcons", "_svgData", "_theme", "_themeList", "_noIcon", "_qIcons", "_svgData", "_theme",
) )
TOGGLE_ICON_KEYS: Final[dict[str, tuple[str, str]]] = { TOGGLE_ICON_KEYS: Final[dict[str, tuple[str, str]]] = {
@@ -612,21 +643,15 @@ class GuiIcons:
self._meta = ThemeMeta() self._meta = ThemeMeta()
# Storage # Storage
self._allThemes: dict[str, ThemeEntry] = {}
self._svgData: dict[str, bytes] = {} self._svgData: dict[str, bytes] = {}
self._qIcons: dict[str, QIcon] = {} self._qIcons: dict[str, QIcon] = {}
self._headerDec: list[QPixmap] = [] self._headerDec: list[QPixmap] = []
self._headerDecNarrow: list[QPixmap] = [] self._headerDecNarrow: list[QPixmap] = []
# Icon Theme Path
self._availThemes: dict[str, Path] = {}
self._themeList: list[tuple[str, str]] = []
# None Icon # None Icon
self._noIcon = QIcon(str(CONFIG.assetPath("icons") / "none.svg")) self._noIcon = QIcon(str(CONFIG.assetPath("icons") / "none.svg"))
_listConf(self._availThemes, CONFIG.assetPath("icons"), ".icons")
_listConf(self._availThemes, CONFIG.dataPath("icons"), ".icons")
return return
def clear(self) -> None: def clear(self) -> None:
@@ -638,29 +663,48 @@ class GuiIcons:
self._meta = ThemeMeta() self._meta = ThemeMeta()
return return
##
# Properties
##
@property
def iconThemes(self) -> dict[str, ThemeEntry]:
"""Return a dictionary of all icon themes."""
return self._allThemes
## ##
# Actions # Actions
## ##
def initIcons(self) -> None:
"""Initialise icons."""
CONFIG.splashMessage("Scanning for icon themes ...")
icons: list[Path] = []
_listContent(icons, CONFIG.assetPath("icons"), ".icons")
_listContent(icons, CONFIG.dataPath("icons"), ".icons")
self._scanThemes(icons)
return
def loadTheme(self, theme: str) -> bool: def loadTheme(self, theme: str) -> bool:
"""Update the theme map. This is more of an init, since many of """Update the theme map. This is more of an init, since many of
the GUI icons cannot really be replaced without writing specific the GUI icons cannot really be replaced without writing specific
update functions for the classes where they're used. update functions for the classes where they're used.
""" """
if theme not in self._availThemes: if theme not in self._allThemes:
logger.error("Could not find icon theme '%s'", theme) logger.error("Could not find icon theme '%s'", theme)
theme = DEF_ICONS theme = DEF_ICONS
CONFIG.iconTheme = theme CONFIG.iconTheme = theme
if not (file := self._availThemes.get(theme)): entry = self._allThemes.get(theme)
if not entry:
logger.error("Could not load icon theme") logger.error("Could not load icon theme")
return False return False
CONFIG.splashMessage("Loading icon theme ...") CONFIG.splashMessage(f"Loading icon theme: {entry.name}")
logger.info("Loading icon theme '%s'", theme) logger.info("Loading icon theme '%s'", theme)
try: try:
meta = ThemeMeta() meta = ThemeMeta()
with open(file, mode="r", encoding="utf-8") as icons: with open(entry.path, mode="r", encoding="utf-8") as icons:
for icon in icons: for icon in icons:
bits = icon.partition("=") bits = icon.partition("=")
key = bits[0].strip() key = bits[0].strip()
@@ -676,14 +720,12 @@ class GuiIcons:
meta.license = value meta.license = value
self._meta = meta self._meta = meta
except Exception: except Exception:
logger.error("Could not read file: %s", file) logger.error("Could not read file: %s", entry.path)
logException() logException()
return False return False
CONFIG.splashMessage(f"Loaded icon theme: {meta.name}")
CONFIG.splashMessage("Generating additional icons ...")
# Populate generated icons cache # Populate generated icons cache
CONFIG.splashMessage("Generating additional icons ...")
self.getHeaderDecoration(0) self.getHeaderDecoration(0)
self.getHeaderDecorationNarrow(0) self.getHeaderDecorationNarrow(0)
@@ -811,21 +853,6 @@ class GuiIcons:
] ]
return self._headerDecNarrow[minmax(hLevel, 0, 5)] return self._headerDecNarrow[minmax(hLevel, 0, 5)]
def listThemes(self) -> list[tuple[str, str]]:
"""Scan the GUI icons folder and list all themes."""
if self._themeList:
return self._themeList
themes = []
for key, path in self._availThemes.items():
logger.debug("Checking icon theme '%s'", key)
if name := _loadIconName(path):
themes.append((key, name))
self._themeList = sorted(themes, key=_sortTheme)
return self._themeList
## ##
# Internal Functions # Internal Functions
## ##
@@ -870,49 +897,39 @@ class GuiIcons:
tMode = Qt.TransformationMode.SmoothTransformation tMode = Qt.TransformationMode.SmoothTransformation
return pixmap.scaledToHeight(height, tMode) return pixmap.scaledToHeight(height, tMode)
def _scanThemes(self, entries: list[Path]) -> None:
"""Scan the GUI themes folder and list all themes."""
data: dict[str, tuple[str, str, Path]] = {}
keys = []
for entry in entries:
try:
with open(entry, mode="r", encoding="utf-8") as fo:
for line in fo:
key, _, value = line.partition("=")
if key.strip() == "meta:name":
if name := value.strip():
lookup = entry.stem
keys.append(lookup)
data[lookup] = (lookup, name, entry)
break
except Exception:
logger.error("Could not read file: %s", entry)
logException()
self._allThemes = {}
for lookup in sorted(keys):
key, name, item = data[lookup]
logger.debug("Checking icon theme '%s'", key)
self._allThemes[key] = ThemeEntry(name, False, item)
return
# Module Functions # Module Functions
# ================ # ================
def _listConf(target: dict, path: Path, extension: str) -> None: def _listContent(data: list[Path], path: Path, extension: str) -> None:
"""Scan for theme files and populate the dictionary.""" """List files of a specific type and extend the list."""
if path.is_dir(): if path.is_dir():
for item in path.iterdir(): data.extend(n for n in path.iterdir() if n.is_file() and n.suffix == extension)
if item.is_file() and item.name.endswith(extension):
target[item.stem] = item
return return
def _sortTheme(data: tuple) -> str:
"""Key function for theme sorting."""
key, name = data[:2]
return f"*{name}" if key.startswith("default_") else name
def _loadInternalName(parser: ConfigParser, path: str | Path) -> tuple[str, bool]:
"""Open a conf file and read the 'name' setting."""
try:
parser.clear()
with open(path, mode="r", encoding="utf-8") as inFile:
parser.read_file(inFile)
name = parser.get("Main", "name", fallback="")
dark = parser.get("Main", "mode", fallback="light").lower() == "dark"
return name, dark
except Exception:
logger.error("Could not read file: %s", path)
logException()
return "", False
def _loadIconName(path: Path) -> str:
"""Open an icons file and read the name setting."""
try:
with open(path, mode="r", encoding="utf-8") as icons:
for icon in icons:
key, _, value = icon.partition("=")
if key.strip() == "meta:name":
return value.strip()
except Exception:
logger.error("Could not read file: %s", path)
logException()
return ""
+1 -2
View File
@@ -937,8 +937,7 @@ class GuiMain(QMainWindow):
def changeEvent(self, event: QEvent) -> None: def changeEvent(self, event: QEvent) -> None:
"""Capture application change events.""" """Capture application change events."""
if int(event.type()) == 210: if int(event.type()) == 210: # ThemeChange
# ThemeChange
self.checkThemeUpdate() self.checkThemeUpdate()
return return
+1
View File
@@ -175,6 +175,7 @@ class SharedData(QObject):
is created. is created.
""" """
self._theme = theme self._theme = theme
self._theme.initThemes()
return return
def initSharedData(self, gui: GuiMain) -> None: def initSharedData(self, gui: GuiMain) -> None:
+1 -1
View File
@@ -31,7 +31,7 @@ from tests.tools import C, buildTestProject
@pytest.mark.gui @pytest.mark.gui
def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, projPath, mockRnd): def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
"""Test the the various features of the status bar.""" """Test the various features of the status bar."""
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
cHandle = SHARED.project.newFile("A Note", C.hCharRoot) cHandle = SHARED.project.newFile("A Note", C.hCharRoot)
newDoc = SHARED.project.storage.getDocument(cHandle) newDoc = SHARED.project.storage.getDocument(cHandle)
+12 -13
View File
@@ -33,7 +33,7 @@ from novelwriter import CONFIG, SHARED
from novelwriter.config import DEF_GUI_LIGHT from novelwriter.config import DEF_GUI_LIGHT
from novelwriter.constants import nwLabels from novelwriter.constants import nwLabels
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
from novelwriter.gui.theme import _listConf from novelwriter.gui.theme import _listContent
from tests.mocked import causeOSError from tests.mocked import causeOSError
from tests.tools import writeFile from tests.tools import writeFile
@@ -54,18 +54,17 @@ def testGuiTheme_Main(qtbot, nwGUI, tstPaths):
# Scan for Themes # Scan for Themes
# =============== # ===============
result = {} result = []
_listConf({}, Path("not_a_path"), ".conf") _listContent(result, Path("not_a_path"), ".conf")
assert result == {} assert result == []
themeOne = tstPaths.cnfDir / "themes" / "themeone.conf" themeOne = tstPaths.cnfDir / "themes" / "themeone.conf"
themeTwo = tstPaths.cnfDir / "themes" / "themetwo.conf" themeTwo = tstPaths.cnfDir / "themes" / "themetwo.conf"
writeFile(themeOne, "# Stuff") writeFile(themeOne, "# Stuff")
writeFile(themeTwo, "# Stuff") writeFile(themeTwo, "# Stuff")
_listConf(result, tstPaths.cnfDir / "themes", ".conf") _listContent(result, tstPaths.cnfDir / "themes", ".conf")
assert result["themeone"] == themeOne assert result == [themeOne, themeTwo]
assert result["themetwo"] == themeTwo
# Parse Colours # Parse Colours
# ============= # =============
@@ -157,17 +156,17 @@ def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI, tstPaths):
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError) mp.setattr("builtins.open", causeOSError)
theme._themeList = [] theme._themeList = []
assert theme.listThemes() == [] assert theme.getColourThemes() == []
# Load the theme info, default themes first # Load the theme info, default themes first
themesList = theme.listThemes() themesList = theme.getColourThemes()
assert themesList[0] == ("default_dark", "Default Dark Theme") assert themesList[0] == ("default_dark", "Default Dark Theme")
assert themesList[1] == ("default_light", "Default Light Theme") assert themesList[1] == ("default_light", "Default Light Theme")
assert themesList[2] == ("cyberpunk_night", "Cyberpunk Night") assert themesList[2] == ("cyberpunk_night", "Cyberpunk Night")
assert themesList[3] == ("dracula", "Dracula") assert themesList[3] == ("dracula", "Dracula")
# A second call should returned the cached list # A second call should returned the cached list
assert theme.listThemes() == theme._themeList assert theme.getColourThemes() == theme._themeList
# Check handling of broken theme settings # Check handling of broken theme settings
CONFIG.guiTheme = "not_a_theme" CONFIG.guiTheme = "not_a_theme"
@@ -376,18 +375,18 @@ def testGuiTheme_IconThemes(qtbot, caplog, monkeypatch, nwGUI, tstPaths):
# Load error returns empty list # Load error returns empty list
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError) mp.setattr("builtins.open", causeOSError)
themes = iconCache.listThemes() themes = iconCache.getIconThemes()
assert themes == [] assert themes == []
# Successful read # Successful read
themes = iconCache.listThemes() themes = iconCache.getIconThemes()
assert len(themes) > 1 assert len(themes) > 1
assert "material_rounded_normal" in dict(themes) assert "material_rounded_normal" in dict(themes)
# Load error doesn't matter on second read since list is cached # Load error doesn't matter on second read since list is cached
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError) mp.setattr("builtins.open", causeOSError)
assert iconCache.listThemes() == themes assert iconCache.getIconThemes() == themes
# qtbot.stop() # qtbot.stop()