feat(gui): Implement frosted glass UI controls and config persistence
This commit is contained in:
157
src/gui_2.py
157
src/gui_2.py
@@ -21,6 +21,7 @@ from src import theme_2 as theme
|
|||||||
from src import theme_nerv_fx as theme_fx
|
from src import theme_nerv_fx as theme_fx
|
||||||
from src import api_hooks
|
from src import api_hooks
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import OpenGL.GL as gl
|
||||||
from src import log_registry
|
from src import log_registry
|
||||||
from src import log_pruner
|
from src import log_pruner
|
||||||
from src import models
|
from src import models
|
||||||
@@ -28,6 +29,7 @@ from src import app_controller
|
|||||||
from src import mcp_client
|
from src import mcp_client
|
||||||
from src import markdown_helper
|
from src import markdown_helper
|
||||||
from src import bg_shader
|
from src import bg_shader
|
||||||
|
from src.shader_manager import ShaderManager
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
@@ -211,7 +213,16 @@ class App:
|
|||||||
self._nerv_flicker = theme_fx.StatusFlicker()
|
self._nerv_flicker = theme_fx.StatusFlicker()
|
||||||
self.ui_tool_filter_category = "All"
|
self.ui_tool_filter_category = "All"
|
||||||
self.ui_discussion_split_h = 300.0
|
self.ui_discussion_split_h = 300.0
|
||||||
self.shader_uniforms = {'crt': 1.0, 'scanline': 0.5, 'bloom': 0.8}
|
self.shader_uniforms = {
|
||||||
|
'crt': 1.0,
|
||||||
|
'scanline': 0.5,
|
||||||
|
'bloom': 0.8,
|
||||||
|
'frosted_blur_radius': theme.get_frosted_blur_radius(),
|
||||||
|
'frosted_tint_intensity': theme.get_frosted_tint_intensity(),
|
||||||
|
'frosted_opacity': theme.get_frosted_opacity()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.shader_manager = ShaderManager()
|
||||||
|
|
||||||
def _handle_approve_tool(self, user_data=None) -> None:
|
def _handle_approve_tool(self, user_data=None) -> None:
|
||||||
"""UI-level wrapper for approving a pending tool execution ask."""
|
"""UI-level wrapper for approving a pending tool execution ask."""
|
||||||
@@ -341,6 +352,84 @@ class App:
|
|||||||
imgui.pop_style_var(2)
|
imgui.pop_style_var(2)
|
||||||
imgui.pop_id()
|
imgui.pop_id()
|
||||||
|
|
||||||
|
def _render_frosted_background(self, pos: imgui.ImVec2, size: imgui.ImVec2) -> None:
|
||||||
|
if size.x <= 0 or size.y <= 0: return
|
||||||
|
if self.shader_manager.fbo_width != int(size.x) or self.shader_manager.fbo_height != int(size.y):
|
||||||
|
self.shader_manager.setup_capture_fbo(int(size.x), int(size.y))
|
||||||
|
display_size = imgui.get_io().display_size
|
||||||
|
gl.glBindTexture(gl.GL_TEXTURE_2D, self.shader_manager.capture_tex)
|
||||||
|
gl_y = int(display_size.y - pos.y - size.y)
|
||||||
|
gl.glCopyTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, int(pos.x), gl_y, int(size.x), int(size.y), 0)
|
||||||
|
gl.glBindTexture(gl.GL_TEXTURE_2D, 0)
|
||||||
|
self.shader_manager.capture_begin(int(size.x), int(size.y))
|
||||||
|
self.shader_manager.render_blur(
|
||||||
|
self.shader_manager.capture_tex,
|
||||||
|
int(size.x),
|
||||||
|
int(size.y),
|
||||||
|
self.shader_uniforms['frosted_blur_radius'],
|
||||||
|
self.shader_uniforms['frosted_tint_intensity'],
|
||||||
|
self.shader_uniforms['frosted_opacity']
|
||||||
|
)
|
||||||
|
self.shader_manager.capture_end()
|
||||||
|
imgui.get_background_draw_list().add_image(
|
||||||
|
self.shader_manager.blur_tex,
|
||||||
|
pos,
|
||||||
|
imgui.ImVec2(pos.x + size.x, pos.y + size.y)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _render_operations_hub(self) -> None:
|
||||||
|
exp, opened = imgui.begin("Operations Hub", self.show_windows["Operations Hub"])
|
||||||
|
self.show_windows["Operations Hub"] = bool(opened)
|
||||||
|
if exp:
|
||||||
|
self._render_frosted_background(imgui.get_window_pos(), imgui.get_window_size())
|
||||||
|
imgui.text("Focus Agent:")
|
||||||
|
imgui.same_line()
|
||||||
|
focus_label = self.ui_focus_agent or "All"
|
||||||
|
if imgui.begin_combo("##focus_agent", focus_label, imgui.ComboFlags_.width_fit_preview):
|
||||||
|
if imgui.selectable("All", self.ui_focus_agent is None)[0]:
|
||||||
|
self.ui_focus_agent = None
|
||||||
|
for tier in ["Tier 2", "Tier 3", "Tier 4"]:
|
||||||
|
if imgui.selectable(tier, self.ui_focus_agent == tier)[0]:
|
||||||
|
self.ui_focus_agent = tier
|
||||||
|
imgui.end_combo()
|
||||||
|
imgui.same_line()
|
||||||
|
if self.ui_focus_agent:
|
||||||
|
if imgui.button("x##clear_focus"):
|
||||||
|
self.ui_focus_agent = None
|
||||||
|
|
||||||
|
imgui.push_style_var(imgui.StyleVar_.item_spacing, imgui.ImVec2(10, 4))
|
||||||
|
ch1, self.ui_separate_tool_calls_panel = imgui.checkbox("Pop Out Tool Calls", self.ui_separate_tool_calls_panel)
|
||||||
|
if ch1: self.show_windows["Tool Calls"] = self.ui_separate_tool_calls_panel
|
||||||
|
imgui.same_line()
|
||||||
|
ch2, self.ui_separate_usage_analytics = imgui.checkbox("Pop Out Usage Analytics", self.ui_separate_usage_analytics)
|
||||||
|
if ch2: self.show_windows["Usage Analytics"] = self.ui_separate_usage_analytics
|
||||||
|
imgui.same_line()
|
||||||
|
ch3, self.ui_separate_external_tools = imgui.checkbox('Pop Out External Tools', self.ui_separate_external_tools)
|
||||||
|
if ch3: self.show_windows['External Tools'] = self.ui_separate_external_tools
|
||||||
|
imgui.pop_style_var()
|
||||||
|
|
||||||
|
show_tc_tab = not self.ui_separate_tool_calls_panel
|
||||||
|
show_usage_tab = not self.ui_separate_usage_analytics
|
||||||
|
|
||||||
|
if imgui.begin_tab_bar("ops_tabs"):
|
||||||
|
if imgui.begin_tab_item("Comms History")[0]:
|
||||||
|
self._render_comms_history_panel()
|
||||||
|
imgui.end_tab_item()
|
||||||
|
if show_tc_tab:
|
||||||
|
if imgui.begin_tab_item("Tool Calls")[0]:
|
||||||
|
self._render_tool_calls_panel()
|
||||||
|
imgui.end_tab_item()
|
||||||
|
if show_usage_tab:
|
||||||
|
if imgui.begin_tab_item("Usage Analytics")[0]:
|
||||||
|
self._render_usage_analytics_panel()
|
||||||
|
imgui.end_tab_item()
|
||||||
|
if not self.ui_separate_external_tools:
|
||||||
|
if imgui.begin_tab_item("External Tools")[0]:
|
||||||
|
self._render_external_tools_panel()
|
||||||
|
imgui.end_tab_item()
|
||||||
|
imgui.end_tab_bar()
|
||||||
|
imgui.end()
|
||||||
|
|
||||||
def _show_menus(self) -> None:
|
def _show_menus(self) -> None:
|
||||||
if imgui.begin_menu("manual slop"):
|
if imgui.begin_menu("manual slop"):
|
||||||
if imgui.menu_item("Quit", "Ctrl+Q", False)[0]:
|
if imgui.menu_item("Quit", "Ctrl+Q", False)[0]:
|
||||||
@@ -440,6 +529,17 @@ class App:
|
|||||||
changed_crt, self.shader_uniforms['crt'] = imgui.slider_float('CRT Curvature', self.shader_uniforms['crt'], 0.0, 2.0)
|
changed_crt, self.shader_uniforms['crt'] = imgui.slider_float('CRT Curvature', self.shader_uniforms['crt'], 0.0, 2.0)
|
||||||
changed_scan, self.shader_uniforms['scanline'] = imgui.slider_float('Scanline Intensity', self.shader_uniforms['scanline'], 0.0, 1.0)
|
changed_scan, self.shader_uniforms['scanline'] = imgui.slider_float('Scanline Intensity', self.shader_uniforms['scanline'], 0.0, 1.0)
|
||||||
changed_bloom, self.shader_uniforms['bloom'] = imgui.slider_float('Bloom Threshold', self.shader_uniforms['bloom'], 0.0, 1.0)
|
changed_bloom, self.shader_uniforms['bloom'] = imgui.slider_float('Bloom Threshold', self.shader_uniforms['bloom'], 0.0, 1.0)
|
||||||
|
|
||||||
|
imgui.separator()
|
||||||
|
imgui.text("Frosted Glass")
|
||||||
|
changed_fbr, self.shader_uniforms['frosted_blur_radius'] = imgui.slider_float('Blur Radius', self.shader_uniforms['frosted_blur_radius'], 0.0, 32.0)
|
||||||
|
if changed_fbr: theme.set_frosted_blur_radius(self.shader_uniforms['frosted_blur_radius'])
|
||||||
|
|
||||||
|
changed_fti, self.shader_uniforms['frosted_tint_intensity'] = imgui.slider_float('Tint Intensity', self.shader_uniforms['frosted_tint_intensity'], 0.0, 1.0)
|
||||||
|
if changed_fti: theme.set_frosted_tint_intensity(self.shader_uniforms['frosted_tint_intensity'])
|
||||||
|
|
||||||
|
changed_fo, self.shader_uniforms['frosted_opacity'] = imgui.slider_float('Frosted Opacity', self.shader_uniforms['frosted_opacity'], 0.0, 1.0)
|
||||||
|
if changed_fo: theme.set_frosted_opacity(self.shader_uniforms['frosted_opacity'])
|
||||||
imgui.end()
|
imgui.end()
|
||||||
|
|
||||||
def _gui_func(self) -> None:
|
def _gui_func(self) -> None:
|
||||||
@@ -660,56 +760,7 @@ class App:
|
|||||||
|
|
||||||
imgui.end()
|
imgui.end()
|
||||||
if self.show_windows.get("Operations Hub", False):
|
if self.show_windows.get("Operations Hub", False):
|
||||||
exp, opened = imgui.begin("Operations Hub", self.show_windows["Operations Hub"])
|
self._render_operations_hub()
|
||||||
self.show_windows["Operations Hub"] = bool(opened)
|
|
||||||
if exp:
|
|
||||||
imgui.text("Focus Agent:")
|
|
||||||
imgui.same_line()
|
|
||||||
focus_label = self.ui_focus_agent or "All"
|
|
||||||
if imgui.begin_combo("##focus_agent", focus_label, imgui.ComboFlags_.width_fit_preview):
|
|
||||||
if imgui.selectable("All", self.ui_focus_agent is None)[0]:
|
|
||||||
self.ui_focus_agent = None
|
|
||||||
for tier in ["Tier 2", "Tier 3", "Tier 4"]:
|
|
||||||
if imgui.selectable(tier, self.ui_focus_agent == tier)[0]:
|
|
||||||
self.ui_focus_agent = tier
|
|
||||||
imgui.end_combo()
|
|
||||||
imgui.same_line()
|
|
||||||
if self.ui_focus_agent:
|
|
||||||
if imgui.button("x##clear_focus"):
|
|
||||||
self.ui_focus_agent = None
|
|
||||||
if exp:
|
|
||||||
imgui.push_style_var(imgui.StyleVar_.item_spacing, imgui.ImVec2(10, 4))
|
|
||||||
ch1, self.ui_separate_tool_calls_panel = imgui.checkbox("Pop Out Tool Calls", self.ui_separate_tool_calls_panel)
|
|
||||||
if ch1: self.show_windows["Tool Calls"] = self.ui_separate_tool_calls_panel
|
|
||||||
imgui.same_line()
|
|
||||||
ch2, self.ui_separate_usage_analytics = imgui.checkbox("Pop Out Usage Analytics", self.ui_separate_usage_analytics)
|
|
||||||
if ch2: self.show_windows["Usage Analytics"] = self.ui_separate_usage_analytics
|
|
||||||
imgui.same_line()
|
|
||||||
ch3, self.ui_separate_external_tools = imgui.checkbox('Pop Out External Tools', self.ui_separate_external_tools)
|
|
||||||
if ch3: self.show_windows['External Tools'] = self.ui_separate_external_tools
|
|
||||||
imgui.pop_style_var()
|
|
||||||
|
|
||||||
show_tc_tab = not self.ui_separate_tool_calls_panel
|
|
||||||
show_usage_tab = not self.ui_separate_usage_analytics
|
|
||||||
|
|
||||||
if imgui.begin_tab_bar("ops_tabs"):
|
|
||||||
if imgui.begin_tab_item("Comms History")[0]:
|
|
||||||
self._render_comms_history_panel()
|
|
||||||
imgui.end_tab_item()
|
|
||||||
if show_tc_tab:
|
|
||||||
if imgui.begin_tab_item("Tool Calls")[0]:
|
|
||||||
self._render_tool_calls_panel()
|
|
||||||
imgui.end_tab_item()
|
|
||||||
if show_usage_tab:
|
|
||||||
if imgui.begin_tab_item("Usage Analytics")[0]:
|
|
||||||
self._render_usage_analytics_panel()
|
|
||||||
imgui.end_tab_item()
|
|
||||||
if not self.ui_separate_external_tools:
|
|
||||||
if imgui.begin_tab_item("External Tools")[0]:
|
|
||||||
self._render_external_tools_panel()
|
|
||||||
imgui.end_tab_item()
|
|
||||||
imgui.end_tab_bar()
|
|
||||||
imgui.end()
|
|
||||||
|
|
||||||
if self.ui_separate_message_panel and self.show_windows.get("Message", False):
|
if self.ui_separate_message_panel and self.show_windows.get("Message", False):
|
||||||
exp, opened = imgui.begin("Message", self.show_windows["Message"])
|
exp, opened = imgui.begin("Message", self.show_windows["Message"])
|
||||||
@@ -1904,7 +1955,6 @@ class App:
|
|||||||
imgui.text(f"History: {key}")
|
imgui.text(f"History: {key}")
|
||||||
hist_data = self.perf_monitor.get_history(key)
|
hist_data = self.perf_monitor.get_history(key)
|
||||||
if hist_data:
|
if hist_data:
|
||||||
import numpy as np
|
|
||||||
imgui.plot_lines(f"##plot_{key}", np.array(hist_data, dtype=np.float32), graph_size=imgui.ImVec2(-1, 60))
|
imgui.plot_lines(f"##plot_{key}", np.array(hist_data, dtype=np.float32), graph_size=imgui.ImVec2(-1, 60))
|
||||||
else:
|
else:
|
||||||
imgui.text_disabled(f"(no history data for {key})")
|
imgui.text_disabled(f"(no history data for {key})")
|
||||||
@@ -2052,6 +2102,7 @@ def hello():
|
|||||||
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_screenshots_panel")
|
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_screenshots_panel")
|
||||||
|
|
||||||
def _render_discussion_panel(self) -> None:
|
def _render_discussion_panel(self) -> None:
|
||||||
|
self._render_frosted_background(imgui.get_window_pos(), imgui.get_window_size())
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_discussion_panel")
|
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_discussion_panel")
|
||||||
# THINKING indicator
|
# THINKING indicator
|
||||||
is_thinking = self.ai_status in ['sending...', 'streaming...', 'running powershell...']
|
is_thinking = self.ai_status in ['sending...', 'streaming...', 'running powershell...']
|
||||||
@@ -3993,6 +4044,8 @@ def hello():
|
|||||||
|
|
||||||
def _post_init(self) -> None:
|
def _post_init(self) -> None:
|
||||||
theme.apply_current()
|
theme.apply_current()
|
||||||
|
self.shader_manager.setup_background_shader()
|
||||||
|
self.shader_manager.setup_frosted_glass_shader()
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
"""Initializes the ImGui runner and starts the main application loop."""
|
"""Initializes the ImGui runner and starts the main application loop."""
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class ShaderManager:
|
|||||||
self.blur_program = None
|
self.blur_program = None
|
||||||
self.capture_fbo = None
|
self.capture_fbo = None
|
||||||
self.capture_tex = None
|
self.capture_tex = None
|
||||||
|
self.blur_tex = None
|
||||||
self.fbo_width = 0
|
self.fbo_width = 0
|
||||||
self.fbo_height = 0
|
self.fbo_height = 0
|
||||||
|
|
||||||
@@ -16,14 +17,21 @@ class ShaderManager:
|
|||||||
gl.glDeleteFramebuffers(1, [self.capture_fbo])
|
gl.glDeleteFramebuffers(1, [self.capture_fbo])
|
||||||
if self.capture_tex is not None:
|
if self.capture_tex is not None:
|
||||||
gl.glDeleteTextures(1, [self.capture_tex])
|
gl.glDeleteTextures(1, [self.capture_tex])
|
||||||
|
if self.blur_tex is not None:
|
||||||
|
gl.glDeleteTextures(1, [self.blur_tex])
|
||||||
self.capture_fbo = gl.glGenFramebuffers(1)
|
self.capture_fbo = gl.glGenFramebuffers(1)
|
||||||
self.capture_tex = gl.glGenTextures(1)
|
self.capture_tex = gl.glGenTextures(1)
|
||||||
gl.glBindTexture(gl.GL_TEXTURE_2D, self.capture_tex)
|
gl.glBindTexture(gl.GL_TEXTURE_2D, self.capture_tex)
|
||||||
gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, width, height, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, None)
|
gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, width, height, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, None)
|
||||||
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR)
|
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR)
|
||||||
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)
|
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)
|
||||||
|
self.blur_tex = gl.glGenTextures(1)
|
||||||
|
gl.glBindTexture(gl.GL_TEXTURE_2D, self.blur_tex)
|
||||||
|
gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, width, height, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, None)
|
||||||
|
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR)
|
||||||
|
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)
|
||||||
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.capture_fbo)
|
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.capture_fbo)
|
||||||
gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0, gl.GL_TEXTURE_2D, self.capture_tex, 0)
|
gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0, gl.GL_TEXTURE_2D, self.blur_tex, 0)
|
||||||
if gl.glCheckFramebufferStatus(gl.GL_FRAMEBUFFER) != gl.GL_FRAMEBUFFER_COMPLETE:
|
if gl.glCheckFramebufferStatus(gl.GL_FRAMEBUFFER) != gl.GL_FRAMEBUFFER_COMPLETE:
|
||||||
raise RuntimeError("Framebuffer not complete")
|
raise RuntimeError("Framebuffer not complete")
|
||||||
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)
|
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)
|
||||||
@@ -232,3 +240,25 @@ void main() {
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
self.blur_program = self.compile_shader(vertex_src, fragment_src)
|
self.blur_program = self.compile_shader(vertex_src, fragment_src)
|
||||||
|
|
||||||
|
def render_blur(self, texture_id, width, height, radius, tint, opacity):
|
||||||
|
if not self.blur_program:
|
||||||
|
return
|
||||||
|
gl.glUseProgram(self.blur_program)
|
||||||
|
gl.glActiveTexture(gl.GL_TEXTURE0)
|
||||||
|
gl.glBindTexture(gl.GL_TEXTURE_2D, texture_id)
|
||||||
|
u_tex_loc = gl.glGetUniformLocation(self.blur_program, "u_texture")
|
||||||
|
if u_tex_loc != -1:
|
||||||
|
gl.glUniform1i(u_tex_loc, 0)
|
||||||
|
u_radius_loc = gl.glGetUniformLocation(self.blur_program, "u_blur_radius")
|
||||||
|
if u_radius_loc != -1:
|
||||||
|
gl.glUniform1f(u_radius_loc, float(radius))
|
||||||
|
u_tint_loc = gl.glGetUniformLocation(self.blur_program, "u_tint_intensity")
|
||||||
|
if u_tint_loc != -1:
|
||||||
|
gl.glUniform1f(u_tint_loc, float(tint))
|
||||||
|
u_opacity_loc = gl.glGetUniformLocation(self.blur_program, "u_opacity")
|
||||||
|
if u_opacity_loc != -1:
|
||||||
|
gl.glUniform1f(u_opacity_loc, float(opacity))
|
||||||
|
gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4)
|
||||||
|
gl.glBindTexture(gl.GL_TEXTURE_2D, 0)
|
||||||
|
gl.glUseProgram(0)
|
||||||
|
|||||||
@@ -235,6 +235,9 @@ _current_font_size: float = 16.0
|
|||||||
_current_scale: float = 1.0
|
_current_scale: float = 1.0
|
||||||
_transparency: float = 1.0
|
_transparency: float = 1.0
|
||||||
_child_transparency: float = 1.0
|
_child_transparency: float = 1.0
|
||||||
|
_frosted_blur_radius: float = 8.0
|
||||||
|
_frosted_tint_intensity: float = 0.1
|
||||||
|
_frosted_opacity: float = 1.0
|
||||||
|
|
||||||
# ------------------------------------------------------------------ public API
|
# ------------------------------------------------------------------ public API
|
||||||
|
|
||||||
@@ -269,6 +272,27 @@ def set_child_transparency(val: float) -> None:
|
|||||||
_child_transparency = val
|
_child_transparency = val
|
||||||
apply(_current_palette)
|
apply(_current_palette)
|
||||||
|
|
||||||
|
def get_frosted_blur_radius() -> float:
|
||||||
|
return _frosted_blur_radius
|
||||||
|
|
||||||
|
def set_frosted_blur_radius(val: float) -> None:
|
||||||
|
global _frosted_blur_radius
|
||||||
|
_frosted_blur_radius = val
|
||||||
|
|
||||||
|
def get_frosted_tint_intensity() -> float:
|
||||||
|
return _frosted_tint_intensity
|
||||||
|
|
||||||
|
def set_frosted_tint_intensity(val: float) -> None:
|
||||||
|
global _frosted_tint_intensity
|
||||||
|
_frosted_tint_intensity = val
|
||||||
|
|
||||||
|
def get_frosted_opacity() -> float:
|
||||||
|
return _frosted_opacity
|
||||||
|
|
||||||
|
def set_frosted_opacity(val: float) -> None:
|
||||||
|
global _frosted_opacity
|
||||||
|
_frosted_opacity = val
|
||||||
|
|
||||||
def apply(palette_name: str) -> None:
|
def apply(palette_name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Apply a named palette by setting all ImGui style colors and applying global professional styling.
|
Apply a named palette by setting all ImGui style colors and applying global professional styling.
|
||||||
@@ -350,13 +374,16 @@ def save_to_config(config: dict) -> None:
|
|||||||
config["theme"]["scale"] = _current_scale
|
config["theme"]["scale"] = _current_scale
|
||||||
config["theme"]["transparency"] = _transparency
|
config["theme"]["transparency"] = _transparency
|
||||||
config["theme"]["child_transparency"] = _child_transparency
|
config["theme"]["child_transparency"] = _child_transparency
|
||||||
|
config["theme"]["frosted_blur_radius"] = _frosted_blur_radius
|
||||||
|
config["theme"]["frosted_tint_intensity"] = _frosted_tint_intensity
|
||||||
|
config["theme"]["frosted_opacity"] = _frosted_opacity
|
||||||
sys.stderr.write(f"[DEBUG theme_2] save_to_config: palette={_current_palette}, transparency={_transparency}\n")
|
sys.stderr.write(f"[DEBUG theme_2] save_to_config: palette={_current_palette}, transparency={_transparency}\n")
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
|
|
||||||
def load_from_config(config: dict) -> None:
|
def load_from_config(config: dict) -> None:
|
||||||
"""Read [theme] from config. Font is handled separately at startup."""
|
"""Read [theme] from config. Font is handled separately at startup."""
|
||||||
import sys
|
import sys
|
||||||
global _current_font_path, _current_font_size, _current_scale, _current_palette, _transparency, _child_transparency
|
global _current_font_path, _current_font_size, _current_scale, _current_palette, _transparency, _child_transparency, _frosted_blur_radius, _frosted_tint_intensity, _frosted_opacity
|
||||||
t = config.get("theme", {})
|
t = config.get("theme", {})
|
||||||
sys.stderr.write(f"[DEBUG theme_2] load_from_config raw: {t}\n")
|
sys.stderr.write(f"[DEBUG theme_2] load_from_config raw: {t}\n")
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
@@ -369,6 +396,9 @@ def load_from_config(config: dict) -> None:
|
|||||||
_current_scale = float(t.get("scale", 1.0))
|
_current_scale = float(t.get("scale", 1.0))
|
||||||
_transparency = float(t.get("transparency", 1.0))
|
_transparency = float(t.get("transparency", 1.0))
|
||||||
_child_transparency = float(t.get("child_transparency", 1.0))
|
_child_transparency = float(t.get("child_transparency", 1.0))
|
||||||
|
_frosted_blur_radius = float(t.get("frosted_blur_radius", 8.0))
|
||||||
|
_frosted_tint_intensity = float(t.get("frosted_tint_intensity", 0.1))
|
||||||
|
_frosted_opacity = float(t.get("frosted_opacity", 1.0))
|
||||||
sys.stderr.write(f"[DEBUG theme_2] load_from_config effective: palette={_current_palette}, transparency={_transparency}\n")
|
sys.stderr.write(f"[DEBUG theme_2] load_from_config effective: palette={_current_palette}, transparency={_transparency}\n")
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
|
|
||||||
|
|||||||
28
tests/test_gui_frosted_integration.py
Normal file
28
tests/test_gui_frosted_integration.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import pytest
|
||||||
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
|
def test_gui_frosted_background_call():
|
||||||
|
# Mock ShaderManager and OpenGL functions
|
||||||
|
with patch("src.gui_2.ShaderManager") as mock_sm_class, \
|
||||||
|
patch("src.gui_2.gl") as mock_gl, \
|
||||||
|
patch("src.gui_2.imgui") as mock_imgui:
|
||||||
|
|
||||||
|
mock_sm = mock_sm_class.return_value
|
||||||
|
mock_sm.fbo_width = 0
|
||||||
|
mock_sm.fbo_height = 0
|
||||||
|
mock_sm.capture_tex = 1
|
||||||
|
mock_sm.blur_tex = 2
|
||||||
|
|
||||||
|
mock_imgui.get_io().display_size = MagicMock(x=1920, y=1080)
|
||||||
|
|
||||||
|
from src.gui_2 import App
|
||||||
|
app = App()
|
||||||
|
|
||||||
|
# Simulate frame
|
||||||
|
app._render_frosted_background(pos=MagicMock(x=10, y=10), size=MagicMock(x=100, y=100))
|
||||||
|
|
||||||
|
assert mock_sm.setup_capture_fbo.called
|
||||||
|
assert mock_gl.glCopyTexImage2D.called
|
||||||
|
assert mock_sm.render_blur.called
|
||||||
|
assert mock_sm.capture_begin.called
|
||||||
|
assert mock_sm.capture_end.called
|
||||||
Reference in New Issue
Block a user