fix(chat): CH2 re-review nits — resize centering, top-aligned flow, sweep wording
Applies the seven NITs from the CH2 re-review (verdict APPROVE-WITH-FIXES, following the REJECT->rework ate0e78883): 1. SpewBoxController's centered Left was captured once via AnchorEdges.Top and replayed forever on resize (UiElement.ApplyAnchor's Left/Right-both-false branch pins a fixed margin). Anchors is now AnchorEdges.None and Tick recomputes Left every frame against the current root width. 2. OneLine=false was defaulting to UiText's bottom-pinned transcript flow (VerticalJustify honored only via ConfigureDatState, which this synthesized element never calls). Added UiText.HonorVerticalJustification so a non-DAT controller can opt the scrollable path into VerticalJustify without a full LayoutDesc binding; SpewBoxController sets VerticalJustify=Top so lines flow from the top of the 450x72 box, matching newest-at-top insert semantics. Noted as invented-pending- measurement in AP-178's row (no new row). 3. Documented the deliberate inversion of UiText.LinesProvider's oldest-first contract in SpewBoxController.Tick (SpewBoxVM.Lines feeds newest-first, which is correct specifically because the box is now top-aligned) and added a test pinning the rendered order (newer message is the topmost line), driving root.Tick. 4. Fixed the stale "retail's code default, 1" comment in SpewBoxControllerTests — MaxConcurrentItems is the shipped LayoutDesc's AUTHORED value, 4. 5. Added the matching unmapped-id diagnostics line to LiveSessionRuntimeFactory's ShowWeenieError sink, matching the pattern GameEventWiring's WeenieError/WeenieErrorWithString handlers already use. 6. Corrected the "EXHAUSTIVE Portal sweep found ZERO" overclaim in SpewBoxLayoutDumpDiagnostic: the loop's id source was DatCollection's top-level aggregate GetAllIdsOfType<LayoutDesc>(), not dats.Portal's own (which reports a count of ZERO for this type), so querying those ids against dats.Portal.TryGet established nothing about Portal either way. Corrected the same overclaim echoed in SpewBoxState's MaxConcurrentItems doc comment and in AP-178's register text (both the table row and the section-header history line). What's actually established: dats.Local hosts the SpewBox layout at 0x21000011; whether Portal also carries a copy remains unestablished. 7. Added a test exercising the full ShowWeenieError -> AddText -> SpewBox path for id 0x0561 (the 50-friends-cap refusal) in LiveSessionCommandRouterTests, mirroring LiveSessionRuntimeFactory's ShowWeenieError closure exactly since every other LiveSessionRuntimeFactory test in this tree is a source-text conformance grep, not an instantiation. Ledger: CH2 ledger row's review column now reads REJECT -> reworkede0e78883-> re-review APPROVE-WITH-FIXES -> nits (this commit); Status header flips CH2 to code-complete/closed pending the user gate, CH3 next. Build green; touched-project tests green (19/19 new/changed, 4351/3354 App.Tests unaffected pass); full Release suite 11,916 passed / 4 skipped / 0 failed (baseline 11,914/4/0 plus the two new tests this commit adds). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
235820b4f6
commit
233c30d13f
9 changed files with 216 additions and 41 deletions
|
|
@ -1,4 +1,5 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.UI.Layout;
|
||||
using AcDream.UI.Abstractions.Panels.SpewBox;
|
||||
|
||||
namespace AcDream.App.UI;
|
||||
|
|
@ -83,11 +84,23 @@ internal sealed class SpewBoxController : IDisposable
|
|||
/// (<c>leftEdge=3</c>/<c>rightEdge=3</c>, "centered" per
|
||||
/// <c>ElementReader.ToAnchors</c>'s doc comment) mean the box is a
|
||||
/// FIXED-width block horizontally centered in its parent, not a
|
||||
/// full-viewport stretch — the constructor below anchors it that way
|
||||
/// (a one-time centered <see cref="UiText.Left"/> computed against
|
||||
/// <see cref="UiRoot.Width"/>, <see cref="AnchorEdges.Top"/> only) since
|
||||
/// <see cref="AnchorEdges"/> has no "centered, fixed-width" flag
|
||||
/// combination to express retail's mode 3 directly.
|
||||
/// full-viewport stretch. <see cref="AnchorEdges"/> has no "centered,
|
||||
/// fixed-width" flag combination to express retail's mode 3 directly, so
|
||||
/// <see cref="Tick"/> recomputes a centered <see cref="UiText.Left"/>
|
||||
/// against the CURRENT <see cref="UiRoot.Width"/> every frame instead of
|
||||
/// anchoring.
|
||||
/// <para>
|
||||
/// CH2 re-review nit 1
|
||||
/// (<c>docs/plans/2026-08-09-chat-parity-campaign.md</c>): the original
|
||||
/// rework anchored with <see cref="AnchorEdges.Top"/> only, meaning
|
||||
/// <see cref="UiElement.ApplyAnchor"/> captured the constructor's
|
||||
/// one-time centered <c>Left</c> as a fixed left MARGIN
|
||||
/// (<c>ComputeAnchoredRect</c>'s Left/Right-both-false branch) and
|
||||
/// replayed that absolute pixel position forever — a window resize left
|
||||
/// the box off-center. <see cref="Anchors"/> is now
|
||||
/// <see cref="AnchorEdges.None"/> (so <c>ApplyAnchor</c> is a no-op) and
|
||||
/// centering is owned entirely by the per-frame recompute below.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
private const float SpewBoxWidth = 450f;
|
||||
private const float SpewBoxHeight = 72f;
|
||||
|
|
@ -127,17 +140,32 @@ internal sealed class SpewBoxController : IDisposable
|
|||
Name = "SpewBox",
|
||||
// Centered fixed-width block (retail's "mode 3" edge code on
|
||||
// both left and right) — see the AP-178 extent comment above.
|
||||
// This is only the FIRST frame's value; Tick recomputes it
|
||||
// every frame against the current root width (CH2 re-review
|
||||
// nit 1 — see the extent comment's nit-1 paragraph).
|
||||
Left = (root.Width - SpewBoxWidth) / 2f,
|
||||
Top = TopOffset,
|
||||
Width = SpewBoxWidth,
|
||||
Height = SpewBoxHeight,
|
||||
Anchors = AnchorEdges.Top,
|
||||
Anchors = AnchorEdges.None,
|
||||
Centered = true,
|
||||
// AUTHORED MaxConcurrentItems is 4, not retail's code-default 1
|
||||
// (see SpewBoxState.MaxConcurrentItems) — OneLine=true would
|
||||
// silently collapse the box back down to showing only the
|
||||
// newest of up to 4 concurrent lines.
|
||||
OneLine = false,
|
||||
// CH2 re-review nit 2 (docs/plans/2026-08-09-chat-parity-campaign.md):
|
||||
// retail's own authored vertical justification for THIS element
|
||||
// is unknown (register row AP-178 covers presentation
|
||||
// unknowns) — top-aligned flow is acdream's own choice, made
|
||||
// because it is the only placement consistent with "lines flow
|
||||
// from the top, newest on top" (see Tick's ordering comment).
|
||||
// HonorVerticalJustification opts the scrollable multi-line
|
||||
// path into VerticalJustify without a full ConfigureDatState
|
||||
// LayoutDesc binding, which this synthesized element does not
|
||||
// have.
|
||||
VerticalJustify = VJustify.Top,
|
||||
HonorVerticalJustification = true,
|
||||
ClickThrough = true,
|
||||
ZOrder = int.MaxValue,
|
||||
DefaultColor = SpewBoxColor,
|
||||
|
|
@ -166,10 +194,29 @@ internal sealed class SpewBoxController : IDisposable
|
|||
/// </param>
|
||||
private void Tick(double nowSeconds)
|
||||
{
|
||||
// SpewBoxVM.Lines returns newest-first, matching retail's
|
||||
// InsertItem(item, 0) — with OneLine now false and the AUTHORED
|
||||
// MaxConcurrentItems == 4 (see SpewBoxState.MaxConcurrentItems),
|
||||
// up to 4 lines render, newest on top.
|
||||
// CH2 re-review nit 1 (docs/plans/2026-08-09-chat-parity-campaign.md):
|
||||
// Anchors is AnchorEdges.None (see the AP-178 extent comment on
|
||||
// SpewBoxWidth), so nothing else recenters this element on a
|
||||
// window resize — recompute every frame against the CURRENT root
|
||||
// width rather than the width captured once at construction.
|
||||
_text.Left = (_root.Width - SpewBoxWidth) / 2f;
|
||||
|
||||
// CH2 re-review nit 3 (docs/plans/2026-08-09-chat-parity-campaign.md):
|
||||
// deliberate inversion of UiText.LinesProvider's documented
|
||||
// contract ("Provider of the lines to show, oldest-first" —
|
||||
// UiText.cs). SpewBoxVM.Lines returns newest-first, matching
|
||||
// retail's InsertItem(item, 0), and this controller feeds that
|
||||
// order straight through WITHOUT reversing it. That is correct
|
||||
// here specifically because the box is top-aligned
|
||||
// (VerticalJustify.Top + HonorVerticalJustification above): index 0
|
||||
// of the lines array draws at the TOP of the box, so feeding
|
||||
// newest-first into a slot documented as oldest-first is exactly
|
||||
// what makes "newest line on top" true. Reversing the feed to
|
||||
// satisfy the doc comment literally would flip the visible order to
|
||||
// oldest-on-top, which is wrong for this element — see
|
||||
// SpewBoxControllerTests' rendered-order test. With OneLine now
|
||||
// false and the AUTHORED MaxConcurrentItems == 4 (see
|
||||
// SpewBoxState.MaxConcurrentItems), up to 4 lines render.
|
||||
IReadOnlyList<SpewBoxLine> lines = _vm.Lines(nowSeconds);
|
||||
_text.Visible = lines.Count > 0;
|
||||
if (lines.Count == 0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue