From 627325559c1dc49305a18e2437cd5a3c142e8d4e Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 26 Apr 2026 22:58:05 +0200 Subject: [PATCH] =?UTF-8?q?fix(ui):=20title-bar-only=20drag=20=E2=80=94=20?= =?UTF-8?q?absorb=20body=20clicks=20via=20InvisibleButton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User reported that clicking anywhere in a panel (chat, settings, etc) started a window drag. ImGui's default window-drag init fires on any body click that doesn't land on an "active" widget — empty space between Text widgets, BeginChild background pad, etc. all qualified. Fix: right after Begin, place an InvisibleButton sized to the full body content region, then reset the cursor so subsequent panel content renders normally. ImGui's click-priority is "last drawn, first checked" — so real widgets drawn afterwards still claim their own clicks. The InvisibleButton catches ONLY clicks on empty body space, marks itself as the active item, and ImGui's window-drag check sees ActiveId != 0 → no drag. Net effect: title bar still drags (ImGui default), body never drags. Applies uniformly to every panel that calls IPanelRenderer.Begin (chat / settings / vitals / debug). dotnet build green (0 warnings). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/AcDream.UI.ImGui/ImGuiPanelRenderer.cs | 25 +++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/AcDream.UI.ImGui/ImGuiPanelRenderer.cs b/src/AcDream.UI.ImGui/ImGuiPanelRenderer.cs index 4396874..94f09d4 100644 --- a/src/AcDream.UI.ImGui/ImGuiPanelRenderer.cs +++ b/src/AcDream.UI.ImGui/ImGuiPanelRenderer.cs @@ -14,7 +14,30 @@ namespace AcDream.UI.ImGui; public sealed class ImGuiPanelRenderer : IPanelRenderer { /// - public bool Begin(string title) => ImGuiNET.ImGui.Begin(title); + public bool Begin(string title) + { + bool open = ImGuiNET.ImGui.Begin(title); + if (open) + { + // Title-bar-only drag: ImGui's default lets the user drag the + // window by clicking on the empty body background (because a + // window-drag is initiated whenever a body click lands without + // any widget being "active"). Filling the body with an + // InvisibleButton absorbs those stray clicks — real widgets + // drawn afterwards still claim their own clicks because click + // priority is "last drawn, first checked", so the button + // catches only empty-space clicks. Net effect: title bar + // still drags (ImGui default), body never does. + var avail = ImGuiNET.ImGui.GetContentRegionAvail(); + if (avail.X > 0f && avail.Y > 0f) + { + var savedCursor = ImGuiNET.ImGui.GetCursorPos(); + ImGuiNET.ImGui.InvisibleButton("##bodydragabsorb", avail); + ImGuiNET.ImGui.SetCursorPos(savedCursor); + } + } + return open; + } /// public void End() => ImGuiNET.ImGui.End();