Files
manual_slop/theme_2.py

273 lines
13 KiB
Python

# theme_2.py
"""
Theming support for manual_slop GUI — imgui-bundle port.
Replaces theme.py (DearPyGui-specific) with imgui-bundle equivalents.
Palettes are applied via imgui.get_style().set_color_() calls.
Font loading uses hello_imgui.load_font().
Scale uses imgui.get_io().font_global_scale.
"""
from imgui_bundle import imgui, hello_imgui
from pathlib import Path
# ------------------------------------------------------------------ palettes
# Each palette maps imgui color enum values to (R, G, B, A) floats [0..1].
# Only keys that differ from the ImGui dark defaults need to be listed.
def _c(r, g, b, a=255):
"""Convert 0-255 RGBA to 0.0-1.0 floats."""
return (r / 255.0, g / 255.0, b / 255.0, a / 255.0)
_PALETTES: dict[str, dict[int, tuple]] = {
"ImGui Dark": {}, # empty = use imgui dark defaults
"10x Dark": {
imgui.Col_.window_bg: _c( 34, 32, 28),
imgui.Col_.child_bg: _c( 30, 28, 24),
imgui.Col_.popup_bg: _c( 35, 30, 20),
imgui.Col_.border: _c( 60, 55, 50),
imgui.Col_.border_shadow: _c( 0, 0, 0, 0),
imgui.Col_.frame_bg: _c( 45, 42, 38),
imgui.Col_.frame_bg_hovered: _c( 60, 56, 50),
imgui.Col_.frame_bg_active: _c( 75, 70, 62),
imgui.Col_.title_bg: _c( 40, 35, 25),
imgui.Col_.title_bg_active: _c( 60, 45, 15),
imgui.Col_.title_bg_collapsed: _c( 30, 27, 20),
imgui.Col_.menu_bar_bg: _c( 35, 30, 20),
imgui.Col_.scrollbar_bg: _c( 30, 28, 24),
imgui.Col_.scrollbar_grab: _c( 80, 78, 72),
imgui.Col_.scrollbar_grab_hovered: _c(100, 100, 92),
imgui.Col_.scrollbar_grab_active: _c(120, 118, 110),
imgui.Col_.check_mark: _c(194, 164, 74),
imgui.Col_.slider_grab: _c(126, 78, 14),
imgui.Col_.slider_grab_active: _c(194, 140, 30),
imgui.Col_.button: _c( 83, 76, 60),
imgui.Col_.button_hovered: _c(126, 78, 14),
imgui.Col_.button_active: _c(115, 90, 70),
imgui.Col_.header: _c( 83, 76, 60),
imgui.Col_.header_hovered: _c(126, 78, 14),
imgui.Col_.header_active: _c(115, 90, 70),
imgui.Col_.separator: _c( 70, 65, 55),
imgui.Col_.separator_hovered: _c(126, 78, 14),
imgui.Col_.separator_active: _c(194, 164, 74),
imgui.Col_.resize_grip: _c( 60, 55, 44),
imgui.Col_.resize_grip_hovered: _c(126, 78, 14),
imgui.Col_.resize_grip_active: _c(194, 164, 74),
imgui.Col_.tab: _c( 83, 83, 70),
imgui.Col_.tab_hovered: _c(126, 77, 25),
imgui.Col_.tab_selected: _c(126, 77, 25),
imgui.Col_.tab_dimmed: _c( 60, 58, 50),
imgui.Col_.tab_dimmed_selected: _c( 90, 80, 55),
imgui.Col_.docking_preview: _c(126, 78, 14, 180),
imgui.Col_.docking_empty_bg: _c( 20, 20, 20),
imgui.Col_.text: _c(200, 200, 200),
imgui.Col_.text_disabled: _c(130, 130, 120),
imgui.Col_.text_selected_bg: _c( 59, 86, 142, 180),
imgui.Col_.table_header_bg: _c( 55, 50, 38),
imgui.Col_.table_border_strong: _c( 70, 65, 55),
imgui.Col_.table_border_light: _c( 50, 47, 42),
imgui.Col_.table_row_bg: _c( 0, 0, 0, 0),
imgui.Col_.table_row_bg_alt: _c( 40, 38, 34, 40),
imgui.Col_.nav_cursor: _c(126, 78, 14),
imgui.Col_.nav_windowing_highlight: _c(194, 164, 74, 180),
imgui.Col_.nav_windowing_dim_bg: _c( 20, 20, 20, 80),
imgui.Col_.modal_window_dim_bg: _c( 10, 10, 10, 100),
},
"Nord Dark": {
imgui.Col_.window_bg: _c( 36, 41, 49),
imgui.Col_.child_bg: _c( 30, 34, 42),
imgui.Col_.popup_bg: _c( 36, 41, 49),
imgui.Col_.border: _c( 59, 66, 82),
imgui.Col_.border_shadow: _c( 0, 0, 0, 0),
imgui.Col_.frame_bg: _c( 46, 52, 64),
imgui.Col_.frame_bg_hovered: _c( 59, 66, 82),
imgui.Col_.frame_bg_active: _c( 67, 76, 94),
imgui.Col_.title_bg: _c( 36, 41, 49),
imgui.Col_.title_bg_active: _c( 59, 66, 82),
imgui.Col_.title_bg_collapsed: _c( 30, 34, 42),
imgui.Col_.menu_bar_bg: _c( 46, 52, 64),
imgui.Col_.scrollbar_bg: _c( 30, 34, 42),
imgui.Col_.scrollbar_grab: _c( 76, 86, 106),
imgui.Col_.scrollbar_grab_hovered: _c( 94, 129, 172),
imgui.Col_.scrollbar_grab_active: _c(129, 161, 193),
imgui.Col_.check_mark: _c(136, 192, 208),
imgui.Col_.slider_grab: _c( 94, 129, 172),
imgui.Col_.slider_grab_active: _c(129, 161, 193),
imgui.Col_.button: _c( 59, 66, 82),
imgui.Col_.button_hovered: _c( 94, 129, 172),
imgui.Col_.button_active: _c(129, 161, 193),
imgui.Col_.header: _c( 59, 66, 82),
imgui.Col_.header_hovered: _c( 94, 129, 172),
imgui.Col_.header_active: _c(129, 161, 193),
imgui.Col_.separator: _c( 59, 66, 82),
imgui.Col_.separator_hovered: _c( 94, 129, 172),
imgui.Col_.separator_active: _c(136, 192, 208),
imgui.Col_.resize_grip: _c( 59, 66, 82),
imgui.Col_.resize_grip_hovered: _c( 94, 129, 172),
imgui.Col_.resize_grip_active: _c(136, 192, 208),
imgui.Col_.tab: _c( 46, 52, 64),
imgui.Col_.tab_hovered: _c( 94, 129, 172),
imgui.Col_.tab_selected: _c( 76, 86, 106),
imgui.Col_.tab_dimmed: _c( 36, 41, 49),
imgui.Col_.tab_dimmed_selected: _c( 59, 66, 82),
imgui.Col_.docking_preview: _c( 94, 129, 172, 180),
imgui.Col_.docking_empty_bg: _c( 20, 22, 28),
imgui.Col_.text: _c(216, 222, 233),
imgui.Col_.text_disabled: _c(116, 128, 150),
imgui.Col_.text_selected_bg: _c( 94, 129, 172, 180),
imgui.Col_.table_header_bg: _c( 59, 66, 82),
imgui.Col_.table_border_strong: _c( 76, 86, 106),
imgui.Col_.table_border_light: _c( 59, 66, 82),
imgui.Col_.table_row_bg: _c( 0, 0, 0, 0),
imgui.Col_.table_row_bg_alt: _c( 46, 52, 64, 40),
imgui.Col_.nav_cursor: _c(136, 192, 208),
imgui.Col_.modal_window_dim_bg: _c( 10, 12, 16, 100),
},
"Monokai": {
imgui.Col_.window_bg: _c( 39, 40, 34),
imgui.Col_.child_bg: _c( 34, 35, 29),
imgui.Col_.popup_bg: _c( 39, 40, 34),
imgui.Col_.border: _c( 60, 61, 52),
imgui.Col_.border_shadow: _c( 0, 0, 0, 0),
imgui.Col_.frame_bg: _c( 50, 51, 44),
imgui.Col_.frame_bg_hovered: _c( 65, 67, 56),
imgui.Col_.frame_bg_active: _c( 80, 82, 68),
imgui.Col_.title_bg: _c( 39, 40, 34),
imgui.Col_.title_bg_active: _c( 73, 72, 62),
imgui.Col_.title_bg_collapsed: _c( 30, 31, 26),
imgui.Col_.menu_bar_bg: _c( 50, 51, 44),
imgui.Col_.scrollbar_bg: _c( 34, 35, 29),
imgui.Col_.scrollbar_grab: _c( 80, 80, 72),
imgui.Col_.scrollbar_grab_hovered: _c(102, 217, 39),
imgui.Col_.scrollbar_grab_active: _c(166, 226, 46),
imgui.Col_.check_mark: _c(166, 226, 46),
imgui.Col_.slider_grab: _c(102, 217, 39),
imgui.Col_.slider_grab_active: _c(166, 226, 46),
imgui.Col_.button: _c( 73, 72, 62),
imgui.Col_.button_hovered: _c(249, 38, 114),
imgui.Col_.button_active: _c(198, 30, 92),
imgui.Col_.header: _c( 73, 72, 62),
imgui.Col_.header_hovered: _c(249, 38, 114),
imgui.Col_.header_active: _c(198, 30, 92),
imgui.Col_.separator: _c( 60, 61, 52),
imgui.Col_.separator_hovered: _c(249, 38, 114),
imgui.Col_.separator_active: _c(166, 226, 46),
imgui.Col_.resize_grip: _c( 73, 72, 62),
imgui.Col_.resize_grip_hovered: _c(249, 38, 114),
imgui.Col_.resize_grip_active: _c(166, 226, 46),
imgui.Col_.tab: _c( 73, 72, 62),
imgui.Col_.tab_hovered: _c(249, 38, 114),
imgui.Col_.tab_selected: _c(249, 38, 114),
imgui.Col_.tab_dimmed: _c( 50, 51, 44),
imgui.Col_.tab_dimmed_selected: _c( 90, 88, 76),
imgui.Col_.docking_preview: _c(249, 38, 114, 180),
imgui.Col_.docking_empty_bg: _c( 20, 20, 18),
imgui.Col_.text: _c(248, 248, 242),
imgui.Col_.text_disabled: _c(117, 113, 94),
imgui.Col_.text_selected_bg: _c(249, 38, 114, 150),
imgui.Col_.table_header_bg: _c( 60, 61, 52),
imgui.Col_.table_border_strong: _c( 73, 72, 62),
imgui.Col_.table_border_light: _c( 55, 56, 48),
imgui.Col_.table_row_bg: _c( 0, 0, 0, 0),
imgui.Col_.table_row_bg_alt: _c( 50, 51, 44, 40),
imgui.Col_.nav_cursor: _c(166, 226, 46),
imgui.Col_.modal_window_dim_bg: _c( 10, 10, 8, 100),
},
}
PALETTE_NAMES: list[str] = list(_PALETTES.keys())
# ------------------------------------------------------------------ state
_current_palette: str = "ImGui Dark"
_current_font_path: str = ""
_current_font_size: float = 16.0
_current_scale: float = 1.0
_custom_font: imgui.ImFont = None # type: ignore
# ------------------------------------------------------------------ public API
def get_palette_names() -> list[str]:
return list(_PALETTES.keys())
def get_current_palette() -> str:
return _current_palette
def get_current_font_path() -> str:
return _current_font_path
def get_current_font_size() -> float:
return _current_font_size
def get_current_scale() -> float:
return _current_scale
def apply(palette_name: str):
"""
Apply a named palette by setting all ImGui style colors.
Call this once per frame if you want dynamic switching, or once at startup.
In practice we call it once when the user picks a palette, and imgui retains the style.
"""
global _current_palette
_current_palette = palette_name
colours = _PALETTES.get(palette_name, {})
if not colours:
# Reset to imgui dark defaults
imgui.style_colors_dark()
return
style = imgui.get_style()
# Start from dark defaults so unlisted keys have sensible values
imgui.style_colors_dark()
for col_enum, rgba in colours.items():
style.set_color_(col_enum, imgui.ImVec4(*rgba))
def set_scale(factor: float):
"""Set the global font scale factor."""
global _current_scale
_current_scale = factor
io = imgui.get_io()
io.font_global_scale = factor
def save_to_config(config: dict):
"""Persist theme settings into the config dict under [theme]."""
config.setdefault("theme", {})
config["theme"]["palette"] = _current_palette
config["theme"]["font_path"] = _current_font_path
config["theme"]["font_size"] = _current_font_size
config["theme"]["scale"] = _current_scale
def load_from_config(config: dict):
"""Read [theme] from config and apply palette + scale. Font is handled separately at startup."""
global _current_font_path, _current_font_size, _current_scale, _current_palette
t = config.get("theme", {})
_current_palette = t.get("palette", "ImGui Dark")
_current_font_path = t.get("font_path", "")
_current_font_size = float(t.get("font_size", 16.0))
_current_scale = float(t.get("scale", 1.0))
apply(_current_palette)
set_scale(_current_scale)
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