wrap win32 usage in conditionals

This commit is contained in:
2026-03-13 13:29:13 -04:00
parent 87bd2ae11c
commit 9f8dd48a2e
2 changed files with 81 additions and 70 deletions

View File

@@ -74,8 +74,8 @@ Collapsed=0
DockId=0xAFC85805,2 DockId=0xAFC85805,2
[Window][Theme] [Window][Theme]
Pos=0,542 Pos=0,495
Size=310,658 Size=547,737
Collapsed=0 Collapsed=0
DockId=0x00000002,2 DockId=0x00000002,2
@@ -91,8 +91,8 @@ Collapsed=0
DockId=0x00000010,2 DockId=0x00000010,2
[Window][Context Hub] [Window][Context Hub]
Pos=0,542 Pos=0,495
Size=310,658 Size=547,737
Collapsed=0 Collapsed=0
DockId=0x00000002,1 DockId=0x00000002,1
@@ -103,26 +103,26 @@ Collapsed=0
DockId=0x0000000D,0 DockId=0x0000000D,0
[Window][Discussion Hub] [Window][Discussion Hub]
Pos=649,24 Pos=886,24
Size=449,1176 Size=449,1208
Collapsed=0 Collapsed=0
DockId=0x00000013,0 DockId=0x00000013,0
[Window][Operations Hub] [Window][Operations Hub]
Pos=312,24 Pos=549,24
Size=335,1176 Size=335,1208
Collapsed=0 Collapsed=0
DockId=0x00000005,0 DockId=0x00000005,0
[Window][Files & Media] [Window][Files & Media]
Pos=0,542 Pos=0,495
Size=310,658 Size=547,737
Collapsed=0 Collapsed=0
DockId=0x00000002,0 DockId=0x00000002,0
[Window][AI Settings] [Window][AI Settings]
Pos=0,24 Pos=0,24
Size=310,516 Size=547,469
Collapsed=0 Collapsed=0
DockId=0x00000001,0 DockId=0x00000001,0
@@ -132,14 +132,14 @@ Size=416,325
Collapsed=0 Collapsed=0
[Window][MMA Dashboard] [Window][MMA Dashboard]
Pos=1100,24 Pos=1337,24
Size=580,1176 Size=580,1208
Collapsed=0 Collapsed=0
DockId=0x00000010,0 DockId=0x00000010,0
[Window][Log Management] [Window][Log Management]
Pos=1100,24 Pos=1337,24
Size=580,1176 Size=580,1208
Collapsed=0 Collapsed=0
DockId=0x00000010,1 DockId=0x00000010,1
@@ -498,12 +498,12 @@ Column 1 Weight=1.0000
DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y
DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02 DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02
DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,24 Size=1680,1176 Split=X DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,24 Size=1917,1208 Split=X
DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=1647,1183 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=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2
DockNode ID=0x00000007 Parent=0x0000000B SizeRef=310,858 Split=Y Selected=0x8CA2375C 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=0x00000001 Parent=0x00000007 SizeRef=824,525 CentralNode=1 Selected=0x7BD57D6A
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=824,658 Selected=0xF4139CA2 DockNode ID=0x00000002 Parent=0x00000007 SizeRef=824,737 Selected=0x8CA2375C
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=786,858 Split=X Selected=0x418C7449 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=0x00000012 Parent=0x0000000E SizeRef=335,402 Split=Y Selected=0x418C7449
DockNode ID=0x00000005 Parent=0x00000012 SizeRef=876,1749 Selected=0x418C7449 DockNode ID=0x00000005 Parent=0x00000012 SizeRef=876,1749 Selected=0x418C7449

View File

