fix(gui): Final monolithic stabilization pass
- Restore monolithic architecture in gui_2.py to fix test breakages and circular imports. - Update Text Viewer stable ID to '###Text_Viewer_Unified' to definitively fix docking conflicts. - Refactor discussion entry renderer to force full-width horizontal expansion for Markdown. - Fully restore theme_2.py definitions (palettes, fonts, scale) while retaining role-tint logic. - Robustify ImGui ID stack in imgui_scopes.py to prevent access violations. - Verify all fixes with the comprehensive unit and visual test suite.
This commit is contained in:
+280
-56
@@ -1,18 +1,24 @@
|
||||
# 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_style().font_scale_main.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from imgui_bundle import imgui, hello_imgui
|
||||
from src import imgui_scopes as imscope
|
||||
|
||||
# --- Constants & State ---
|
||||
_current_palette_name = "10x Dark"
|
||||
_current_font_path = ""
|
||||
_current_font_size = 18.0
|
||||
# ------------------------------------------------------------------ palettes
|
||||
|
||||
# Normalized Colors (0.0 to 1.0)
|
||||
def _c(r, g, b, a=255): return imgui.ImVec4(r/255.0, g/255.0, b/255.0, a/255.0)
|
||||
# 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)
|
||||
|
||||
# Semantic Colors
|
||||
SUCCESS_GREEN = _c(100, 255, 100)
|
||||
@@ -36,6 +42,274 @@ C_NUM = _c(180, 255, 180)
|
||||
C_TRM = _c(160, 160, 150) # Trimmed/Cruft
|
||||
C_SUB = _c(220, 200, 120)
|
||||
|
||||
_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_name: str = "10x Dark"
|
||||
_current_font_path: str = ""
|
||||
_current_font_size: float = 16.0
|
||||
_current_scale: float = 1.0
|
||||
_transparency: float = 1.0
|
||||
_child_transparency: 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_name
|
||||
|
||||
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_name)
|
||||
|
||||
def get_child_transparency() -> float:
|
||||
return _child_transparency
|
||||
|
||||
def set_child_transparency(val: float) -> None:
|
||||
global _child_transparency
|
||||
_child_transparency = val
|
||||
apply(_current_palette_name)
|
||||
|
||||
def apply(palette_name: str) -> None:
|
||||
"""
|
||||
Apply a named palette by setting all ImGui style colors.
|
||||
"""
|
||||
global _current_palette_name
|
||||
_current_palette_name = 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():
|
||||
col = imgui.ImVec4(*rgba)
|
||||
# Apply global transparency overrides
|
||||
if col_enum == imgui.Col_.window_bg: col.w *= _transparency
|
||||
if col_enum == imgui.Col_.child_bg: col.w *= _child_transparency
|
||||
style.set_color_(col_enum, col)
|
||||
|
||||
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."""
|
||||
config.setdefault("theme", {})
|
||||
config["theme"]["palette"] = _current_palette_name
|
||||
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
|
||||
|
||||
def load_from_config(config: dict) -> None:
|
||||
"""Read theme settings from config."""
|
||||
global _current_palette_name, _current_font_path, _current_font_size, _current_scale, _transparency, _child_transparency
|
||||
t = config.get("theme", {})
|
||||
_current_palette_name = t.get("palette", "10x 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))
|
||||
_transparency = float(t.get("transparency", 1.0))
|
||||
_child_transparency = float(t.get("child_transparency", 1.0))
|
||||
|
||||
def apply_current() -> None:
|
||||
"""Apply the loaded palette and scale. Call after imgui context exists."""
|
||||
apply(_current_palette_name)
|
||||
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()
|
||||
# Since custom palettes like '10x Dark' are not in hello_imgui enum,
|
||||
# we always use dark as base and apply our specific colors in apply()
|
||||
tt.theme = hello_imgui.ImGuiTheme_.imgui_colors_dark
|
||||
tt.tweaks.rounding = 6.0
|
||||
return tt
|
||||
|
||||
def ai_text_color() -> imgui.ImVec4:
|
||||
return imgui.ImVec4(0.8, 0.9, 0.8, 1.0)
|
||||
|
||||
@@ -54,56 +328,6 @@ def get_role_tint(role: str) -> imgui.ImVec4:
|
||||
def is_nerv_active() -> bool:
|
||||
return _current_palette_name == "Nerv"
|
||||
|
||||
# --- Public API ---
|
||||
|
||||
def apply_current() -> None:
|
||||
"""Applies the current theme settings to ImGui."""
|
||||
if _current_palette_name == "10x Dark":
|
||||
style = imgui.get_style()
|
||||
style.window_rounding = 4.0
|
||||
style.child_rounding = 4.0
|
||||
style.frame_rounding = 4.0
|
||||
style.grab_rounding = 4.0
|
||||
style.popup_rounding = 4.0
|
||||
|
||||
colors = style.colors
|
||||
colors[imgui.Col_.window_bg] = _c(25, 25, 25)
|
||||
colors[imgui.Col_.child_bg] = _c(30, 30, 30)
|
||||
colors[imgui.Col_.border] = _c(45, 45, 45)
|
||||
colors[imgui.Col_.frame_bg] = _c(40, 40, 40)
|
||||
colors[imgui.Col_.header] = _c(50, 50, 50)
|
||||
colors[imgui.Col_.header_hovered] = _c(70, 70, 70)
|
||||
colors[imgui.Col_.header_active] = _c(90, 90, 90)
|
||||
colors[imgui.Col_.button] = _c(50, 50, 50)
|
||||
colors[imgui.Col_.button_hovered] = _c(70, 70, 70)
|
||||
colors[imgui.Col_.button_active] = _c(100, 100, 100)
|
||||
colors[imgui.Col_.tab] = _c(35, 35, 35)
|
||||
colors[imgui.Col_.tab_hovered] = _c(60, 60, 60)
|
||||
colors[imgui.Col_.tab_active] = _c(50, 50, 50)
|
||||
colors[imgui.Col_.tab_unfocused] = _c(30, 30, 30)
|
||||
colors[imgui.Col_.tab_unfocused_active] = _c(45, 45, 45)
|
||||
colors[imgui.Col_.text] = _c(210, 210, 210)
|
||||
|
||||
def set_palette(name: str) -> None:
|
||||
global _current_palette_name
|
||||
_current_palette_name = name
|
||||
apply_current()
|
||||
|
||||
def save_to_config(config: dict) -> None:
|
||||
"""Persist theme settings into the config dict."""
|
||||
config.setdefault("theme", {})
|
||||
config["theme"]["palette"] = _current_palette_name
|
||||
config["theme"]["font_path"] = _current_font_path
|
||||
config["theme"]["font_size"] = _current_font_size
|
||||
|
||||
def load_from_config(config: dict) -> None:
|
||||
"""Read theme settings from config."""
|
||||
global _current_palette_name, _current_font_path, _current_font_size
|
||||
t = config.get("theme", {})
|
||||
_current_palette_name = t.get("palette", "10x Dark")
|
||||
_current_font_path = t.get("font_path", "")
|
||||
_current_font_size = float(t.get("font_size", 18.0))
|
||||
|
||||
from src.theme_nerv_fx import AlertPulsing, CRTFilter
|
||||
_alert_pulsing = AlertPulsing()
|
||||
_crt_filter = CRTFilter()
|
||||
|
||||
Reference in New Issue
Block a user