Private
Public Access
0
0

feat(theme): add tone mapping and fix missing palette colors

This commit is contained in:
2026-06-04 23:44:43 -04:00
parent d9d0fea971
commit 06e305aba6
10 changed files with 186 additions and 47 deletions
+60 -13
View File
@@ -72,6 +72,38 @@ _current_scale: float = 1.0
_transparency: float = 1.0
_child_transparency: float = 1.0
# Per-palette tone mapping: { "Palette Name": value }
_brightness: dict[str, float] = {}
_contrast: dict[str, float] = {}
_gamma: dict[str, float] = {}
def _get_tm(d: dict[str, float], palette: str, default: float) -> float:
return d.get(palette, default)
def get_brightness(palette: str) -> float: return _get_tm(_brightness, palette, 1.0)
def get_contrast(palette: str) -> float: return _get_tm(_contrast, palette, 1.0)
def get_gamma(palette: str) -> float: return _get_tm(_gamma, palette, 1.0)
def set_brightness(palette: str, val: float) -> None: _brightness[palette] = val; apply(palette)
def set_contrast(palette: str, val: float) -> None: _contrast[palette] = val; apply(palette)
def set_gamma(palette: str, val: float) -> None: _gamma[palette] = val; apply(palette)
def reset_tone_mapping(palette: str) -> None:
for d in [_brightness, _contrast, _gamma]:
if palette in d: del d[palette]
apply(palette)
def _tone_map(rgb: tuple[float, float, float, float], palette: str) -> tuple[float, float, float, float]:
b, c, g = get_brightness(palette), get_contrast(palette), get_gamma(palette)
r, g_val, bl, a = rgb
# 1. Brightness
r *= b; g_val *= b; bl *= b
# 2. Contrast
r = (r - 0.5) * c + 0.5; g_val = (g_val - 0.5) * c + 0.5; bl = (bl - 0.5) * c + 0.5
# 3. Gamma
r = max(0, r)**(1.0/g); g_val = max(0, g_val)**(1.0/g); bl = max(0, bl)**(1.0/g)
return (max(0.0, min(1.0, r)), max(0.0, min(1.0, g_val)), max(0.0, min(1.0, bl)), a)
_crt_filter = CRTFilter()
_alert_pulsing = AlertPulsing()
_status_flicker = StatusFlicker()
@@ -129,7 +161,7 @@ def apply(palette_name: str) -> None:
imgui.style_colors_dark()
style = imgui.get_style()
for col_enum, rgba in colours.items():
style.set_color_(col_enum, imgui.ImVec4(*rgba))
style.set_color_(col_enum, imgui.ImVec4(*_tone_map(rgba, palette_name)))
elif palette_name in _TOML_PALETTES:
colours = _TOML_COLOUR_CACHE.get(palette_name, {})
if not colours:
@@ -139,17 +171,15 @@ def apply(palette_name: str) -> None:
imgui.style_colors_dark()
style = imgui.get_style()
for colenum, rgba in colours.items():
style.set_color_(colenum, imgui.ImVec4(*rgba))
style.set_color_(colenum, imgui.ImVec4(*_tone_map(rgba, palette_name)))
elif hasattr(hello_imgui.ImGuiTheme_, palette_name):
theme_enum = getattr(hello_imgui.ImGuiTheme_, palette_name)
hello_imgui.apply_theme(theme_enum)
# hello_imgui doesn't expose the underlying dict easily to tone-map after-the-fact
# without re-reading every enum. For now, BUILTIN and TOML themes get full TM.
else:
# Fallback to Nord Dark if requested but not found, otherwise ImGui Dark
if palette_name == "Nord Dark":
# This should not happen since it's in _PALETTES, but for safety
imgui.style_colors_dark()
else:
imgui.style_colors_dark()
# Fallback
imgui.style_colors_dark()
# 2. Apply our "Subtle Rounding" professional tweaks on top of ANY theme
style = imgui.get_style()
@@ -187,6 +217,11 @@ def apply(palette_name: str) -> None:
style.anti_aliased_fill = True
style.anti_aliased_lines_use_tex = True
# 3. Sync syntax palette and clear markdown render cache
apply_syntax_palette(get_syntax_palette_for_theme(palette_name))
import src.markdown_helper
src.markdown_helper.get_renderer().clear_cache()
def set_scale(factor: float) -> None:
"""Set the global font/UI scale factor."""
global _current_scale
@@ -204,16 +239,23 @@ def save_to_config(config: dict) -> None:
config["theme"]["scale"] = _current_scale
config["theme"]["transparency"] = _transparency
config["theme"]["child_transparency"] = _child_transparency
sys.stderr.write(f"[DEBUG theme_2] save_to_config: palette={_current_palette}, transparency={_transparency}\n")
# Tone mapping
tm = {}
for p in set(list(_brightness.keys()) + list(_contrast.keys()) + list(_gamma.keys())):
tm[p] = {
"brightness": _brightness.get(p, 1.0),
"contrast": _contrast.get(p, 1.0),
"gamma": _gamma.get(p, 1.0)
}
config["theme"]["tone_mapping"] = tm
sys.stderr.write(f"[DEBUG theme_2] save_to_config: palette={_current_palette}\n")
sys.stderr.flush()
def load_from_config(config: dict) -> None:
"""Read [theme] from config. Font is handled separately at startup."""
import sys
global _current_font_path, _current_font_size, _current_scale, _current_palette, _transparency, _child_transparency
global _current_font_path, _current_font_size, _current_scale, _current_palette, _transparency, _child_transparency, _brightness, _contrast, _gamma
t = config.get("theme", {})
sys.stderr.write(f"[DEBUG theme_2] load_from_config raw: {t}\n")
sys.stderr.flush()
_current_palette = t.get("palette", "10x Dark")
if _current_palette in ("", "DPG Default"):
_current_palette = "10x Dark"
@@ -223,7 +265,12 @@ def load_from_config(config: dict) -> None:
_current_scale = float(t.get("scale", 1.0))
_transparency = float(t.get("transparency", 1.0))
_child_transparency = float(t.get("child_transparency", 1.0))
sys.stderr.write(f"[DEBUG theme_2] load_from_config effective: palette={_current_palette}, transparency={_transparency}\n")
# Tone mapping
tm = t.get("tone_mapping", {})
_brightness = {p: float(v.get("brightness", 1.0)) for p, v in tm.items()}
_contrast = {p: float(v.get("contrast", 1.0)) for p, v in tm.items()}
_gamma = {p: float(v.get("gamma", 1.0)) for p, v in tm.items()}
sys.stderr.write(f"[DEBUG theme_2] load_from_config: palette={_current_palette}\n")
sys.stderr.flush()
def apply_current() -> None: