From 5ab23f9eea64495e1871ccb42f2ce3530b36b7a4 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Tue, 30 Jun 2026 07:30:44 -0400 Subject: [PATCH] fix(layout): make 2-column dock layout actually auto-apply The pre-run install wrote the bundled INI to cwd, and the _install_default_layout_if_empty helper applies it via imgui.load_ini_settings_from_memory() when cwd is empty. But the GUI was rendering all panels as floating windows at default position (60, 60) with no DockId, despite the bundled INI having a full [Docking][Data] block with DockSpace + DockNodes + per-window DockIds. Root cause analysis (via imgui.save_ini_settings_to_memory() at runtime): 1. With default_imgui_window_type=provide_full_screen_dock_space: HelloImGui creates its own DockSpace at runtime, overriding the INI's DockSpace settings. The DockSpace ID matches (0xAFC85805) but the Split/X and child DockNodes from the bundled INI are discarded. Runtime INI shows: 'DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,28 Size=1666,1172 CentralNode=1' (no DockNodes, no DockIds honored). 2. The pre-run install writes the INI to disk, but HelloImGui's load_user_pref runs BEFORE post_init, so even a perfect on-disk INI doesn't get re-applied to the current session's dock state unless we call imgui.load_ini_settings_from_memory() after the first frame. Two-part fix: A. src/gui_2.py line 678: change default_imgui_window_type from 'provide_full_screen_dock_space' to 'no_default_window'. Without the auto-created DockSpace, HelloImGui honors the INI's full docking tree structure. B. src/gui_2.py _post_init (line 575): always call imgui.load_ini_settings_from_memory() after _install_default_layout runs, regardless of whether the cwd INI was empty. This re-applies the bundled INI to the live session after the first frame is rendered, so the panels are docked correctly on the current launch. Layouts/default.ini: replace the simple 'DockSpace + 2 direct DockNode children' structure (silently ignored by HelloImGui) with the user's working nested DockNode tree (5-level deep), mapped to: - LEFT column (DockNode 0x10, CentralNode=1): Theme, Project Settings, AI Settings, Files & Media, Operations Hub - RIGHT column (DockNode 0x01): Discussion Hub, Log Management, Diagnostics Verification (imgui.save_ini_settings_to_memory at runtime after 15s + first frame): - LEFT column windows: Pos=0,28, Size=881,1697 (5 panels stacked) - RIGHT column windows: Pos=883,28, Size=1183,1697 (3 panels stacked) - [Docking][Data] block fully preserved (DockSpace + 8 DockNodes) - All 8 panels docked (not floating) Tests: - tests/test_default_layout_install.py: 3/3 PASS - tests/test_api_hooks_gui_health_live.py: 1/1 PASS - tests/test_command_palette_sim.py: 7/7 PASS - tests/test_saved_presets_sim.py: 2/2 PASS - tests/test_live_gui_integration_v2.py: 3/3 PASS --- layouts/default.ini | 91 ++++++++++++++++++------------------- manualslop_layout.ini | 101 ------------------------------------------ src/gui_2.py | 15 ++++++- 3 files changed, 60 insertions(+), 147 deletions(-) delete mode 100644 manualslop_layout.ini diff --git a/layouts/default.ini b/layouts/default.ini index ec02a784..eeb5a4dd 100644 --- a/layouts/default.ini +++ b/layouts/default.ini @@ -4,22 +4,19 @@ ;;; missing/empty/small). ;;; ;;; Mechanism: HelloImGui reads this INI at app startup via the -;;; ini_folder_type/ini_filename on the RunnerParams. The DockSpace ID, -;;; DockNode IDs, and per-window DockId lines below tell HelloImgui where -;;; to place each panel. The literal IDs (0xAFC85805, 0x00000001, -;;; 0x00000002) match the runtime-generated MainDockSpace ID (decimal -;;; 2949142533) that HelloImGui computes deterministically per session. +;;; ini_folder_type/ini_filename on the RunnerParams. The DockSpace ID +;;; 0xAFC85805 (= decimal 2949142533) is the runtime-generated MainDockSpace +;;; ID. The nested DockNode tree creates a 2-column layout: +;;; - LEFT column (DockNode 0x10, CentralNode=1): tabs for Theme, Project +;;; Settings, AI Settings, Files & Media, Operations Hub +;;; - RIGHT column (DockNode 0x01): tabs for Discussion Hub, Log +;;; Management, Diagnostics ;;; -;;; Window list matches the post-config-merge effective visibility set -;;; (8 default-true windows excluding Response (stale) and the four -;;; Tier panels (disabled in config.toml)): -;;; Project Settings, Files & Media, AI Settings, AI operations, -;;; Discussion Hub, Operations Hub, Theme, Log Management, Diagnostics. -;;; Per-window DockId: -;;; 0x00000001,0..4 = left column tabs (Theme, Project Settings, -;;; AI Settings, Files & Media, Operations Hub) -;;; 0x00000002,0..2 = right column tabs (Discussion Hub, Log Management, -;;; Diagnostics) +;;; The nested structure (DockSpace -> DockNode 0x03 -> DockNode 0x0B -> +;;; DockNode 0x05 -> DockNode 0x10) is REQUIRED for HelloImGui to honor +;;; the docking. A simpler "DockSpace + 2 direct children" structure is +;;; silently ignored by HelloImGui (verified empirically — runtime INI +;;; shows DockSpace with no DockNodes, all windows float). ;;; ;;; All Collapsed=0 so the windows expand immediately on first render. ;;; @@ -30,65 +27,70 @@ ;;; ;;; To iterate on this layout: open sloppy.py, arrange windows as ;;; desired, quit (HelloImGui auto-saves), then copy the resulting -;;; cwd/manualslop_layout.ini over this one. (HelloImGui adds SplitsIds, -;;; Tables, and other internal sections on save; the bundled default -;;; version is the minimal scaffold needed for first-run visibility.) +;;; cwd/manualslop_layout.ini over this one. ;;; [Window][Project Settings] Pos=0,28 -Size=481,1172 +Size=881,1697 Collapsed=0 -DockId=0x00000001,1 +DockId=0x00000010,1 [Window][Files & Media] Pos=0,28 -Size=481,1172 +Size=881,1697 Collapsed=0 -DockId=0x00000001,3 +DockId=0x00000010,2 [Window][AI Settings] Pos=0,28 -Size=481,1172 +Size=881,1697 Collapsed=0 -DockId=0x00000001,2 +DockId=0x00000010,3 [Window][Operations Hub] Pos=0,28 -Size=481,1172 +Size=881,1697 Collapsed=0 -DockId=0x00000001,4 +DockId=0x00000010,4 [Window][Theme] Pos=0,28 -Size=481,1172 +Size=881,1697 +Collapsed=0 +DockId=0x00000010,0 + +[Window][Discussion Hub] +Pos=883,28 +Size=1183,1697 Collapsed=0 DockId=0x00000001,0 -[Window][Discussion Hub] -Pos=483,28 -Size=1197,1172 -Collapsed=0 -DockId=0x00000002,0 - [Window][Log Management] -Pos=483,28 -Size=1197,1172 +Pos=883,28 +Size=1183,1697 Collapsed=0 -DockId=0x00000002,1 +DockId=0x00000001,1 [Window][Diagnostics] -Pos=483,28 -Size=1197,1172 +Pos=883,28 +Size=1183,1697 Collapsed=0 -DockId=0x00000002,2 +DockId=0x00000001,2 [Docking][Data] -DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,28 Size=1680,1172 Split=X - DockNode ID=0x00000001 Parent=0xAFC85805 SizeRef=481,1172 CentralNode=1 Selected=0x3F1379AF - DockNode ID=0x00000002 Parent=0xAFC85805 SizeRef=1197,1172 Selected=0xB4CBF21A +DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,28 Size=1680,1172 Split=X + DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=2357,1183 Split=X + DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2 + DockNode ID=0x00000005 Parent=0x0000000B SizeRef=820,1681 Split=Y Selected=0x3F1379AF + DockNode ID=0x00000010 Parent=0x00000005 SizeRef=983,1140 CentralNode=1 Selected=0x3F1379AF + DockNode ID=0x00000011 Parent=0x00000005 SizeRef=983,184 Selected=0x432BAE4E + DockNode ID=0x00000006 Parent=0x0000000B SizeRef=1754,1681 Split=X Selected=0x6F2B5B04 + DockNode ID=0x00000001 Parent=0x00000006 SizeRef=1183,1924 Selected=0xB4CBF21A + DockNode ID=0x00000002 Parent=0x00000006 SizeRef=569,1924 Selected=0x0D5A5273 + DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6 + DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=488,1183 Selected=0x3AEC3498 -;;;<<>>;;; [Layout] Name=Default [StatusBar] @@ -97,5 +99,4 @@ ShowFps=true [Theme] Name=ImGuiColorsDark -;;;<<>>;;; -{"gImGuiSplitIDs":{"MainDockSpace":2949142533}} +{"gImGuiSplitIDs":{"MainDockSpace":2949142533}} \ No newline at end of file diff --git a/manualslop_layout.ini b/manualslop_layout.ini deleted file mode 100644 index ec02a784..00000000 --- a/manualslop_layout.ini +++ /dev/null @@ -1,101 +0,0 @@ -;;; -;;; Manual Slop default docking layout for live_gui test sessions AND -;;; first-run production launches (when cwd/manualslop_layout.ini is -;;; missing/empty/small). -;;; -;;; Mechanism: HelloImGui reads this INI at app startup via the -;;; ini_folder_type/ini_filename on the RunnerParams. The DockSpace ID, -;;; DockNode IDs, and per-window DockId lines below tell HelloImgui where -;;; to place each panel. The literal IDs (0xAFC85805, 0x00000001, -;;; 0x00000002) match the runtime-generated MainDockSpace ID (decimal -;;; 2949142533) that HelloImGui computes deterministically per session. -;;; -;;; Window list matches the post-config-merge effective visibility set -;;; (8 default-true windows excluding Response (stale) and the four -;;; Tier panels (disabled in config.toml)): -;;; Project Settings, Files & Media, AI Settings, AI operations, -;;; Discussion Hub, Operations Hub, Theme, Log Management, Diagnostics. -;;; Per-window DockId: -;;; 0x00000001,0..4 = left column tabs (Theme, Project Settings, -;;; AI Settings, Files & Media, Operations Hub) -;;; 0x00000002,0..2 = right column tabs (Discussion Hub, Log Management, -;;; Diagnostics) -;;; -;;; All Collapsed=0 so the windows expand immediately on first render. -;;; -;;; This INI does NOT include any of the _STALE_WINDOW_NAMES from -;;; src/gui_2.py:603-607 (Projects, Files, Screenshots, Discussion History, -;;; Provider, Message, Response, Tool Calls, Comms History, System Prompts). -;;; _diag_layout_state will not emit a "stale window name" warning. -;;; -;;; To iterate on this layout: open sloppy.py, arrange windows as -;;; desired, quit (HelloImGui auto-saves), then copy the resulting -;;; cwd/manualslop_layout.ini over this one. (HelloImGui adds SplitsIds, -;;; Tables, and other internal sections on save; the bundled default -;;; version is the minimal scaffold needed for first-run visibility.) -;;; - -[Window][Project Settings] -Pos=0,28 -Size=481,1172 -Collapsed=0 -DockId=0x00000001,1 - -[Window][Files & Media] -Pos=0,28 -Size=481,1172 -Collapsed=0 -DockId=0x00000001,3 - -[Window][AI Settings] -Pos=0,28 -Size=481,1172 -Collapsed=0 -DockId=0x00000001,2 - -[Window][Operations Hub] -Pos=0,28 -Size=481,1172 -Collapsed=0 -DockId=0x00000001,4 - -[Window][Theme] -Pos=0,28 -Size=481,1172 -Collapsed=0 -DockId=0x00000001,0 - -[Window][Discussion Hub] -Pos=483,28 -Size=1197,1172 -Collapsed=0 -DockId=0x00000002,0 - -[Window][Log Management] -Pos=483,28 -Size=1197,1172 -Collapsed=0 -DockId=0x00000002,1 - -[Window][Diagnostics] -Pos=483,28 -Size=1197,1172 -Collapsed=0 -DockId=0x00000002,2 - -[Docking][Data] -DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,28 Size=1680,1172 Split=X - DockNode ID=0x00000001 Parent=0xAFC85805 SizeRef=481,1172 CentralNode=1 Selected=0x3F1379AF - DockNode ID=0x00000002 Parent=0xAFC85805 SizeRef=1197,1172 Selected=0xB4CBF21A - -;;;<<>>;;; -[Layout] -Name=Default -[StatusBar] -Show=false -ShowFps=true -[Theme] -Name=ImGuiColorsDark - -;;;<<>>;;; -{"gImGuiSplitIDs":{"MainDockSpace":2949142533}} diff --git a/src/gui_2.py b/src/gui_2.py index 6793917a..f5c061ef 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -580,6 +580,19 @@ class App: if not install_result.ok: if not hasattr(self, "_startup_timeline_errors"): self._startup_timeline_errors = [] self._startup_timeline_errors.append(("_install_default_layout", install_result.errors[0])) + # Always re-apply the bundled layout to the live imgui session after + # the first frame is rendered. HelloImGui reads ini_filename from disk + # BEFORE post_init fires (during immapp.run's load_user_pref phase), + # so even a perfect on-disk INI may not be honored unless we also + # apply it via imgui.load_ini_settings_from_memory here. The pre-run + # install writes the INI for the NEXT launch; this live apply + # makes the CURRENT launch render the bundled layout correctly. + try: + src_text = src_layout_path.read_text(encoding="utf-8", errors="replace") + imgui.load_ini_settings_from_memory(src_text) + except OSError as e: + if not hasattr(self, "_startup_timeline_errors"): self._startup_timeline_errors = [] + self._startup_timeline_errors.append(("_post_init.live_apply", str(e))) if hasattr(self.controller, "on_warmup_complete"): cb_result = _post_init_callback_result(self) if not cb_result.ok: @@ -662,7 +675,7 @@ class App: 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.tweaked_theme = theme.get_tweaked_theme() - self.runner_params.imgui_window_params.default_imgui_window_type = hello_imgui.DefaultImGuiWindowType.provide_full_screen_dock_space + self.runner_params.imgui_window_params.default_imgui_window_type = hello_imgui.DefaultImGuiWindowType.no_default_window # Enforce DPI Awareness and User Scale user_scale = theme.get_current_scale()