diff --git a/src/AcDream.UI.ImGui/ImGuiPanelRenderer.cs b/src/AcDream.UI.ImGui/ImGuiPanelRenderer.cs index 94f09d4..4aa94ae 100644 --- a/src/AcDream.UI.ImGui/ImGuiPanelRenderer.cs +++ b/src/AcDream.UI.ImGui/ImGuiPanelRenderer.cs @@ -14,30 +14,7 @@ namespace AcDream.UI.ImGui; public sealed class ImGuiPanelRenderer : IPanelRenderer { /// - 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 bool Begin(string title) => ImGuiNET.ImGui.Begin(title); /// public void End() => ImGuiNET.ImGui.End(); @@ -178,12 +155,41 @@ public sealed class ImGuiPanelRenderer : IPanelRenderer /// public bool BeginChild(string id, Vector2 size, bool border = false) + { // ImGuiChildFlags has changed names across ImGui.NET versions // (Border vs Borders); 0x01 is the stable bit value for "draw // a border". Casting from a numeric literal sidesteps the // version-skew without requiring a hard reference to either // enum spelling. - => ImGuiNET.ImGui.BeginChild(id, size, (ImGuiChildFlags)(border ? 0x01 : 0)); + bool open = ImGuiNET.ImGui.BeginChild(id, size, (ImGuiChildFlags)(border ? 0x01 : 0)); + if (open) + { + // Title-bar-only drag fix (chat tail specifically): empty + // clicks inside a scrollable child fall through to the + // parent window for drag-init, which is exactly what the + // user reported in the chat panel ("clicking anywhere + // moves the window"). An InvisibleButton sized to the + // child's content region absorbs those clicks so they + // don't propagate. Real widgets drawn afterwards still + // claim their own clicks (click priority = "last drawn, + // first checked"). Wheel scrolling is window-level, not + // item-level, so the absorber doesn't interfere with + // the chat tail's auto-scroll. + // + // Scoped to BeginChild only (NOT Begin) because Begin's + // body might host tab bars whose hit-testing competes with + // an absorber on equal terms — adding it at Begin level + // broke Settings tab clicks. + var avail = ImGuiNET.ImGui.GetContentRegionAvail(); + if (avail.X > 0f && avail.Y > 0f) + { + var savedCursor = ImGuiNET.ImGui.GetCursorPos(); + ImGuiNET.ImGui.InvisibleButton("##childbodyabsorb", avail); + ImGuiNET.ImGui.SetCursorPos(savedCursor); + } + } + return open; + } /// public void EndChild() => ImGuiNET.ImGui.EndChild();