User reported wanting to mark text in-game and copy it out (item names,
coordinates, NPC dialogue, etc). ImGui doesn't natively let you select
across multiple TextColored widgets, but a read-only multi-line
InputText is fully click-drag selectable + Ctrl+C copyable. This
commit adds a "Copy mode" toggle to ChatPanel that swaps the chat
tail's render path between the colored-line view and a single
selectable text region.
New IPanelRenderer primitive:
void TextMultilineReadOnly(string id, string content, Vector2 size);
ImGui maps this to InputTextMultiline with the ReadOnly flag — same
selection + Ctrl+C UX a user expects from any text-input widget.
FakePanelRenderer records the call for tests. The future D.2b
custom retail-look backend implements its own equivalent (likely
the same widget pattern with retail font/skin).
ChatPanel rendering:
· A "Copy mode (select text to Ctrl+C)" Checkbox at the top of
the panel toggles _copyMode.
· Off (default) — current per-line render with colored combat
entries. Visually unchanged from before.
· On — the chat tail becomes a single TextMultilineReadOnly
widget holding every visible line joined with newlines. Loses
per-line color, gains arbitrary-span text selection.
· Footer (separator + input field) renders identically in both
modes so the user can still type while in copy mode.
Existing ChatPanelLayoutTests's footer-separator probe was using
IndexOf("Separator") — which now matches the new pre-tail separator
between the Checkbox and the chat tail. Switched to LastIndexOf
which still pins the footer separator (between EndChild and
InputTextSubmit). Behaviour and intent unchanged.
DisplaySettingsTests' With_expression test was still asserting the
old "1920x1080" Default.Resolution; updated to the new "1280x720"
that the previous wire-up commit introduced (the earlier commit
forgot this one).
dotnet build green (0 warnings); dotnet test 1,309 / 1,309 green
(243 Core.Net + 393 UI.Abstractions + 673 Core).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>