Add basic support for loading icons from a single file

This commit is contained in:
Veronica Berglyd Olsen
2025-01-07 17:55:49 +01:00
parent 362da679b6
commit e66288b4de
+50 -8
View File
@@ -38,6 +38,7 @@ from novelwriter.common import NWConfigParser, cssCol, minmax
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.error import logException from novelwriter.error import logException
from novelwriter.types import QtTransparent
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -245,6 +246,18 @@ class GuiTheme:
self.themeLicenseUrl = parser.rdStr(sec, "licenseurl", "") self.themeLicenseUrl = parser.rdStr(sec, "licenseurl", "")
self.themeIcons = parser.rdStr(sec, "icontheme", "") self.themeIcons = parser.rdStr(sec, "icontheme", "")
# Icons
sec = "Icons"
if parser.has_section(sec):
self.iconCache.setIconColor("default", self._parseColour(parser, sec, "default"))
self.iconCache.setIconColor("red", self._parseColour(parser, sec, "red"))
self.iconCache.setIconColor("orange", self._parseColour(parser, sec, "orange"))
self.iconCache.setIconColor("yellow", self._parseColour(parser, sec, "yellow"))
self.iconCache.setIconColor("green", self._parseColour(parser, sec, "green"))
self.iconCache.setIconColor("aqua", self._parseColour(parser, sec, "aqua"))
self.iconCache.setIconColor("blue", self._parseColour(parser, sec, "blue"))
self.iconCache.setIconColor("purple", self._parseColour(parser, sec, "purple"))
# Palette # Palette
sec = "Palette" sec = "Palette"
if parser.has_section(sec): if parser.has_section(sec):
@@ -294,6 +307,7 @@ class GuiTheme:
# Icons # Icons
defaultIcons = "typicons_light" if backLNess >= 0.5 else "typicons_dark" defaultIcons = "typicons_light" if backLNess >= 0.5 else "typicons_dark"
self.iconCache.loadTheme(self.themeIcons or defaultIcons) self.iconCache.loadTheme(self.themeIcons or defaultIcons)
self.iconCache.loadNewTheme("")
# Apply Styles # Apply Styles
QApplication.setPalette(self._guiPalette) QApplication.setPalette(self._guiPalette)
@@ -540,6 +554,9 @@ class GuiIcons:
self.mainTheme = mainTheme self.mainTheme = mainTheme
# Storage # Storage
self._svgData: dict[str, bytes] = {}
self._svgColours: dict[str, bytes] = {}
self._qIcons: dict[str, QIcon] = {} self._qIcons: dict[str, QIcon] = {}
self._themeMap: dict[str, Path] = {} self._themeMap: dict[str, Path] = {}
self._headerDec: list[QPixmap] = [] self._headerDec: list[QPixmap] = []
@@ -638,6 +655,22 @@ class GuiIcons:
return True return True
def loadNewTheme(self, iconTheme: str) -> bool:
"""Load new style theme."""
themePath = self._iconPath / "material_outline_normal.icons"
with open(themePath, mode="r", encoding="utf-8") as icons:
for icon in icons:
key, _, svg = icon.partition(" = ")
if key and svg:
self._svgData[key.strip()] = svg.strip().encode("utf-8")
return True
def setIconColor(self, key: str, color: QColor) -> None:
"""Set an icon colour for a named colour."""
self._svgColours[key] = color.name(QColor.NameFormat.HexRgb).encode("utf-8")
return
## ##
# Access Functions # Access Functions
## ##
@@ -670,13 +703,14 @@ class GuiIcons:
return pixmap return pixmap
def getIcon(self, name: str) -> QIcon: def getIcon(self, name: str, color: str | None = None) -> QIcon:
"""Return an icon from the icon buffer, or load it.""" """Return an icon from the icon buffer, or load it."""
if name in self._qIcons: key = f"{name}_{color}" if color else name
if key in self._qIcons:
return self._qIcons[name] return self._qIcons[name]
else: else:
icon = self._loadIcon(name) icon = self._loadIcon(name, color)
self._qIcons[name] = icon self._qIcons[key] = icon
return icon return icon
def getToggleIcon(self, name: str, size: tuple[int, int]) -> QIcon: def getToggleIcon(self, name: str, size: tuple[int, int]) -> QIcon:
@@ -755,13 +789,13 @@ class GuiIcons:
# Internal Functions # Internal Functions
## ##
def _loadIcon(self, name: str) -> QIcon: def _loadIcon(self, name: str, color: str | None = None) -> QIcon:
"""Load an icon from the assets themes folder. Is guaranteed to """Load an icon from the assets themes folder. Is guaranteed to
return a QIcon. return a QIcon.
""" """
if name not in self.ICON_KEYS: # if name not in self.ICON_KEYS:
logger.error("Requested unknown icon name '%s'", name) # logger.error("Requested unknown icon name '%s'", name)
return self._noIcon # return self._noIcon
# If we just want the app icons, return right away # If we just want the app icons, return right away
if name == "novelwriter": if name == "novelwriter":
@@ -769,6 +803,14 @@ class GuiIcons:
elif name == "proj_nwx": elif name == "proj_nwx":
return QIcon(str(self._iconPath / "x-novelwriter-project.svg")) return QIcon(str(self._iconPath / "x-novelwriter-project.svg"))
if svg := self._svgData.get(name, b""):
if fill := self._svgColours.get(color or "default"):
svg = svg.replace(b"#000000", fill)
pixmap = QPixmap(24, 24)
pixmap.fill(QtTransparent)
pixmap.loadFromData(svg, "svg")
return QIcon(pixmap)
# Otherwise, we load from the theme folder # Otherwise, we load from the theme folder
if name in self._themeMap: if name in self._themeMap:
logger.debug("Loading: %s", self._themeMap[name].name) logger.debug("Loading: %s", self._themeMap[name].name)