From 59d7368bd742d9ce8d25a3fc809d978c12ade7a1 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 13 Mar 2026 12:20:37 -0400 Subject: [PATCH] feat(gui): Implement custom title bar and window controls --- src/gui_2.py | 36 +++++++++++++++++++++++++++++++ tests/test_gui_window_controls.py | 31 ++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 tests/test_gui_window_controls.py diff --git a/src/gui_2.py b/src/gui_2.py index 5e199e1..02eafc4 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -29,6 +29,8 @@ from src import mcp_client from src import markdown_helper from src import bg_shader import re +import win32gui +import win32con from pydantic import BaseModel from imgui_bundle import imgui, hello_imgui, immapp, imgui_node_editor as ed @@ -364,7 +366,40 @@ class App: self.ai_status = f"error: {e}" imgui.end_menu() + def _render_custom_title_bar(self) -> None: + hwnd = imgui.get_main_viewport().platform_handle + if not hwnd: return + bar_height = 30 + imgui.set_next_window_pos((0, 0)) + imgui.set_next_window_size((imgui.get_io().display_size.x, bar_height)) + flags = (imgui.WindowFlags_.no_title_bar | imgui.WindowFlags_.no_resize | + imgui.WindowFlags_.no_move | imgui.WindowFlags_.no_scrollbar | + imgui.WindowFlags_.no_saved_settings | imgui.WindowFlags_.no_bring_to_front_on_focus) + imgui.push_style_var(imgui.StyleVar_.window_rounding, 0) + imgui.push_style_var(imgui.StyleVar_.window_border_size, 0) + imgui.push_style_var(imgui.StyleVar_.window_padding, (10, 5)) + if imgui.begin("##custom_title_bar", None, flags): + imgui.text("manual slop") + if imgui.is_window_hovered() and imgui.is_mouse_dragging(0): + win32gui.ReleaseCapture() + win32gui.SendMessage(hwnd, win32con.WM_NCLBUTTONDOWN, win32con.HTCAPTION, 0) + btn_w = 40 + spacing = imgui.get_style().item_spacing.x + right_x = imgui.get_window_width() - (btn_w * 3 + spacing * 2 + 10) + imgui.same_line(right_x) + if imgui.button("_", (btn_w, 20)): + win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE) + imgui.same_line() + if imgui.button("[]", (btn_w, 20)): + win32gui.ShowWindow(hwnd, win32con.SW_MAXIMIZE) + imgui.same_line() + if imgui.button("X", (btn_w, 20)): + win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0) + imgui.end() + imgui.pop_style_var(3) + def _gui_func(self) -> None: + self._render_custom_title_bar() pushed_prior_tint = False # Render background shader bg = bg_shader.get_bg() @@ -3928,6 +3963,7 @@ def hello(): theme.load_from_config(self.config) self.runner_params = hello_imgui.RunnerParams() self.runner_params.app_window_params.window_title = "manual slop" + self.runner_params.app_window_params.borderless = True 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 diff --git a/tests/test_gui_window_controls.py b/tests/test_gui_window_controls.py new file mode 100644 index 0000000..8c0d1b5 --- /dev/null +++ b/tests/test_gui_window_controls.py @@ -0,0 +1,31 @@ +import pytest +from unittest.mock import patch, MagicMock +from imgui_bundle import imgui + +def test_gui_window_controls_minimize_maximize_close(): + # We will test the logic of the title bar controls that we are about to implement. + from src.gui_2 import App + app = App() + + with patch("src.gui_2.win32gui") as mock_win32gui, \ + patch("src.gui_2.win32con") as mock_win32con, \ + patch("src.gui_2.imgui") as mock_imgui: + + # Setup mock for HWND + mock_viewport = MagicMock() + mock_viewport.platform_handle = 12345 + mock_imgui.get_main_viewport.return_value = mock_viewport + + # Setup mock for buttons to simulate clicks + # Let's say _render_custom_title_bar uses imgui.button + # We will test the close button logic + # Since it's UI code, we just simulate the conditions + mock_imgui.button.return_value = True # Simulate all buttons being clicked + + # Call the method (to be implemented) + app._render_custom_title_bar() + + # Verify that win32gui calls are made for minimize, maximize, close + # Since all buttons returned True, all actions should be triggered in this dummy test + assert mock_win32gui.ShowWindow.called + assert mock_win32gui.PostMessage.called