feat(ui): Improve text rendering clarity with 3x font oversampling
This commit is contained in:
18
config.toml
18
config.toml
@@ -1,6 +1,6 @@
|
||||
[ai]
|
||||
provider = "minimax"
|
||||
model = "MiniMax-M2.5"
|
||||
provider = "deepseek"
|
||||
model = "deepseek-chat"
|
||||
temperature = 0.0
|
||||
max_tokens = 24000
|
||||
history_trunc_limit = 900000
|
||||
@@ -9,6 +9,11 @@ system_prompt = ""
|
||||
[projects]
|
||||
paths = [
|
||||
"C:/projects/gencpp/gencpp_sloppy.toml",
|
||||
"C:\\projects\\manual_slop\\tests\\artifacts\\temp_livecontextsim.toml",
|
||||
"C:\\projects\\manual_slop\\tests\\artifacts\\temp_liveaisettingssim.toml",
|
||||
"C:\\projects\\manual_slop\\tests\\artifacts\\temp_livetoolssim.toml",
|
||||
"C:\\projects\\manual_slop\\tests\\artifacts\\temp_liveexecutionsim.toml",
|
||||
"C:\\projects\\manual_slop\\tests\\artifacts\\temp_project.toml",
|
||||
]
|
||||
active = "C:/projects/gencpp/gencpp_sloppy.toml"
|
||||
|
||||
@@ -16,6 +21,7 @@ active = "C:/projects/gencpp/gencpp_sloppy.toml"
|
||||
separate_message_panel = false
|
||||
separate_response_panel = false
|
||||
separate_tool_calls_panel = false
|
||||
bg_shader_enabled = true
|
||||
|
||||
[gui.show_windows]
|
||||
"Context Hub" = true
|
||||
@@ -28,12 +34,12 @@ separate_tool_calls_panel = false
|
||||
"Tier 4: QA" = true
|
||||
"Discussion Hub" = true
|
||||
"Operations Hub" = true
|
||||
Message = true
|
||||
Response = true
|
||||
"Tool Calls" = true
|
||||
Message = false
|
||||
Response = false
|
||||
"Tool Calls" = false
|
||||
Theme = true
|
||||
"Log Management" = true
|
||||
Diagnostics = true
|
||||
Diagnostics = false
|
||||
|
||||
[theme]
|
||||
palette = "DPG Default"
|
||||
|
||||
12
scripts/test_font_config.py
Normal file
12
scripts/test_font_config.py
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
from imgui_bundle import imgui, hello_imgui
|
||||
|
||||
def test_font_config():
|
||||
config = imgui.ImFontConfig()
|
||||
config.oversample_h = 3
|
||||
config.oversample_v = 3
|
||||
print(f"Oversample H: {config.oversample_h}")
|
||||
print(f"Oversample V: {config.oversample_v}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_font_config()
|
||||
@@ -324,7 +324,8 @@ class AppController:
|
||||
'manual_approve': 'ui_manual_approve',
|
||||
'inject_file_path': '_inject_file_path',
|
||||
'inject_mode': '_inject_mode',
|
||||
'show_inject_modal': '_show_inject_modal'
|
||||
'show_inject_modal': '_show_inject_modal',
|
||||
'bg_shader_enabled': 'bg_shader_enabled'
|
||||
}
|
||||
self._gettable_fields = dict(self._settable_fields)
|
||||
self._gettable_fields.update({
|
||||
@@ -346,7 +347,8 @@ class AppController:
|
||||
'_inject_file_path': '_inject_file_path',
|
||||
'_inject_mode': '_inject_mode',
|
||||
'_inject_preview': '_inject_preview',
|
||||
'_show_inject_modal': '_show_inject_modal'
|
||||
'_show_inject_modal': '_show_inject_modal',
|
||||
'bg_shader_enabled': 'bg_shader_enabled'
|
||||
})
|
||||
self.perf_monitor = performance_monitor.get_monitor()
|
||||
self._perf_profiling_enabled = False
|
||||
@@ -783,6 +785,11 @@ class AppController:
|
||||
self.ui_summary_only = proj_meta.get("summary_only", False)
|
||||
self.ui_auto_add_history = disc_sec.get("auto_add", False)
|
||||
self.ui_global_system_prompt = self.config.get("ai", {}).get("system_prompt", "")
|
||||
|
||||
gui_cfg = self.config.get("gui", {})
|
||||
from src import bg_shader
|
||||
bg_shader.get_bg().enabled = gui_cfg.get("bg_shader_enabled", False)
|
||||
|
||||
_default_windows = {
|
||||
"Context Hub": True,
|
||||
"Files & Media": True,
|
||||
@@ -2077,12 +2084,15 @@ class AppController:
|
||||
}
|
||||
self.config["ai"]["system_prompt"] = self.ui_global_system_prompt
|
||||
self.config["projects"] = {"paths": self.project_paths, "active": self.active_project_path}
|
||||
from src import bg_shader
|
||||
self.config["gui"] = {
|
||||
"show_windows": self.show_windows,
|
||||
"separate_message_panel": getattr(self, "ui_separate_message_panel", False),
|
||||
"separate_response_panel": getattr(self, "ui_separate_response_panel", False),
|
||||
"separate_tool_calls_panel": getattr(self, "ui_separate_tool_calls_panel", False),
|
||||
"bg_shader_enabled": bg_shader.get_bg().enabled
|
||||
}
|
||||
# Explicitly call theme save to ensure self.config is updated
|
||||
theme.save_to_config(self.config)
|
||||
|
||||
def _do_generate(self) -> tuple[str, Path, list[dict[str, Any]], str, str]:
|
||||
|
||||
65
src/bg_shader.py
Normal file
65
src/bg_shader.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# src/bg_shader.py
|
||||
import time
|
||||
import math
|
||||
from typing import Optional
|
||||
import numpy as np
|
||||
from imgui_bundle import imgui, nanovg as nvg, hello_imgui
|
||||
|
||||
class BackgroundShader:
|
||||
def __init__(self):
|
||||
self.enabled = False
|
||||
self.start_time = time.time()
|
||||
self.ctx: Optional[nvg.Context] = None
|
||||
|
||||
def render(self, width: float, height: float):
|
||||
if not self.enabled:
|
||||
return
|
||||
|
||||
# In imgui-bundle, hello_imgui handles the background.
|
||||
# We can use the background_draw_list to draw primitives.
|
||||
# Since we don't have raw GLSL easily in Python without PyOpenGL,
|
||||
# we'll use a "faux-shader" approach with NanoVG or DrawList gradients.
|
||||
|
||||
t = time.time() - self.start_time
|
||||
dl = imgui.get_background_draw_list()
|
||||
|
||||
# Base deep sea color
|
||||
dl.add_rect_filled(imgui.ImVec2(0, 0), imgui.ImVec2(width, height), imgui.get_color_u32(imgui.ImVec4(0.01, 0.07, 0.20, 1.0)))
|
||||
|
||||
# Layer 1: Slow moving large blobs (FBM approximation)
|
||||
for i in range(3):
|
||||
phase = t * (0.1 + i * 0.05)
|
||||
x = (math.sin(phase) * 0.5 + 0.5) * width
|
||||
y = (math.cos(phase * 0.8) * 0.5 + 0.5) * height
|
||||
radius = (0.4 + 0.2 * math.sin(t * 0.2)) * max(width, height)
|
||||
|
||||
col = imgui.ImVec4(0.02, 0.26, 0.55, 0.3)
|
||||
dl.add_circle_filled(imgui.ImVec2(x, y), radius, imgui.get_color_u32(col), num_segments=32)
|
||||
|
||||
# Layer 2: Shimmering caustics (Animated Lines)
|
||||
num_lines = 15
|
||||
for i in range(num_lines):
|
||||
offset = (t * 20.0 + i * (width / num_lines)) % width
|
||||
alpha = 0.1 * (1.0 + math.sin(t + i))
|
||||
col = imgui.get_color_u32(imgui.ImVec4(0.08, 0.60, 0.88, alpha))
|
||||
|
||||
p1 = imgui.ImVec2(offset, 0)
|
||||
p2 = imgui.ImVec2(offset - 100, height)
|
||||
dl.add_line(p1, p2, col, thickness=2.0)
|
||||
|
||||
# Vignette
|
||||
center = imgui.ImVec2(width/2, height/2)
|
||||
radius = max(width, height) * 0.8
|
||||
# Draw multiple concentric circles for a soft vignette
|
||||
for i in range(10):
|
||||
r = radius + (i * 50)
|
||||
alpha = (i / 10.0) * 0.5
|
||||
dl.add_circle(center, r, imgui.get_color_u32(imgui.ImVec4(0, 0, 0, alpha)), num_segments=64, thickness=60.0)
|
||||
|
||||
_bg: Optional[BackgroundShader] = None
|
||||
|
||||
def get_bg():
|
||||
global _bg
|
||||
if _bg is None:
|
||||
_bg = BackgroundShader()
|
||||
return _bg
|
||||
55
src/gui_2.py
55
src/gui_2.py
@@ -23,6 +23,7 @@ from src import models
|
||||
from src import app_controller
|
||||
from src import mcp_client
|
||||
from src import markdown_helper
|
||||
from src import bg_shader
|
||||
import re
|
||||
|
||||
from pydantic import BaseModel
|
||||
@@ -287,6 +288,12 @@ class App:
|
||||
imgui.end_menu()
|
||||
|
||||
def _gui_func(self) -> None:
|
||||
# Render background shader
|
||||
bg = bg_shader.get_bg()
|
||||
if bg.enabled:
|
||||
ws = imgui.get_io().display_size
|
||||
bg.render(ws.x, ws.y)
|
||||
|
||||
pushed_prior_tint = False
|
||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_gui_func")
|
||||
if self.is_viewing_prior_session:
|
||||
@@ -2780,6 +2787,8 @@ def hello():
|
||||
for p in theme.get_palette_names():
|
||||
if imgui.selectable(p, p == cp)[0]:
|
||||
theme.apply(p)
|
||||
self._flush_to_config()
|
||||
models.save_config(self.config)
|
||||
imgui.end_combo()
|
||||
|
||||
imgui.separator()
|
||||
@@ -2815,7 +2824,33 @@ def hello():
|
||||
imgui.separator()
|
||||
imgui.text("UI Scale (DPI)")
|
||||
ch, scale = imgui.slider_float("##scale", theme.get_current_scale(), 0.5, 3.0, "%.2f")
|
||||
if ch: theme.set_scale(scale)
|
||||
if ch:
|
||||
theme.set_scale(scale)
|
||||
self._flush_to_config()
|
||||
models.save_config(self.config)
|
||||
|
||||
imgui.text("Panel Transparency")
|
||||
ch_t, trans = imgui.slider_float("##trans", theme.get_transparency(), 0.1, 1.0, "%.2f")
|
||||
if ch_t:
|
||||
theme.set_transparency(trans)
|
||||
self._flush_to_config()
|
||||
models.save_config(self.config)
|
||||
|
||||
imgui.text("Panel Item Transparency")
|
||||
ch_ct, ctrans = imgui.slider_float("##ctrans", theme.get_child_transparency(), 0.1, 1.0, "%.2f")
|
||||
if ch_ct:
|
||||
theme.set_child_transparency(ctrans)
|
||||
self._flush_to_config()
|
||||
models.save_config(self.config)
|
||||
|
||||
imgui.separator()
|
||||
bg = bg_shader.get_bg()
|
||||
ch_bg, bg.enabled = imgui.checkbox("Animated Background Shader", bg.enabled)
|
||||
if ch_bg:
|
||||
gui_cfg = self.config.setdefault("gui", {})
|
||||
gui_cfg["bg_shader_enabled"] = bg.enabled
|
||||
self._flush_to_config()
|
||||
models.save_config(self.config)
|
||||
imgui.end()
|
||||
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_theme_panel")
|
||||
|
||||
@@ -2825,12 +2860,17 @@ def hello():
|
||||
if assets_dir.exists():
|
||||
hello_imgui.set_assets_folder(str(assets_dir.absolute()))
|
||||
|
||||
# Improved font rendering with oversampling
|
||||
config = imgui.ImFontConfig()
|
||||
config.oversample_h = 3
|
||||
config.oversample_v = 3
|
||||
|
||||
font_path, font_size = theme.get_font_loading_params()
|
||||
|
||||
if font_path:
|
||||
# Just try loading it directly; hello_imgui will look in the assets folder
|
||||
try:
|
||||
self.main_font = hello_imgui.load_font_ttf_with_font_awesome_icons(font_path, font_size)
|
||||
self.main_font = hello_imgui.load_font_ttf_with_font_awesome_icons(font_path, font_size, config)
|
||||
except Exception as e:
|
||||
print(f"Failed to load main font {font_path}: {e}")
|
||||
self.main_font = None
|
||||
@@ -2838,7 +2878,8 @@ def hello():
|
||||
self.main_font = None
|
||||
|
||||
try:
|
||||
self.mono_font = hello_imgui.load_font("fonts/MapleMono-Regular.ttf", font_size)
|
||||
params = hello_imgui.FontLoadingParams(font_config=config)
|
||||
self.mono_font = hello_imgui.load_font("fonts/MapleMono-Regular.ttf", font_size, params)
|
||||
except Exception as e:
|
||||
print(f"Failed to load mono font: {e}")
|
||||
self.mono_font = None
|
||||
@@ -2862,7 +2903,13 @@ def hello():
|
||||
self.runner_params.app_window_params.window_title = "manual slop"
|
||||
self.runner_params.app_window_params.window_geometry.size = (1680, 1200)
|
||||
self.runner_params.imgui_window_params.enable_viewports = getattr(self, "ui_multi_viewport", False)
|
||||
self.runner_params.imgui_window_params.remember_theme = True
|
||||
self.runner_params.imgui_window_params.tweaked_theme = theme.get_tweaked_theme()
|
||||
self.runner_params.imgui_window_params.default_imgui_window_type = hello_imgui.DefaultImGuiWindowType.provide_full_screen_dock_space
|
||||
|
||||
# Enforce DPI Awareness and User Scale
|
||||
user_scale = theme.get_current_scale()
|
||||
self.runner_params.dpi_aware_params.dpi_window_size_factor = user_scale
|
||||
|
||||
# Detect Monitor Refresh Rate for capping
|
||||
fps_cap = 60.0
|
||||
@@ -2879,11 +2926,13 @@ def hello():
|
||||
self.runner_params.fps_idling.fps_idle = fps_cap
|
||||
|
||||
self.runner_params.imgui_window_params.show_menu_bar = True
|
||||
self.runner_params.imgui_window_params.show_menu_view_themes = True
|
||||
self.runner_params.ini_folder_type = hello_imgui.IniFolderType.current_folder
|
||||
self.runner_params.ini_filename = "manualslop_layout.ini"
|
||||
self.runner_params.callbacks.show_gui = self._gui_func
|
||||
self.runner_params.callbacks.show_menus = self._show_menus
|
||||
self.runner_params.callbacks.load_additional_fonts = self._load_fonts
|
||||
self.runner_params.callbacks.setup_imgui_style = theme.apply_current
|
||||
self.runner_params.callbacks.post_init = self._post_init
|
||||
self._fetch_models(self.current_provider)
|
||||
md_options = markdown_helper.get_renderer().options
|
||||
|
||||
153
src/theme_2.py
153
src/theme_2.py
@@ -3,12 +3,13 @@
|
||||
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.
|
||||
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
|
||||
from imgui_bundle import imgui, hello_imgui
|
||||
from typing import Any, Optional
|
||||
|
||||
# ------------------------------------------------------------------ palettes
|
||||
|
||||
@@ -173,23 +174,68 @@ _PALETTES: dict[str, dict[int, tuple]] = {
|
||||
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),
|
||||
},
|
||||
}
|
||||
|
||||
PALETTE_NAMES: list[str] = list(_PALETTES.keys())
|
||||
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 = "ImGui Dark"
|
||||
_current_font_path: str = ""
|
||||
_current_palette: str = "10x Dark"
|
||||
_current_font_path: str = "fonts/Inter-Regular.ttf"
|
||||
_current_font_size: float = 16.0
|
||||
_current_scale: float = 1.0
|
||||
_custom_font: imgui.ImFont = None # type: ignore
|
||||
_transparency: float = 1.0
|
||||
_child_transparency: float = 1.0
|
||||
|
||||
# ------------------------------------------------------------------ public API
|
||||
|
||||
def get_palette_names() -> list[str]:
|
||||
return list(_PALETTES.keys())
|
||||
|
||||
def get_current_palette() -> str:
|
||||
return _current_palette
|
||||
|
||||
@@ -202,18 +248,49 @@ def get_current_font_size() -> float:
|
||||
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.
|
||||
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, {})
|
||||
style = imgui.get_style()
|
||||
|
||||
# 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()
|
||||
|
||||
# Subtle Rounding Professional Theme
|
||||
# 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
|
||||
@@ -225,6 +302,17 @@ def apply(palette_name: str) -> None:
|
||||
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)
|
||||
@@ -237,16 +325,6 @@ def apply(palette_name: str) -> None:
|
||||
style.anti_aliased_fill = True
|
||||
style.anti_aliased_lines_use_tex = True
|
||||
|
||||
if not colours:
|
||||
# Reset to imgui dark defaults
|
||||
imgui.style_colors_dark()
|
||||
return
|
||||
|
||||
# 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) -> None:
|
||||
"""Set the global font/UI scale factor."""
|
||||
global _current_scale
|
||||
@@ -261,17 +339,22 @@ def save_to_config(config: dict) -> None:
|
||||
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] from config and apply palette + scale. Font is handled separately at startup."""
|
||||
global _current_font_path, _current_font_size, _current_scale, _current_palette
|
||||
"""Read [theme] from config. Font is handled separately at startup."""
|
||||
global _current_font_path, _current_font_size, _current_scale, _current_palette, _transparency, _child_transparency
|
||||
t = config.get("theme", {})
|
||||
_current_palette = t.get("palette", "ImGui Dark")
|
||||
_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))
|
||||
# Don't apply here — imgui context may not exist yet.
|
||||
# Call apply_current() after imgui is initialised.
|
||||
_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."""
|
||||
@@ -281,3 +364,15 @@ def apply_current() -> None:
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user