feat(gui): Implement custom title bar and window controls
This commit is contained in:
36
src/gui_2.py
36
src/gui_2.py
@@ -29,6 +29,8 @@ 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
|
||||||
|
import win32con
|
||||||
|
|
||||||
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
|
||||||
@@ -364,7 +366,40 @@ class App:
|
|||||||
self.ai_status = f"error: {e}"
|
self.ai_status = f"error: {e}"
|
||||||
imgui.end_menu()
|
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:
|
def _gui_func(self) -> None:
|
||||||
|
self._render_custom_title_bar()
|
||||||
pushed_prior_tint = False
|
pushed_prior_tint = False
|
||||||
# Render background shader
|
# Render background shader
|
||||||
bg = bg_shader.get_bg()
|
bg = bg_shader.get_bg()
|
||||||
@@ -3928,6 +3963,7 @@ 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
|
||||||
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
|
||||||
|
|||||||
31
tests/test_gui_window_controls.py
Normal file
31
tests/test_gui_window_controls.py
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user