160 lines
6 KiB
C#
160 lines
6 KiB
C#
namespace AcDream.App.UI.Layout;
|
|
|
|
/// <summary>
|
|
/// Retail type-5 <c>ConfirmationTextInputDialog</c> (class type
|
|
/// <c>0x15</c>, catalog root <c>0x2C</c>). Accept stores the field text under
|
|
/// property <c>0x9C</c>; reject/Escape stores the empty string. Character
|
|
/// deletion is the first consumer and performs retail's case-insensitive
|
|
/// comparison with the localized <c>DELETE</c> response in its callback.
|
|
/// </summary>
|
|
internal sealed class RetailConfirmationTextInputDialogView : IRetailDialogView
|
|
{
|
|
public const uint RootElementId = 0x2Cu;
|
|
public const uint InputElementId = 0x2Cu;
|
|
public const uint AcceptButtonId = 0x2Eu;
|
|
public const uint RejectButtonId = 0x2Fu;
|
|
public const uint PopupElementId = 0x3Du;
|
|
public const uint MessageElementId = 0x3Eu;
|
|
|
|
private readonly UiRoot _host;
|
|
private readonly RetailDialogData _data;
|
|
private readonly uint _context;
|
|
private readonly Action<uint> _closeDialog;
|
|
private readonly UiElement _popup;
|
|
private readonly UiText _message;
|
|
private readonly UiField _input;
|
|
private readonly UiButton _accept;
|
|
private readonly UiButton _reject;
|
|
private readonly float _basePopupHeight;
|
|
private readonly float _baseMessageHeight;
|
|
private bool _focusPending = true;
|
|
|
|
public RetailConfirmationTextInputDialogView(
|
|
UiRoot host,
|
|
ImportedLayout layout,
|
|
RetailDialogData data,
|
|
uint context,
|
|
Action<uint> closeDialog)
|
|
{
|
|
_host = host ?? throw new ArgumentNullException(nameof(host));
|
|
ArgumentNullException.ThrowIfNull(layout);
|
|
_data = data ?? throw new ArgumentNullException(nameof(data));
|
|
_context = context;
|
|
_closeDialog = closeDialog ?? throw new ArgumentNullException(nameof(closeDialog));
|
|
|
|
Root = layout.Root as UiDialogRoot
|
|
?? throw new ArgumentException(
|
|
"Confirmation-text-input layout root is not a UiDialogRoot.",
|
|
nameof(layout));
|
|
_popup = layout.FindElement(PopupElementId)
|
|
?? throw new ArgumentException(
|
|
"Confirmation-text-input layout is missing popup element 0x3D.",
|
|
nameof(layout));
|
|
_message = layout.FindElement(MessageElementId) as UiText
|
|
?? throw new ArgumentException(
|
|
"Confirmation-text-input layout is missing text element 0x3E.",
|
|
nameof(layout));
|
|
// The field deliberately repeats the root's numeric id. ImportedLayout
|
|
// registers descendants after ancestors, matching GetChildRecursive's
|
|
// effective result for this catalog shape.
|
|
_input = layout.FindElement(InputElementId) as UiField
|
|
?? throw new ArgumentException(
|
|
"Confirmation-text-input layout is missing input field 0x2C.",
|
|
nameof(layout));
|
|
_accept = layout.FindElement(AcceptButtonId) as UiButton
|
|
?? throw new ArgumentException(
|
|
"Confirmation-text-input layout is missing accept button 0x2E.",
|
|
nameof(layout));
|
|
_reject = layout.FindElement(RejectButtonId) as UiButton
|
|
?? throw new ArgumentException(
|
|
"Confirmation-text-input layout is missing reject button 0x2F.",
|
|
nameof(layout));
|
|
|
|
_basePopupHeight = _popup.Height;
|
|
_baseMessageHeight = _message.Height;
|
|
_popup.LayoutPolicy = null;
|
|
_popup.Anchors = AnchorEdges.None;
|
|
_message.LayoutPolicy = null;
|
|
_message.Anchors = AnchorEdges.None;
|
|
_message.Padding = 0f;
|
|
_message.Selectable = false;
|
|
_input.ClearOnSubmit = false;
|
|
_input.RecordHistory = false;
|
|
|
|
if (_data.GetString(RetailDialogProperty.TextInputAcceptLabel) is { } acceptLabel)
|
|
_accept.Label = acceptLabel;
|
|
if (_data.GetString(RetailDialogProperty.TextInputRejectLabel) is { } rejectLabel)
|
|
_reject.Label = rejectLabel;
|
|
|
|
Root.Cancel = Reject;
|
|
_accept.OnClick = Accept;
|
|
_reject.OnClick = Reject;
|
|
_input.OnSubmit = _ => Accept();
|
|
SetMessage(_data.GetString(RetailDialogProperty.Message) ?? string.Empty);
|
|
SizeAndCenter();
|
|
}
|
|
|
|
public UiDialogRoot Root { get; }
|
|
|
|
public void Tick()
|
|
{
|
|
SizeAndCenter();
|
|
if (_focusPending && Root.Parent is not null)
|
|
{
|
|
_host.SetKeyboardFocus(_input);
|
|
_focusPending = false;
|
|
}
|
|
}
|
|
|
|
public void SetPendingCount(int count)
|
|
{
|
|
// This catalog root authors no pending-count display.
|
|
}
|
|
|
|
public void DetachHandlers()
|
|
{
|
|
Root.Cancel = null;
|
|
_accept.OnClick = null;
|
|
_reject.OnClick = null;
|
|
_input.OnSubmit = null;
|
|
}
|
|
|
|
private void Accept()
|
|
{
|
|
_data.Set(RetailDialogProperty.TextInputResult, _input.Text);
|
|
_closeDialog(_context);
|
|
}
|
|
|
|
private void Reject()
|
|
{
|
|
_data.Set(RetailDialogProperty.TextInputResult, string.Empty);
|
|
_closeDialog(_context);
|
|
}
|
|
|
|
private void SetMessage(string text)
|
|
{
|
|
float maximumWidth = Math.Max(1f, _message.Width - 2f * _message.Padding);
|
|
Func<string, float> measure = _message.DatFont is { } font
|
|
? font.MeasureWidth
|
|
: static value => value.Length * 8f;
|
|
IReadOnlyList<string> wrapped = UiText.WrapWords(text, measure, maximumWidth);
|
|
var lines = new UiText.Line[wrapped.Count];
|
|
for (int i = 0; i < wrapped.Count; i++)
|
|
lines[i] = new UiText.Line(wrapped[i], _message.DefaultColor);
|
|
_message.LinesProvider = () => lines;
|
|
|
|
float lineHeight = _message.DatFont?.LineHeight ?? 16f;
|
|
_message.Height = Math.Max(_baseMessageHeight, lines.Length * lineHeight);
|
|
_popup.Height = _basePopupHeight + (_message.Height - _baseMessageHeight);
|
|
}
|
|
|
|
private void SizeAndCenter()
|
|
{
|
|
Root.Left = 0f;
|
|
Root.Top = 0f;
|
|
Root.Width = _host.Width;
|
|
Root.Height = _host.Height;
|
|
_popup.Left = MathF.Round((Root.Width - _popup.Width) * 0.5f);
|
|
_popup.Top = MathF.Round((Root.Height - _popup.Height) * 0.5f);
|
|
}
|
|
}
|