From ca0139788541ed34a1f3759abee4a5c68d3bb7e8 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 13 Mar 2026 13:13:35 -0400 Subject: [PATCH] checkpoint: fixing ux with window frame bar --- check_hello_imgui.py | 9 ++ config.toml | 11 +-- imgui.ini | 25 ++++++ manual_slop_test.ini | 22 +++++ manualslop_layout.ini | 131 +++++++++++++++++++----------- pyproject.toml | 1 + scripts/temp_handle_test.py | 12 +++ src/gui_2.py | 80 +++++++++++------- tests/test_gui_window_controls.py | 24 ++++-- 9 files changed, 226 insertions(+), 89 deletions(-) create mode 100644 check_hello_imgui.py create mode 100644 imgui.ini create mode 100644 manual_slop_test.ini create mode 100644 scripts/temp_handle_test.py diff --git a/check_hello_imgui.py b/check_hello_imgui.py new file mode 100644 index 0000000..0dd815e --- /dev/null +++ b/check_hello_imgui.py @@ -0,0 +1,9 @@ + +import sys +import os +try: + from imgui_bundle import hello_imgui + rp = hello_imgui.RunnerParams() + print(f"Default borderless: {rp.app_window_params.borderless}") +except Exception as e: + print(f"Error: {e}") diff --git a/config.toml b/config.toml index b1b216d..09d6b02 100644 --- a/config.toml +++ b/config.toml @@ -23,7 +23,7 @@ active = "C:/projects/gencpp/gencpp_sloppy.toml" separate_message_panel = false separate_response_panel = false separate_tool_calls_panel = false -bg_shader_enabled = false +bg_shader_enabled = true crt_filter_enabled = false separate_task_dag = false separate_usage_analytics = false @@ -51,20 +51,21 @@ separate_external_tools = false "Discussion Hub" = true "Operations Hub" = true Message = false -Response = false -"Tool Calls" = false +Response = true +"Tool Calls" = true Theme = true "Log Management" = true Diagnostics = false "External Tools" = false +"Shader Editor" = false [theme] palette = "Nord Dark" font_path = "fonts/Inter-Regular.ttf" font_size = 16.0 scale = 1.0 -transparency = 1.0 -child_transparency = 1.0 +transparency = 0.5400000214576721 +child_transparency = 0.5899999737739563 [mma] max_workers = 4 diff --git a/imgui.ini b/imgui.ini new file mode 100644 index 0000000..22a87bb --- /dev/null +++ b/imgui.ini @@ -0,0 +1,25 @@ +;;; !!! This configuration is handled by HelloImGui and stores several Ini Files, separated by markers like this: + ;;;<<>>;;; + +;;;<<>>;;; +[Window][Debug##Default] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[Docking][Data] + + +;;;<<>>;;; + +;;;<<>>;;; +[Layout] +Name=Default +[StatusBar] +Show=false +ShowFps=true +[Theme] +Name=DarculaDarker + +;;;<<>>;;; +{"gImGuiSplitIDs":{}} diff --git a/manual_slop_test.ini b/manual_slop_test.ini new file mode 100644 index 0000000..797698c --- /dev/null +++ b/manual_slop_test.ini @@ -0,0 +1,22 @@ +;;; !!! This configuration is handled by HelloImGui and stores several Ini Files, separated by markers like this: + ;;;<<>>;;; + +;;;<<>>;;; +[Window][Debug##Default] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[Docking][Data] + +;;;<<>>;;; +;;;<<>>;;; +[Layout] +Name=Default +[StatusBar] +Show=false +ShowFps=true +[Theme] +Name=DarculaDarker +;;;<<>>;;; +{"gImGuiSplitIDs":{}} diff --git a/manualslop_layout.ini b/manualslop_layout.ini index 6b83c34..521a48a 100644 --- a/manualslop_layout.ini +++ b/manualslop_layout.ini @@ -44,18 +44,18 @@ Collapsed=0 DockId=0x00000001,0 [Window][Message] -Pos=642,1879 -Size=1002,242 +Pos=661,1426 +Size=716,455 Collapsed=0 [Window][Response] -Pos=1700,1898 -Size=1111,224 +Pos=2437,925 +Size=1111,773 Collapsed=0 [Window][Tool Calls] -Pos=855,1482 -Size=1014,655 +Pos=464,1088 +Size=564,220 Collapsed=0 DockId=0x00000006,0 @@ -74,8 +74,8 @@ Collapsed=0 DockId=0xAFC85805,2 [Window][Theme] -Pos=0,207 -Size=499,1403 +Pos=0,542 +Size=310,658 Collapsed=0 DockId=0x00000002,2 @@ -85,14 +85,14 @@ Size=900,700 Collapsed=0 [Window][Diagnostics] -Pos=2641,34 -Size=1199,2103 +Pos=1649,24 +Size=580,1284 Collapsed=0 DockId=0x00000010,2 [Window][Context Hub] -Pos=0,207 -Size=499,1403 +Pos=0,542 +Size=310,658 Collapsed=0 DockId=0x00000002,1 @@ -103,26 +103,26 @@ Collapsed=0 DockId=0x0000000D,0 [Window][Discussion Hub] -Pos=1172,24 -Size=703,1586 +Pos=649,24 +Size=449,1176 Collapsed=0 DockId=0x00000013,0 [Window][Operations Hub] -Pos=501,24 -Size=669,1586 +Pos=312,24 +Size=335,1176 Collapsed=0 DockId=0x00000005,0 [Window][Files & Media] -Pos=0,207 -Size=499,1403 +Pos=0,542 +Size=310,658 Collapsed=0 DockId=0x00000002,0 [Window][AI Settings] Pos=0,24 -Size=499,181 +Size=310,516 Collapsed=0 DockId=0x00000001,0 @@ -132,14 +132,14 @@ Size=416,325 Collapsed=0 [Window][MMA Dashboard] -Pos=1877,24 -Size=1018,1586 +Pos=1100,24 +Size=580,1176 Collapsed=0 DockId=0x00000010,0 [Window][Log Management] -Pos=1877,24 -Size=1018,1586 +Pos=1100,24 +Size=580,1176 Collapsed=0 DockId=0x00000010,1 @@ -175,7 +175,7 @@ Size=381,329 Collapsed=0 [Window][Last Script Output] -Pos=1005,343 +Pos=2810,265 Size=800,562 Collapsed=0 @@ -285,8 +285,8 @@ Size=900,700 Collapsed=0 [Window][Text Viewer - Tool Call #1 Details] -Pos=2318,1220 -Size=900,700 +Pos=165,1081 +Size=727,725 Collapsed=0 [Window][Text Viewer - Tool Call #10 Details] @@ -330,8 +330,8 @@ Size=967,499 Collapsed=0 [Window][Usage Analytics] -Pos=2822,1717 -Size=1018,420 +Pos=1649,1053 +Size=580,255 Collapsed=0 DockId=0x0000000F,0 @@ -350,6 +350,41 @@ Pos=856,546 Size=1000,800 Collapsed=0 +[Window][External Tools] +Pos=531,376 +Size=616,409 +Collapsed=0 + +[Window][Text Viewer - Tool Call #2 Details] +Pos=60,60 +Size=900,700 +Collapsed=0 + +[Window][Text Viewer - Tool Call #3 Details] +Pos=60,60 +Size=900,700 +Collapsed=0 + +[Window][Text Viewer - Entry #4] +Pos=1127,922 +Size=900,700 +Collapsed=0 + +[Window][Text Viewer - Entry #10] +Pos=755,715 +Size=1593,1240 +Collapsed=0 + +[Window][Text Viewer - Entry #5] +Pos=60,60 +Size=900,700 +Collapsed=0 + +[Window][Shader Editor] +Pos=457,710 +Size=493,252 +Collapsed=0 + [Table][0xFB6E3870,4] RefScale=13 Column 0 Width=80 @@ -381,11 +416,11 @@ Column 3 Width=20 Column 4 Weight=1.0000 [Table][0x2A6000B6,4] -RefScale=14 -Column 0 Width=42 -Column 1 Width=61 +RefScale=16 +Column 0 Width=48 +Column 1 Width=68 Column 2 Weight=1.0000 -Column 3 Width=105 +Column 3 Width=120 [Table][0x8BCC69C7,6] RefScale=13 @@ -407,7 +442,7 @@ Column 3 Width=120 RefScale=16 Column 0 Width=48 Column 1 Weight=1.0000 -Column 2 Width=119 +Column 2 Width=118 Column 3 Width=48 [Table][0xD99F45C5,4] @@ -429,9 +464,9 @@ Column 1 Width=100 Column 2 Weight=1.0000 [Table][0xA02D8C87,3] -RefScale=24 -Column 0 Width=270 -Column 1 Width=180 +RefScale=16 +Column 0 Width=180 +Column 1 Width=120 Column 2 Weight=1.0000 [Table][0xD0277E63,2] @@ -463,20 +498,20 @@ Column 1 Weight=1.0000 DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02 -DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,24 Size=2895,1586 Split=X - DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=2820,1183 Split=X +DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,24 Size=1680,1176 Split=X + DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=1647,1183 Split=X DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2 - DockNode ID=0x00000007 Parent=0x0000000B SizeRef=499,858 Split=Y Selected=0x8CA2375C - DockNode ID=0x00000001 Parent=0x00000007 SizeRef=824,708 CentralNode=1 Selected=0x7BD57D6A - DockNode ID=0x00000002 Parent=0x00000007 SizeRef=824,1403 Selected=0xF4139CA2 - DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1374,858 Split=X Selected=0x418C7449 - DockNode ID=0x00000012 Parent=0x0000000E SizeRef=669,402 Split=Y Selected=0x418C7449 - DockNode ID=0x00000005 Parent=0x00000012 SizeRef=876,1455 Selected=0x418C7449 - DockNode ID=0x00000006 Parent=0x00000012 SizeRef=876,654 Selected=0x1D56B311 - DockNode ID=0x00000013 Parent=0x0000000E SizeRef=703,402 Selected=0x6F2B5B04 + DockNode ID=0x00000007 Parent=0x0000000B SizeRef=310,858 Split=Y Selected=0x8CA2375C + DockNode ID=0x00000001 Parent=0x00000007 SizeRef=824,624 CentralNode=1 Selected=0x7BD57D6A + DockNode ID=0x00000002 Parent=0x00000007 SizeRef=824,658 Selected=0xF4139CA2 + DockNode ID=0x0000000E Parent=0x0000000B SizeRef=786,858 Split=X Selected=0x418C7449 + DockNode ID=0x00000012 Parent=0x0000000E SizeRef=335,402 Split=Y Selected=0x418C7449 + DockNode ID=0x00000005 Parent=0x00000012 SizeRef=876,1749 Selected=0x418C7449 + DockNode ID=0x00000006 Parent=0x00000012 SizeRef=876,362 Selected=0x1D56B311 + DockNode ID=0x00000013 Parent=0x0000000E SizeRef=449,402 Selected=0x6F2B5B04 DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6 - DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=1018,1183 Split=Y Selected=0x3AEC3498 - DockNode ID=0x00000010 Parent=0x00000004 SizeRef=1199,1689 Selected=0x3AEC3498 + DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=580,1183 Split=Y Selected=0x3AEC3498 + DockNode ID=0x00000010 Parent=0x00000004 SizeRef=1199,1689 Selected=0x2C0206CE DockNode ID=0x00000011 Parent=0x00000004 SizeRef=1199,420 Split=X Selected=0xDEB547B6 DockNode ID=0x0000000C Parent=0x00000011 SizeRef=916,380 Selected=0x655BC6E9 DockNode ID=0x0000000F Parent=0x00000011 SizeRef=281,380 Selected=0xDEB547B6 diff --git a/pyproject.toml b/pyproject.toml index 8fb282c..040e529 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ dependencies = [ "tree-sitter-python>=0.25.0", "mcp>=1.0.0", "pytest-timeout>=2.4.0", + "pyopengl>=3.1.10", ] [dependency-groups] diff --git a/scripts/temp_handle_test.py b/scripts/temp_handle_test.py new file mode 100644 index 0000000..abb8062 --- /dev/null +++ b/scripts/temp_handle_test.py @@ -0,0 +1,12 @@ +from imgui_bundle import hello_imgui, imgui + +def on_gui(): + imgui.text("Hello world") + +params = hello_imgui.RunnerParams() +params.app_window_params.borderless = True +params.app_window_params.borderless_movable = True +params.app_window_params.borderless_resizable = True +params.app_window_params.borderless_closable = True + +hello_imgui.run(params) diff --git a/src/gui_2.py b/src/gui_2.py index 7acacc8..ee16420 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -367,38 +367,58 @@ class App: except Exception as e: self.ai_status = f"error: {e}" imgui.end_menu() + + # Draw right-aligned window controls directly in the menu bar + try: + import ctypes + ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p + ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [ctypes.py_object, ctypes.c_char_p] + hwnd_capsule = imgui.get_main_viewport().platform_handle_raw + hwnd = ctypes.pythonapi.PyCapsule_GetPointer(hwnd_capsule, b"nb_handle") + except Exception: + hwnd = 0 + + if hwnd: + btn_w = 40 + display_w = imgui.get_io().display_size.x + right_x = display_w - (btn_w * 3) + + # Drag area check using an explicit invisible button spanning the empty space + curr_x = imgui.get_cursor_pos_x() + drag_w = right_x - curr_x + if drag_w > 0: + imgui.invisible_button("##drag_area", (drag_w, 0)) + if imgui.is_item_hovered() and imgui.is_mouse_clicked(0): + win32gui.ReleaseCapture() + win32gui.SendMessage(hwnd, win32con.WM_NCLBUTTONDOWN, win32con.HTCAPTION, 0) + + imgui.push_style_color(imgui.Col_.button, vec4(0, 0, 0, 0)) + + try: + is_max = win32gui.GetWindowPlacement(hwnd)[1] == win32con.SW_SHOWMAXIMIZED + except Exception: + is_max = False + + imgui.set_cursor_pos_x(right_x) + if imgui.button("_", (btn_w, 0)): + win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE) + + imgui.set_cursor_pos_x(right_x + btn_w) + if imgui.button("[=]" if is_max else "[]", (btn_w, 0)): + win32gui.ShowWindow(hwnd, win32con.SW_RESTORE if is_max else win32con.SW_MAXIMIZE) + + imgui.set_cursor_pos_x(right_x + btn_w * 2) + imgui.push_style_color(imgui.Col_.button_hovered, vec4(200, 50, 50, 255)) + if imgui.button("X", (btn_w, 0)): + win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0) + imgui.pop_style_color() + + imgui.pop_style_color() 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) + # Obsolete, removed since it renders behind the full screen dock space. + # Controls are now embedded in _show_menus. + pass def _render_shader_live_editor(self) -> None: if self.show_windows.get('Shader Editor', False): diff --git a/tests/test_gui_window_controls.py b/tests/test_gui_window_controls.py index 8c0d1b5..a753688 100644 --- a/tests/test_gui_window_controls.py +++ b/tests/test_gui_window_controls.py @@ -9,22 +9,34 @@ def test_gui_window_controls_minimize_maximize_close(): 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: - + patch("src.gui_2.imgui") as mock_imgui, \ + patch("ctypes.pythonapi.PyCapsule_GetPointer") as mock_get_pointer: + # Setup mock for HWND mock_viewport = MagicMock() - mock_viewport.platform_handle = 12345 + mock_viewport.platform_handle_raw = "mock_capsule" mock_imgui.get_main_viewport.return_value = mock_viewport - + mock_get_pointer.return_value = 12345 + mock_imgui.get_window_width.return_value = 800.0 + mock_imgui.get_cursor_pos_x.return_value = 100.0 + mock_imgui.get_io().display_size.x = 800.0 + mock_style = MagicMock() + mock_style.item_spacing.x = 4.0 + mock_imgui.get_style.return_value = mock_style # 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() + # Avoid hitting actual menu logic that requires real runner_params + mock_imgui.begin_menu.return_value = False + app.runner_params = MagicMock() + + # Call the method (now in _show_menus) + app._show_menus() + # 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