Implement retail character management screen

This commit is contained in:
Erik 2026-08-14 20:29:19 +02:00
parent 5535d0adac
commit 6cfab727f1
19 changed files with 2435 additions and 6 deletions

View file

@ -378,6 +378,68 @@ public sealed class RetailDialogFactoryTests
Assert.Equal("text", data.GetString(RetailDialogProperty.Message));
}
[Fact]
public void MessageDialog_UsesAuthoredOkButtonAndReturnsThroughFactoryCallback()
{
var root = new UiRoot { Width = 800f, Height = 600f };
var layouts = new List<(RetailDialogType Type, ImportedLayout Layout)>();
using var factory = new RetailDialogFactory(root, type =>
{
ImportedLayout layout = BuildDialogLayout(type);
layouts.Add((type, layout));
return layout;
});
bool completed = false;
factory.MakeMessage("Character selection failed.", _ => completed = true);
(RetailDialogType type, ImportedLayout layout) = Assert.Single(layouts);
Assert.Equal(RetailDialogType.Message, type);
Assert.Equal("Character selection failed.", Message(layout));
Button(layout, RetailMessageDialogView.OkButtonId).OnClick!();
Assert.True(completed);
Assert.False(factory.IsOpen);
Assert.Null(root.Modal);
}
[Fact]
public void ConfirmationTextInput_AcceptsTypedResultAndRejectsWithEmptyResult()
{
var root = new UiRoot { Width = 800f, Height = 600f };
var layouts = new List<(RetailDialogType Type, ImportedLayout Layout)>();
using var factory = new RetailDialogFactory(root, type =>
{
ImportedLayout layout = BuildDialogLayout(type);
layouts.Add((type, layout));
return layout;
});
var results = new List<string>();
factory.MakeConfirmationTextInput(
"Type DELETE.",
data => results.Add(
data.GetString(RetailDialogProperty.TextInputResult) ?? "<null>"));
ImportedLayout accepted = layouts[^1].Layout;
var field = Assert.IsType<UiField>(
accepted.FindElement(RetailConfirmationTextInputDialogView.InputElementId));
Assert.Same(field, root.KeyboardFocus);
field.SetText("delete");
Button(accepted, RetailConfirmationTextInputDialogView.AcceptButtonId).OnClick!();
factory.MakeConfirmationTextInput(
"Type DELETE.",
data => results.Add(
data.GetString(RetailDialogProperty.TextInputResult) ?? "<null>"));
ImportedLayout rejected = layouts[^1].Layout;
UiDialogRoot rejectedRoot = Assert.IsType<UiDialogRoot>(rejected.Root);
Assert.NotNull(rejectedRoot.Cancel);
rejectedRoot.Cancel!();
Assert.Equal(["delete", ""], results);
Assert.False(factory.IsOpen);
Assert.Null(root.KeyboardFocus);
}
private static RetailDialogFactory CreateFactory(
UiRoot root,
List<ImportedLayout> layouts)
@ -401,4 +463,91 @@ public sealed class RetailDialogFactoryTests
private static string Message(ImportedLayout layout)
=> string.Join(" ", Assert.IsType<UiText>(layout.FindElement(
RetailConfirmationDialogView.MessageElementId)).LinesProvider().Select(static line => line.Text));
internal static ImportedLayout BuildDialogLayout(RetailDialogType type)
{
uint rootId = RetailDialogFactory.RootElementId(type);
uint rootType = type switch
{
RetailDialogType.Message => 0x17u,
RetailDialogType.ConfirmationTextInput => 0x15u,
RetailDialogType.Wait => 0x19u,
_ => 0x13u,
};
var root = new ElementInfo
{
Id = rootId,
Type = rootType,
Width = 800f,
Height = 600f,
};
var popup = new ElementInfo
{
Id = 0x3Du,
Type = 3u,
Width = 400f,
Height = type == RetailDialogType.ConfirmationTextInput ? 125f : 95f,
};
popup.Children.Add(new ElementInfo
{
Id = 0x3Eu,
Type = 12u,
X = 15f,
Y = 15f,
Width = 370f,
Height = 18f,
});
if (type == RetailDialogType.Message)
{
popup.Children.Add(new ElementInfo
{
Id = RetailMessageDialogView.OkButtonId,
Type = 1u,
X = 160f,
Y = 48f,
Width = 80f,
Height = 32f,
});
}
else if (type == RetailDialogType.ConfirmationTextInput)
{
var field = new ElementInfo
{
Id = RetailConfirmationTextInputDialogView.InputElementId,
Type = 12u,
X = 4f,
Y = 43f,
Width = 152f,
Height = 16f,
};
var direct = new UiStateInfo { Id = UiStateInfo.DirectStateId };
direct.Properties.Values[0x16u] = new UiPropertyValue
{
Kind = UiPropertyKind.Bool,
BoolValue = true,
};
field.States.Add(UiStateInfo.DirectStateId, direct);
popup.Children.Add(field);
popup.Children.Add(new ElementInfo
{
Id = RetailConfirmationTextInputDialogView.AcceptButtonId,
Type = 1u,
X = 80f,
Y = 78f,
Width = 80f,
Height = 32f,
});
popup.Children.Add(new ElementInfo
{
Id = RetailConfirmationTextInputDialogView.RejectButtonId,
Type = 1u,
X = 240f,
Y = 78f,
Width = 80f,
Height = 32f,
});
}
root.Children.Add(popup);
return LayoutImporter.Build(root, _ => (0u, 0, 0), null);
}
}