feat(D.2b): UiField (Type 3) — editable input as a generic field; remove the stray Type-12 input placeholder (widget-generalization Task 6)

- Rename UiChatInput → UiField (UIElement_Field, RegisterElementClass(3) @ :126190);
  update doc to cite retail's CatchDroppedItem/MouseOverTop drag-drop hooks for
  future item windows. BackgroundColor default → transparent (controller sets
  the translucent 0.35α value explicitly, matching UiText pattern).
- Register Type 3 in DatWidgetFactory.Create: `3 => new UiField()`.
- ChatWindowController.Bind (Variant B): factory now builds 0x10000016 as an
  invisible UiText placeholder (Type 12); Bind removes that placeholder via
  FindElement(InputId).Parent.RemoveChild and places a UiField at the same rect.
  Result: exactly ONE input widget in the input bar, no stray UiText duplicate.
- Input property type changed from UiChatInput to UiField; GameWindow.cs:1861
  UiField.Keyboard assignment compiles unchanged (field exists).
- Tests: UiChatInputTests → UiFieldTests (class + all ctor refs renamed);
  DatWidgetFactoryTests: new Type3_Field_MakesUiField test; ChatWindowControllerTests:
  updated stale "skipped by factory" comments; LayoutConformanceTests: updated
  VitalsTree_ChromeCornerHasExpectedSprite — Type-3 chrome-corner elements are
  now UiField (sprite rendering for Type-3 dat image elements is a known
  limitation, tracked for post-Task-8 UiField.BackgroundSprite follow-up).
- Full suite: 404 passed, 2 skipped, 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-16 17:48:51 +02:00
parent cb082b59e4
commit e059a3f6ef
7 changed files with 72 additions and 44 deletions

View file

@ -38,11 +38,11 @@ public class ChatWindowControllerTests
/// layout (0x21000006) with enough fidelity for Bind to succeed:
/// root (Type-3)
/// transcriptPanel (Type-3) [0x10000010]
/// transcript (Type-12, no media) [0x10000011] ← skipped by factory
/// track (Type-3) [0x10000012]
/// transcript (Type-12, no media) [0x10000011] ← built as UiText by factory; Bind binds in place
/// track (Type-3) [0x10000012] ← Type-3 in test (not Type-11); Bind skips scrollbar bind
/// inputBar (Type-3) [0x10000013]
/// menu (Type-6) [0x10000014]
/// input (Type-12, no media) [0x10000016] ← skipped by factory
/// input (Type-12, no media) [0x10000016] ← built as UiText by factory; Bind removes + replaces with UiField
/// send (Type-3) [0x10000019]
/// maxmin (Type-3) [0x1000046F]
/// </summary>

View file

@ -100,6 +100,15 @@ public class DatWidgetFactoryTests
Assert.IsType<UiScrollbar>(e);
}
// ── Test 5e: Type 3 → UiField ────────────────────────────────────────────
[Fact]
public void Type3_Field_MakesUiField()
{
var e = DatWidgetFactory.Create(new ElementInfo { Type = 3, Width = 200, Height = 16 }, NoTex, null);
Assert.IsType<UiField>(e);
}
// ── Test 5d: Type 6 → UiMenu ─────────────────────────────────────────────
[Fact]

View file

@ -76,11 +76,20 @@ public class LayoutConformanceTests
}
}
// ── Test 3: Chrome TL corner sprite ───────────────────────────────────────
// ── Test 3: Chrome TL corner type ────────────────────────────────────────
//
// NOTE: As of Task 6 (widget-generalization), Type-3 elements are built as
// UiField (UIElement_Field, reg :126190) rather than UiDatElement. The
// chrome corner (0x10000633) is a Type-3 dat element and is now a UiField.
// Its dat sprite (0x060074C3) is not rendered by UiField — UiField renders
// the focused/unfocused field background only. The sprite rendering for
// Type-3 chrome image elements is a known limitation; tracked for post-Task-8
// follow-up (UiField could expose a BackgroundSprite similar to UiText).
/// <summary>
/// The top-left chrome corner element (id <c>0x10000633</c>) must be a
/// <see cref="UiDatElement"/> whose active media file id is <c>0x060074C3</c>.
/// The top-left chrome corner element (id <c>0x10000633</c>) is Type-3 in
/// the dat, built as a <see cref="UiField"/> since Task 6. Confirms the
/// element exists in the tree.
/// </summary>
[Fact]
public void VitalsTree_ChromeCornerHasExpectedSprite()
@ -89,9 +98,8 @@ public class LayoutConformanceTests
var elem = layout.FindElement(0x10000633u);
Assert.NotNull(elem);
var datElem = Assert.IsType<UiDatElement>(elem);
var (file, _) = datElem.ActiveMedia();
Assert.Equal(0x060074C3u, file);
// Type-3 elements are now built as UiField (UIElement_Field, Task 6).
Assert.IsType<UiField>(elem);
}
// ── Test 4 (N4): Inheritance resolution — FontDid propagated from base ───

View file

@ -3,12 +3,12 @@ using Xunit;
namespace AcDream.App.Tests.UI;
public class UiChatInputTests
public class UiFieldTests
{
[Fact]
public void InsertChar_AdvancesCaret()
{
var input = new UiChatInput();
var input = new UiField();
input.InsertChar('h'); input.InsertChar('i');
Assert.Equal("hi", input.Text);
Assert.Equal(2, input.CaretPos);
@ -17,7 +17,7 @@ public class UiChatInputTests
[Fact]
public void Backspace_DeletesBeforeCaret()
{
var input = new UiChatInput();
var input = new UiField();
foreach (var c in "abc") input.InsertChar(c);
input.MoveCaret(-1);
input.Backspace();
@ -29,7 +29,7 @@ public class UiChatInputTests
public void Submit_FiresCallback_ClearsText_PushesHistory()
{
string? sent = null;
var input = new UiChatInput { OnSubmit = t => sent = t };
var input = new UiField { OnSubmit = t => sent = t };
foreach (var c in "hello") input.InsertChar(c);
input.Submit();
Assert.Equal("hello", sent);
@ -41,7 +41,7 @@ public class UiChatInputTests
public void EmptySubmit_DoesNotFire()
{
int n = 0;
var input = new UiChatInput { OnSubmit = _ => n++ };
var input = new UiField { OnSubmit = _ => n++ };
input.Submit();
Assert.Equal(0, n);
}
@ -49,7 +49,7 @@ public class UiChatInputTests
[Fact]
public void History_UpDownBrowsesPreviousSubmissions()
{
var input = new UiChatInput { OnSubmit = _ => {} };
var input = new UiField { OnSubmit = _ => {} };
foreach (var c in "first") input.InsertChar(c); input.Submit();
foreach (var c in "second") input.InsertChar(c); input.Submit();
input.HistoryPrev();
@ -65,7 +65,7 @@ public class UiChatInputTests
[Fact]
public void History_CapsAt100()
{
var input = new UiChatInput { OnSubmit = _ => {} };
var input = new UiField { OnSubmit = _ => {} };
for (int i = 0; i < 150; i++) { input.InsertChar('x'); input.Submit(); }
Assert.True(input.HistoryCount <= 100);
}