fix(ui): chat input pinned to window bottom on resize via scrollable child
User reported the chat input field disappearing when the chat
window was resized smaller — older entries pushed it past the
visible area. Standard ImGui chat-window pattern fixes it: scrollable
nested region for the chat tail, fixed footer for the
separator + input field below it.
IPanelRenderer extensions (Phase J Tier 3):
- BeginChild(string id, Vector2 size, bool border = false) — opens
a nested scrollable region. Size follows ImGui semantics:
0 = fill available, negative = fill available minus this much.
- EndChild() — closes the nested region.
- FrameHeightWithSpacing() — single-line widget height incl. frame
padding + item spacing. Lets panels compute footer reservations
without hardcoding pixel constants.
- SetScrollHereY(float ratio) — forces scroll within current region;
pass 1.0f to keep the latest line visible after new entries
arrive.
ImGuiPanelRenderer impls. ImGui.NET's BeginChild signature changed
across versions (third arg moved from `bool border` to
`ImGuiChildFlags`); we cast a numeric literal (0x01 = Border bit)
to sidestep the rename. FrameHeightWithSpacing maps to
ImGui.GetFrameHeightWithSpacing(); SetScrollHereY to ImGui.SetScrollHereY.
ChatPanel restructured:
- Reserves footer height = FrameHeightWithSpacing() + 6f (small pad
for the separator above the input).
- Wraps the chat tail in BeginChild("##chattail", (0, -footer))
so the inner region scrolls independently of the window.
- Tracks _lastRenderedCount across frames and calls SetScrollHereY(1f)
only when new entries appended — manual scroll-up isn't fought
against; new messages jump the view back down only when they
actually arrive.
- Header Separator removed (the BeginChild border is enough).
FakePanelRenderer extended with the four new methods + recording.
4 new tests in ChatPanelLayoutTests pin the layout invariants:
- Render order: Begin → BeginChild → ... → EndChild → Separator
→ InputTextSubmit → End.
- BeginChild size has X=0 + negative Y at least matching the
injected FrameHeightWithSpacingValue.
- SetScrollHereY fires when entries grow.
- SetScrollHereY does NOT fire when entries don't grow.
Solution total: 1067 green (243 Core.Net + 164 UI + 660 Core),
0 warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a316d6359c
commit
a44488e277
5 changed files with 240 additions and 17 deletions
|
|
@ -162,4 +162,40 @@ public interface IPanelRenderer
|
|||
/// Use for descriptions, error messages, anything multi-line.
|
||||
/// </summary>
|
||||
void TextWrapped(string text);
|
||||
|
||||
// -- Phase J Tier 3 — scrollable child region for chat-style layouts --
|
||||
|
||||
/// <summary>
|
||||
/// Open a scrollable nested region inside the current window.
|
||||
/// <paramref name="size"/> follows ImGui semantics: 0 means "fill
|
||||
/// available", a positive value is absolute pixels, a negative
|
||||
/// value means "fill available minus this amount". Pass
|
||||
/// <c>(0, -footerHeight)</c> to reserve space at the bottom for
|
||||
/// fixed elements (separator + input field) so the inner content
|
||||
/// scrolls but the footer stays put across window resizes.
|
||||
/// </summary>
|
||||
bool BeginChild(string id, Vector2 size, bool border = false);
|
||||
|
||||
/// <summary>Close the most recent <see cref="BeginChild"/>.</summary>
|
||||
void EndChild();
|
||||
|
||||
/// <summary>
|
||||
/// Approximate single-line widget height including ImGui's frame
|
||||
/// padding + item spacing. Panels use this to compute footer
|
||||
/// reservations for <see cref="BeginChild"/> (e.g. one input
|
||||
/// field + spacing). Backend-friendly because it returns a
|
||||
/// scalar that the custom retail-look toolkit can also expose.
|
||||
/// </summary>
|
||||
float FrameHeightWithSpacing();
|
||||
|
||||
/// <summary>
|
||||
/// Scroll the current scroll region so that the given fractional
|
||||
/// vertical position is visible, where 0 = top and 1 = bottom.
|
||||
/// Typical usage: call with <c>1.0f</c> at the bottom of a
|
||||
/// chat-tail render block to keep the latest line visible when
|
||||
/// new entries arrive. No-op when no new content was emitted —
|
||||
/// callers are expected to skip the call when they don't want
|
||||
/// to force a scroll.
|
||||
/// </summary>
|
||||
void SetScrollHereY(float ratio);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue