fix #394 #395 #396: OP8 re-gate round — caption font, retail key names, capture dialog

Three findings from the user's first Configure Keyboard look (OP8 gate,
2026-08-14), each root-caused against the named retail decomp:

- #394 row-caption font: the synthesized action-label UiText never set
  DatFont and fell to the debug bitmap font. The authored row template
  (0x21000009/0x1000002F, retail UIOption_ActionKeyMap) carries FontDid
  0x4000000A (18px serif) — Bind now takes resolveTemplateFont and applies
  the template's own authored font, resolved once per template pair.

- #395 key captions: raw enum spellings ("Shift+ShiftLeft") replaced by the
  port of CInputManager_WIN32::GetNameFromKey @0x00687F40 /
  GetNameFromKey_Internal @0x00687800 (RetailKeyNames): DAT string-table
  override by DIK-name hash (key enum 4 -> 0x2300000A, meta enum 5 ->
  0x2300000B, delimiter enum 3 -> 0x23000007 — GetDIDByEnum category 4,
  live-probed), else the OS keyboard layout's own key name ("SKIFT") via
  PlatformKeyNameProvider (Win32 GetKeyNameTextW — register row AD-96 for
  the DirectInput-vs-GetKeyNameText adaptation), else the DIK-suffix
  spelling. Bare modifier-key bindings show only the key name.

- #396 capture feedback: clicking a mapping button now opens retail's
  instruction dialog (InitiateBinding @0x004899D0 -> OpenMapWarnDialog
  @0x00488A00): a type-2 WAIT dialog on retail's MapWarn queue key
  0x10000001 with ID_ActionKeyMap_MapInstructions (0x23000004, ACTION
  variable interpolated), closed on key hit or ESC through the capture
  callback; capture is not armed if the dialog cannot open, matching
  retail. New RetailWaitDialogView (wait root 0x31 — same authored
  popup/message pair 0x3D/0x3E as the confirmation root, live-DAT probed)
  behind a shared IRetailDialogView presenter seam.

Probe evidence (env-gated, kept):
KeyboardConfigLiveMountProbeTests.ProbeKeyboardFontsAndKeyNameStrings.
Register: AD-96 filed. Gate script OP8 section updated (step 4 rewritten;
the "pressed/active state is enough" contract is retired).

Full Release solution suite green (13,424 passed / 4 skips).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-14 13:45:55 +02:00
parent 1528e5693b
commit 30fa6ee507
16 changed files with 1105 additions and 24 deletions

View file

@ -329,6 +329,55 @@ public sealed class RetailDialogFactoryTests
Assert.False(factory.IsOpen);
}
/// <summary>
/// OP8 re-gate (2026-08-14): the type-2 wait dialog — retail's MapWarn
/// capture-instruction shape (<c>OpenMapWarnDialog @ 0x00488A00</c>):
/// text-only, no buttons wire a result, closed programmatically by the
/// opener via <see cref="RetailDialogFactory.CloseDialog"/>.
/// </summary>
[Fact]
public void MakeWait_CreatesTextOnlyModal_ClosedByTheOpener()
{
var root = new UiRoot { Width = 1024f, Height = 768f };
var layouts = new List<ImportedLayout>();
// The shipped catalog authors the SAME popup/message child ids
// (0x3D/0x3E) under the wait root 0x31 as under the confirmation root
// (live-DAT probed 2026-08-14); the committed fixture only carries the
// confirmation subtree, which therefore stands in structurally here.
var factory = new RetailDialogFactory(root, type =>
{
Assert.Equal(RetailDialogType.Wait, type);
ImportedLayout layout = FixtureLoader.LoadConfirmationDialog();
layouts.Add(layout);
return layout;
});
uint context = factory.MakeWait(
"The next key you press will be mapped.", queueKey: 0x10000001u);
Assert.NotEqual(0u, context);
ImportedLayout layout = Assert.Single(layouts);
Assert.Same(layout.Root, root.Modal);
Assert.Equal("The next key you press will be mapped.", Message(layout));
Assert.True(factory.CloseDialog(context));
Assert.Null(root.Modal);
Assert.False(factory.IsOpen);
}
[Fact]
public void WaitData_CarriesRetailsMapWarnPropertyShape()
{
RetailDialogData data = RetailDialogData.Wait("text");
// OpenMapWarnDialog @ 0x00488A00: 0x8E=2 (Wait), 0xAC=true, 0xC5=text.
Assert.Equal(
(uint)RetailDialogType.Wait,
data.GetUInt32(RetailDialogProperty.Type));
Assert.True(data.GetBoolean(RetailDialogProperty.ElementAttribute40));
Assert.Equal("text", data.GetString(RetailDialogProperty.Message));
}
private static RetailDialogFactory CreateFactory(
UiRoot root,
List<ImportedLayout> layouts)