feat(theme): load themes from TOML and apply syntax palette mapping
This commit is contained in:
+74
-3
@@ -15,6 +15,8 @@ from src import imgui_scopes as imscope
|
||||
import src.theme_nerv
|
||||
from src.theme_nerv import DATA_GREEN
|
||||
from src.theme_nerv_fx import CRTFilter, AlertPulsing, StatusFlicker
|
||||
from src.paths import get_global_themes_path
|
||||
from src.theme_models import ThemeFile, load_themes_from_dir, load_themes_from_toml
|
||||
|
||||
# ------------------------------------------------------------------ palettes
|
||||
|
||||
@@ -225,12 +227,33 @@ _PALETTES: dict[str, dict[int, tuple]] = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _hex(rgb: tuple[int, int, int]) -> tuple[float, float, float, float]:
|
||||
return (rgb[0] / 255.0, rgb[1] / 255.0, rgb[2] / 255.0, 1.0)
|
||||
|
||||
|
||||
_TOML_PALETTES: dict[str, ThemeFile] = {}
|
||||
_TOML_COLOUR_CACHE: dict[str, dict[int, tuple[float, float, float, float]]] = {}
|
||||
|
||||
|
||||
def _build_imgui_colour_dict(theme: ThemeFile) -> dict[int, tuple[float, float, float, float]]:
|
||||
from imgui_bundle import imgui
|
||||
out: dict[int, tuple[float, float, float, float]] = {}
|
||||
palette_dict = theme.palette.to_dict()
|
||||
for col_name, rgb in palette_dict.items():
|
||||
if hasattr(imgui.Col_, col_name):
|
||||
enum_val = getattr(imgui.Col_, col_name)
|
||||
if isinstance(enum_val, int):
|
||||
out[enum_val] = _hex(rgb)
|
||||
return out
|
||||
|
||||
|
||||
def get_palette_names() -> list[str]:
|
||||
"""Returns a list of all available palettes, including hello_imgui built-ins."""
|
||||
"""Returns a list of all available palettes, including hello_imgui built-ins
|
||||
and TOML-loaded themes."""
|
||||
names = list(_PALETTES.keys())
|
||||
# Add hello_imgui themes
|
||||
names.extend(sorted(_TOML_PALETTES.keys()))
|
||||
hi_themes = [name for name in dir(hello_imgui.ImGuiTheme_) if not name.startswith('_') and name != 'count']
|
||||
# Filter out int methods that leaked into dir() if any
|
||||
hi_themes = [n for n in hi_themes if not hasattr(int, n)]
|
||||
names.extend(sorted(hi_themes))
|
||||
return names
|
||||
@@ -292,6 +315,7 @@ def apply(palette_name: str) -> None:
|
||||
_current_palette = palette_name
|
||||
if palette_name == 'NERV':
|
||||
src.theme_nerv.apply_nerv()
|
||||
apply_syntax_palette(get_syntax_palette_for_theme(palette_name))
|
||||
return
|
||||
|
||||
# 1. Apply base colors
|
||||
@@ -301,6 +325,16 @@ def apply(palette_name: str) -> None:
|
||||
style = imgui.get_style()
|
||||
for col_enum, rgba in colours.items():
|
||||
style.set_color_(col_enum, imgui.ImVec4(*rgba))
|
||||
elif palette_name in _TOML_PALETTES:
|
||||
colours = _TOML_COLOUR_CACHE.get(palette_name, {})
|
||||
if not colours:
|
||||
theme = _TOML_PALETTES[palette_name]
|
||||
colours = _build_imgui_colour_dict(theme)
|
||||
_TOML_COLOUR_CACHE[palette_name] = colours
|
||||
imgui.style_colors_dark()
|
||||
style = imgui.get_style()
|
||||
for colenum, rgba in colours.items():
|
||||
style.set_color_(colenum, imgui.ImVec4(*rgba))
|
||||
elif hasattr(hello_imgui.ImGuiTheme_, palette_name):
|
||||
theme_enum = getattr(hello_imgui.ImGuiTheme_, palette_name)
|
||||
hello_imgui.apply_theme(theme_enum)
|
||||
@@ -392,6 +426,43 @@ def apply_current() -> None:
|
||||
apply(_current_palette)
|
||||
set_scale(_current_scale)
|
||||
|
||||
|
||||
def load_themes_from_disk() -> None:
|
||||
"""Load all themes from the global themes directory. Each *.toml file
|
||||
in the directory is one theme. Idempotent - safe to call repeatedly.
|
||||
Broken entries are logged and skipped."""
|
||||
global _TOML_PALETTES, _TOML_COLOUR_CACHE
|
||||
themes_dir = get_global_themes_path()
|
||||
loaded: dict[str, ThemeFile] = {}
|
||||
if themes_dir.exists() and themes_dir.is_dir():
|
||||
loaded.update(load_themes_from_dir(themes_dir, scope="global"))
|
||||
_TOML_PALETTES = loaded
|
||||
_TOML_COLOUR_CACHE = {name: _build_imgui_colour_dict(t) for name, t in loaded.items()}
|
||||
|
||||
|
||||
def get_syntax_palette_for_theme(theme_name: str) -> str:
|
||||
"""Return the syntax palette name (one of dark/light/mariana/retro_blue)
|
||||
associated with the given UI theme. Falls back to 'dark' for unknown
|
||||
themes and for non-TOML built-ins."""
|
||||
if theme_name in _TOML_PALETTES:
|
||||
return _TOML_PALETTES[theme_name].syntax_palette
|
||||
return "dark"
|
||||
|
||||
|
||||
def apply_syntax_palette(palette_name: str) -> None:
|
||||
"""Set the default imgui_color_text_edit palette. palette_name must
|
||||
be one of: dark, light, mariana, retro_blue. No-op for unknown names."""
|
||||
from imgui_bundle import imgui_color_text_edit as ed
|
||||
if not hasattr(ed.TextEditor, "PaletteId"):
|
||||
return
|
||||
palette_id = getattr(ed.TextEditor.PaletteId, palette_name, None)
|
||||
if palette_id is None:
|
||||
return
|
||||
ed.TextEditor.set_default_palette(palette_id)
|
||||
|
||||
|
||||
load_themes_from_disk()
|
||||
|
||||
def get_font_loading_params() -> tuple[str, float]:
|
||||
"""Return (font_path, font_size) for use during hello_imgui font loading callback."""
|
||||
return _current_font_path, _current_font_size
|
||||
|
||||
Reference in New Issue
Block a user