# 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