diff --git a/src/AcDream.UI.Abstractions/Panels/Chat/ChatPanel.cs b/src/AcDream.UI.Abstractions/Panels/Chat/ChatPanel.cs index 28e035b..c8ece99 100644 --- a/src/AcDream.UI.Abstractions/Panels/Chat/ChatPanel.cs +++ b/src/AcDream.UI.Abstractions/Panels/Chat/ChatPanel.cs @@ -90,6 +90,20 @@ public sealed class ChatPanel : IPanel return; } + // L.0 follow-up: wrap the entire chat panel body in a single + // outer BeginChild so empty-space clicks anywhere in the body + // (Checkbox row, between Separator and input, etc.) are + // absorbed by BeginChild's drag-trap (an InvisibleButton the + // ImGui renderer adds inside every BeginChild). Without this + // wrapper the chat panel was draggable from any empty body + // pixel — only the inner ##chattail area was protected. + if (!renderer.BeginChild("##chatbody", new System.Numerics.Vector2(0f, 0f))) + { + renderer.EndChild(); + renderer.End(); + return; + } + // L.0 follow-up: top-of-panel "Copy mode" toggle. When on, the // chat tail rendering swaps to TextMultilineReadOnly so the // user can mark + Ctrl+C any text. Off (default) preserves the @@ -186,6 +200,7 @@ public sealed class ChatPanel : IPanel if (TryHandleClientCommand(trimmed)) { _input = string.Empty; + renderer.EndChild(); // outer ##chatbody renderer.End(); return; } @@ -206,6 +221,7 @@ public sealed class ChatPanel : IPanel _vm.ShowSystemMessage( $"Unknown command: {verb}. Type /help for the list of supported commands."); _input = string.Empty; + renderer.EndChild(); // outer ##chatbody renderer.End(); return; } @@ -225,6 +241,7 @@ public sealed class ChatPanel : IPanel _input = string.Empty; } + renderer.EndChild(); // outer ##chatbody renderer.End(); } diff --git a/tests/AcDream.UI.Abstractions.Tests/Panels/Chat/ChatPanelLayoutTests.cs b/tests/AcDream.UI.Abstractions.Tests/Panels/Chat/ChatPanelLayoutTests.cs index 0e80233..28813d3 100644 --- a/tests/AcDream.UI.Abstractions.Tests/Panels/Chat/ChatPanelLayoutTests.cs +++ b/tests/AcDream.UI.Abstractions.Tests/Panels/Chat/ChatPanelLayoutTests.cs @@ -67,8 +67,13 @@ public sealed class ChatPanelLayoutTests panel.Render(new PanelContext(0.016f, new NoBus()), renderer); - var beginChildCall = renderer.Calls.Single(c => c.Method == "BeginChild"); - var size = (System.Numerics.Vector2)beginChildCall.Args[1]!; + // L.0 follow-up: the chat panel now wraps its body in an outer + // ##chatbody BeginChild (so empty-space clicks can't drag the + // parent window). The inner ##chattail BeginChild is the one + // that reserves the footer; that's what this test asserts. + var chattailCall = renderer.Calls.Single(c => c.Method == "BeginChild" + && (string)c.Args[0]! == "##chattail"); + var size = (System.Numerics.Vector2)chattailCall.Args[1]!; // Width 0 = fill available; height < 0 = "fill minus this". // Reserved height should equal FrameHeightWithSpacing + a small // separator pad (~6f) so the input never visually clips the