Files
manual_slop/src/theme_2.py
T

396 lines
17 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 or hello_imgui.apply_theme().
Font loading uses hello_imgui.load_font().
Scale uses imgui.get_style().font_scale_main.
"""
from imgui_bundle import imgui, hello_imgui
from typing import Any, Optional
import src.theme_nerv
# ------------------------------------------------------------------ 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: int, g: int, b: int, a: int = 255) -> tuple[float, float, float, float]:
"""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
"NERV": {},
"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),
},
"Binks": {
imgui.Col_.text: _c( 0, 0, 0, 255),
imgui.Col_.text_disabled: _c(153, 153, 153, 255),
imgui.Col_.window_bg: _c(240, 240, 240, 240),
imgui.Col_.child_bg: _c( 0, 0, 0, 0),
imgui.Col_.popup_bg: _c(255, 255, 255, 240),
imgui.Col_.border: _c( 0, 0, 0, 99),
imgui.Col_.border_shadow: _c(255, 255, 255, 25),
imgui.Col_.frame_bg: _c(255, 255, 255, 240),
imgui.Col_.frame_bg_hovered: _c( 66, 150, 250, 102),
imgui.Col_.frame_bg_active: _c( 66, 150, 250, 171),
imgui.Col_.title_bg: _c(245, 245, 245, 255),
imgui.Col_.title_bg_collapsed: _c(255, 255, 255, 130),
imgui.Col_.title_bg_active: _c(209, 209, 209, 255),
imgui.Col_.menu_bar_bg: _c(219, 219, 219, 255),
imgui.Col_.scrollbar_bg: _c(250, 250, 250, 135),
imgui.Col_.scrollbar_grab: _c(176, 176, 176, 255),
imgui.Col_.scrollbar_grab_hovered: _c(150, 150, 150, 255),
imgui.Col_.scrollbar_grab_active: _c(125, 125, 125, 255),
imgui.Col_.check_mark: _c( 66, 150, 250, 255),
imgui.Col_.slider_grab: _c( 61, 133, 224, 255),
imgui.Col_.slider_grab_active: _c( 66, 150, 250, 255),
imgui.Col_.button: _c( 66, 150, 250, 102),
imgui.Col_.button_hovered: _c( 66, 150, 250, 255),
imgui.Col_.button_active: _c( 15, 135, 250, 255),
imgui.Col_.header: _c( 66, 150, 250, 79),
imgui.Col_.header_hovered: _c( 66, 150, 250, 204),
imgui.Col_.header_active: _c( 66, 150, 250, 255),
imgui.Col_.separator: _c(100, 100, 100, 255),
imgui.Col_.resize_grip: _c(255, 255, 255, 127),
imgui.Col_.resize_grip_hovered: _c( 66, 150, 250, 171),
imgui.Col_.resize_grip_active: _c( 66, 150, 250, 242),
imgui.Col_.plot_lines: _c( 99, 99, 99, 255),
imgui.Col_.plot_lines_hovered: _c(255, 110, 89, 255),
imgui.Col_.plot_histogram: _c(230, 178, 0, 255),
imgui.Col_.plot_histogram_hovered: _c(255, 153, 0, 255),
imgui.Col_.text_selected_bg: _c( 66, 150, 250, 89),
imgui.Col_.modal_window_dim_bg: _c( 51, 51, 51, 89),
},
}
def get_palette_names() -> list[str]:
"""Returns a list of all available palettes, including hello_imgui built-ins."""
names = list(_PALETTES.keys())
# Add hello_imgui themes
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
# ------------------------------------------------------------------ state
_current_palette: str = "10x Dark"
_current_font_path: str = "fonts/Inter-Regular.ttf"
_current_font_size: float = 16.0
_current_scale: float = 1.0
_transparency: float = 1.0
_child_transparency: float = 1.0
# ------------------------------------------------------------------ public API
def get_current_palette() -> str:
return _current_palette
def is_nerv_active() -> bool:
return _current_palette == "NERV"
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 get_transparency() -> float:
return _transparency
def set_transparency(val: float) -> None:
global _transparency
_transparency = val
apply(_current_palette)
def get_child_transparency() -> float:
return _child_transparency
def set_child_transparency(val: float) -> None:
global _child_transparency
_child_transparency = val
apply(_current_palette)
def apply(palette_name: str) -> None:
"""
Apply a named palette by setting all ImGui style colors and applying global professional styling.
"""
global _current_palette
_current_palette = palette_name
if palette_name == 'NERV':
src.theme_nerv.apply_nerv()
return
# 1. Apply base colors
if palette_name in _PALETTES:
colours = _PALETTES[palette_name]
imgui.style_colors_dark()
style = imgui.get_style()
for col_enum, rgba in colours.items():
style.set_color_(col_enum, imgui.ImVec4(*rgba))
elif hasattr(hello_imgui.ImGuiTheme_, palette_name):
theme_enum = getattr(hello_imgui.ImGuiTheme_, palette_name)
hello_imgui.apply_theme(theme_enum)
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()
# 2. Apply our "Subtle Rounding" professional tweaks on top of ANY theme
style = imgui.get_style()
style.window_rounding = 6.0
style.child_rounding = 4.0
style.frame_rounding = 4.0
style.popup_rounding = 4.0
style.scrollbar_rounding = 12.0
style.grab_rounding = 4.0
style.tab_rounding = 4.0
style.window_border_size = 1.0
style.frame_border_size = 1.0
style.popup_border_size = 1.0
# Apply transparency to WindowBg
win_bg = style.color_(imgui.Col_.window_bg)
win_bg.w = _transparency
style.set_color_(imgui.Col_.window_bg, win_bg)
# Apply child/frame transparency
for col_idx in [imgui.Col_.child_bg, imgui.Col_.frame_bg, imgui.Col_.popup_bg]:
c = style.color_(col_idx)
c.w = _child_transparency
style.set_color_(col_idx, c)
# Spacing & Padding
style.window_padding = imgui.ImVec2(8.0, 8.0)
style.frame_padding = imgui.ImVec2(8.0, 4.0)
style.item_spacing = imgui.ImVec2(8.0, 4.0)
style.item_inner_spacing = imgui.ImVec2(4.0, 4.0)
style.scrollbar_size = 14.0
# Rendering anti-aliasing (Shaders/Quality)
style.anti_aliased_lines = True
style.anti_aliased_fill = True
style.anti_aliased_lines_use_tex = True
def set_scale(factor: float) -> None:
"""Set the global font/UI scale factor."""
global _current_scale
_current_scale = factor
style = imgui.get_style()
style.font_scale_main = factor
def save_to_config(config: dict) -> None:
"""Persist theme settings into the config dict under [theme]."""
import sys
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
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")
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
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"
_current_font_path = t.get("font_path", "fonts/Inter-Regular.ttf")
_current_font_size = float(t.get("font_size", 16.0))
_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")
sys.stderr.flush()
def apply_current() -> None:
"""Apply the loaded palette and scale. Call after imgui context exists."""
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
def get_tweaked_theme() -> hello_imgui.ImGuiTweakedTheme:
"""Returns an ImGuiTweakedTheme object reflecting the current state."""
tt = hello_imgui.ImGuiTweakedTheme()
if hasattr(hello_imgui.ImGuiTheme_, _current_palette):
tt.theme = getattr(hello_imgui.ImGuiTheme_, _current_palette)
else:
tt.theme = hello_imgui.ImGuiTheme_.imgui_colors_dark
# Sync tweaks
tt.tweaks.rounding = 6.0
return tt