@@ -29,8 +29,12 @@ 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
import re import re
import win32gui if sys.platform == "win32":
import win32con import win32gui
import win32con
else:
win32gui = None
win32con = None
from pydantic import BaseModel from pydantic import BaseModel
from imgui_bundle import imgui, hello_imgui, immapp, imgui_node_editor as ed from imgui_bundle import imgui, hello_imgui, immapp, imgui_node_editor as ed
@@ -368,58 +372,59 @@ class App:
self.ai_status = f"error: {e}" self.ai_status = f"error: {e}"
imgui.end_menu() imgui.end_menu()
# Draw right-aligned window controls directly in the menu bar # Draw right-aligned window controls directly in the menu bar (Win32 only)
try: if sys.platform == "win32":
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:
# Use a small positive height to satisfy IM_ASSERT(size_arg.y != 0.0f)
# The menu bar naturally constrains the hit box height anyway.
imgui.invisible_button("##drag_area", (drag_w, 20.0))
if imgui.is_item_active() and imgui.is_mouse_dragging(0):
# CRITICAL: We must reset ImGui's mouse_down state BEFORE passing control to Windows.
# Otherwise, the Windows modal drag loop swallows the WM_LBUTTONUP event,
# and ImGui thinks the mouse is permanently held down, causing "sticky" dragging.
imgui.get_io().mouse_down[0] = False
win32gui.ReleaseCapture()
win32gui.SendMessage(hwnd, win32con.WM_NCLBUTTONDOWN, win32con.HTCAPTION, 0)
imgui.push_style_color(imgui.Col_.button, vec4(0, 0, 0, 0))
try: try:
is_max = win32gui.GetWindowPlacement(hwnd)[1] == win32con.SW_SHOWMAXIMIZED 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: except Exception:
is_max = False hwnd = 0
imgui.set_cursor_pos_x(right_x) if hwnd:
if imgui.button("_", (btn_w, 0)): btn_w = 40
win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE) display_w = imgui.get_io().display_size.x
right_x = display_w - (btn_w * 3)
imgui.set_cursor_pos_x(right_x + btn_w) # Drag area check using an explicit invisible button spanning the empty space
if imgui.button("[=]" if is_max else "[]", (btn_w, 0)): curr_x = imgui.get_cursor_pos_x()
win32gui.ShowWindow(hwnd, win32con.SW_RESTORE if is_max else win32con.SW_MAXIMIZE) drag_w = right_x - curr_x
if drag_w > 0:
# Use a small positive height to satisfy IM_ASSERT(size_arg.y != 0.0f)
# The menu bar naturally constrains the hit box height anyway.
imgui.invisible_button("##drag_area", (drag_w, 20.0))
if imgui.is_item_active() and imgui.is_mouse_dragging(0):
# CRITICAL: We must reset ImGui's mouse_down state BEFORE passing control to Windows.
# Otherwise, the Windows modal drag loop swallows the WM_LBUTTONUP event,
# and ImGui thinks the mouse is permanently held down, causing "sticky" dragging.
imgui.get_io().mouse_down[0] = False
win32gui.ReleaseCapture()
win32gui.SendMessage(hwnd, win32con.WM_NCLBUTTONDOWN, win32con.HTCAPTION, 0)
imgui.set_cursor_pos_x(right_x + btn_w * 2) imgui.push_style_color(imgui.Col_.button, vec4(0, 0, 0, 0))
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() 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: def _render_custom_title_bar(self) -> None:
# Obsolete, removed since it renders behind the full screen dock space. # Obsolete, removed since it renders behind the full screen dock space.
@@ -4002,7 +4007,13 @@ def hello():
theme.load_from_config(self.config) theme.load_from_config(self.config)
self.runner_params = hello_imgui.RunnerParams() self.runner_params = hello_imgui.RunnerParams()
self.runner_params.app_window_params.window_title = "manual slop" self.runner_params.app_window_params.window_title = "manual slop"
self.runner_params.app_window_params.borderless = True
if sys.platform == "win32":
self.runner_params.app_window_params.borderless = True
self.runner_params.app_window_params.borderless_closable = False
self.runner_params.app_window_params.borderless_movable = False
self.runner_params.app_window_params.borderless_resizable = True
self.runner_params.app_window_params.window_geometry.size = (1680, 1200) 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.enable_viewports = getattr(self, "ui_multi_viewport", False)
self.runner_params.imgui_window_params.remember_theme = True self.runner_params.imgui_window_params.remember_theme = True