-using System.Linq;
using Content.Client.UserInterface.Controls;
using Content.Shared.Administration;
-using Robust.Client.UserInterface;
-using Robust.Client.UserInterface.Controls;
namespace Content.Client.Administration;
-// mfw they ported input() from BYOND
-
/// <summary>
/// This handles the client portion of quick dialogs.
/// </summary>
private void OpenDialog(QuickDialogOpenEvent ev)
{
- var window = new FancyWindow()
- {
- Title = ev.Title
- };
-
- var entryContainer = new BoxContainer()
- {
- Orientation = BoxContainer.LayoutOrientation.Vertical,
- Margin = new Thickness(8),
- };
-
- var promptsDict = new Dictionary<string, LineEdit>();
-
- for (var index = 0; index < ev.Prompts.Count; index++)
- {
- var entry = ev.Prompts[index];
- var entryBox = new BoxContainer()
- {
- Orientation = BoxContainer.LayoutOrientation.Horizontal
- };
-
- entryBox.AddChild(new Label { Text = entry.Prompt, HorizontalExpand = true, SizeFlagsStretchRatio = 0.5f });
- var edit = new LineEdit() { HorizontalExpand = true };
- entryBox.AddChild(edit);
- switch (entry.Type)
- {
- case QuickDialogEntryType.Integer:
- edit.IsValid += VerifyInt;
- edit.PlaceHolder = Loc.GetString("quick-dialog-ui-integer");
- break;
- case QuickDialogEntryType.Float:
- edit.IsValid += VerifyFloat;
- edit.PlaceHolder = Loc.GetString("quick-dialog-ui-float");
- break;
- case QuickDialogEntryType.ShortText:
- edit.IsValid += VerifyShortText;
- edit.PlaceHolder = Loc.GetString("quick-dialog-ui-short-text");
- break;
- case QuickDialogEntryType.LongText:
- edit.IsValid += VerifyLongText;
- edit.PlaceHolder = Loc.GetString("quick-dialog-ui-long-text");
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
-
- promptsDict.Add(entry.FieldId, edit);
- entryContainer.AddChild(entryBox);
-
- if (index == ev.Prompts.Count - 1)
- {
- // Last text box gets enter confirmation.
- // Only the last so you don't accidentally confirm early.
- edit.OnTextEntered += _ => Confirm();
- }
- }
+ var ok = (ev.Buttons & QuickDialogButtonFlag.OkButton) != 0;
+ var cancel = (ev.Buttons & QuickDialogButtonFlag.CancelButton) != 0;
+ var window = new DialogWindow(ev.Title, ev.Prompts, ok: ok, cancel: cancel);
- var buttonsBox = new BoxContainer()
+ window.OnConfirmed += responses =>
{
- Orientation = BoxContainer.LayoutOrientation.Horizontal,
- HorizontalAlignment = Control.HAlignment.Center,
- };
-
- var alreadyReplied = false;
-
- if ((ev.Buttons & QuickDialogButtonFlag.OkButton) != 0)
- {
- var okButton = new Button()
- {
- Text = Loc.GetString("quick-dialog-ui-ok"),
- };
-
- okButton.OnPressed += _ => Confirm();
-
- buttonsBox.AddChild(okButton);
- }
-
- if ((ev.Buttons & QuickDialogButtonFlag.OkButton) != 0)
- {
- var cancelButton = new Button()
- {
- Text = Loc.GetString("quick-dialog-ui-cancel"),
- };
-
- cancelButton.OnPressed += _ =>
- {
- RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
- new(),
- QuickDialogButtonFlag.CancelButton));
- alreadyReplied = true;
- window.Close();
- };
-
- buttonsBox.AddChild(cancelButton);
- }
-
- window.OnClose += () =>
- {
- if (!alreadyReplied)
- {
- RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
- new(),
- QuickDialogButtonFlag.CancelButton));
- }
+ RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
+ responses,
+ QuickDialogButtonFlag.OkButton));
};
- entryContainer.AddChild(buttonsBox);
-
- window.ContentsContainer.AddChild(entryContainer);
-
- window.MinWidth *= 2; // Just double it.
-
- window.OpenCentered();
-
- return;
-
- void Confirm()
+ window.OnCancelled += () =>
{
RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
- promptsDict.Select(x => (x.Key, x.Value.Text)).ToDictionary(x => x.Key, x => x.Text),
- QuickDialogButtonFlag.OkButton));
- alreadyReplied = true;
- window.Close();
- }
- }
-
- private bool VerifyInt(string input)
- {
- return int.TryParse(input, out var _);
- }
-
- private bool VerifyFloat(string input)
- {
- return float.TryParse(input, out var _);
- }
-
- private bool VerifyShortText(string input)
- {
- return input.Length <= 100;
- }
-
- private bool VerifyLongText(string input)
- {
- return input.Length <= 2000;
+ new(),
+ QuickDialogButtonFlag.CancelButton));
+ };
}
}
--- /dev/null
+<controls:FancyWindow xmlns="https://spacestation14.io" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls">
+ <BoxContainer Orientation="Vertical" Margin="8">
+ <BoxContainer Name="Prompts" Orientation="Vertical"/> <!-- Populated in constructor -->
+ <BoxContainer Orientation="Horizontal" HorizontalAlignment="Center">
+ <Button Name="OkButton" Text="{Loc 'quick-dialog-ui-ok'}"/>
+ <Button Name="CancelButton" Text="{Loc 'quick-dialog-ui-cancel'}"/>
+ </BoxContainer>
+ </BoxContainer>
+</controls:FancyWindow>
--- /dev/null
+using Content.Shared.Administration;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.UserInterface.Controls;
+
+// mfw they ported input() from BYOND
+
+/// <summary>
+/// Client-side dialog with multiple prompts.
+/// Used by admin tools quick dialog system among other things.
+/// </summary>
+[GenerateTypedNameReferences]
+public sealed partial class DialogWindow : FancyWindow
+{
+ /// <summary>
+ /// Action for when the ok button is pressed or the last field has enter pressed.
+ /// Results maps prompt FieldIds to the LineEdit's text contents.
+ /// </summary>
+ public Action<Dictionary<string, string>>? OnConfirmed;
+
+ /// <summary>
+ /// Action for when the cancel button is pressed or the window is closed.
+ /// </summary>
+ public Action? OnCancelled;
+
+ /// <summary>
+ /// Used to ensure that only one output action is invoked.
+ /// E.g. Pressing cancel will invoke then close the window, but OnClose will not invoke.
+ /// </summary>
+ private bool _finished;
+
+ private List<(string, LineEdit)> _promptLines;
+
+ /// <summary>
+ /// Create and open a new dialog with some prompts.
+ /// </summary>
+ /// <param name="title">String to use for the window title.</param>
+ /// <param name="entries">Quick dialog entries to create prompts with.</param>
+ /// <param name="ok">Whether to have an Ok button.</param>
+ /// <param name="cancel">Whether to have a Cancel button. Closing the window will still cancel it.</param>
+ /// <remarks>
+ /// Won't do anything on its own, you need to handle or network with <see cref="OnConfirmed"/> and <see cref="OnCancelled"/>.
+ /// </remarks>
+ public DialogWindow(string title, List<QuickDialogEntry> entries, bool ok = true, bool cancel = true)
+ {
+ RobustXamlLoader.Load(this);
+
+ Title = title;
+
+ OkButton.Visible = ok;
+ CancelButton.Visible = cancel;
+
+ _promptLines = new(entries.Count);
+
+ for (int i = 0; i < entries.Count; i++)
+ {
+ var entry = entries[i];
+
+ var box = new BoxContainer();
+ box.AddChild(new Label() { Text = entry.Prompt, HorizontalExpand = true, SizeFlagsStretchRatio = 0.5f });
+
+ var edit = new LineEdit() { HorizontalExpand = true };
+
+ (Func<string, bool>, string) pair = entry.Type switch
+ {
+ QuickDialogEntryType.Integer => (VerifyInt, "integer"),
+ QuickDialogEntryType.Float => (VerifyFloat, "float"),
+ QuickDialogEntryType.ShortText => (VerifyShortText, "short-text"),
+ QuickDialogEntryType.LongText => (VerifyLongText, "long-text"),
+ _ => throw new ArgumentOutOfRangeException()
+ };
+ var (valid, name) = pair;
+
+ edit.IsValid += valid;
+ // try use placeholder from the caller, fall back to the generic one for whatever type is being validated.
+ edit.PlaceHolder = entry.Placeholder ?? Loc.GetString($"quick-dialog-ui-{name}");
+
+ // Last text box gets enter confirmation.
+ // Only the last so you don't accidentally confirm early.
+ if (i == entries.Count - 1)
+ edit.OnTextEntered += _ => Confirm();
+
+ _promptLines.Add((entry.FieldId, edit));
+ box.AddChild(edit);
+ Prompts.AddChild(box);
+ }
+
+ OkButton.OnPressed += _ => Confirm();
+
+ CancelButton.OnPressed += _ =>
+ {
+ _finished = true;
+ OnCancelled?.Invoke();
+ Close();
+ };
+
+ OnClose += () =>
+ {
+ if (!_finished)
+ OnCancelled?.Invoke();
+ };
+
+ MinWidth *= 2; // Just double it.
+
+ OpenCentered();
+ }
+
+ private void Confirm()
+ {
+ var results = new Dictionary<string, string>();
+ foreach (var (field, edit) in _promptLines)
+ {
+ results[field] = edit.Text;
+ }
+
+ _finished = true;
+ OnConfirmed?.Invoke(results);
+ Close();
+ }
+
+ #region Input validation
+
+
+ private bool VerifyInt(string input)
+ {
+ return int.TryParse(input, out var _);
+ }
+
+ private bool VerifyFloat(string input)
+ {
+ return float.TryParse(input, out var _);
+ }
+
+ private bool VerifyShortText(string input)
+ {
+ return input.Length <= 100;
+ }
+
+ private bool VerifyLongText(string input)
+ {
+ return input.Length <= 2000;
+ }
+
+ #endregion
+}
/// <summary>
/// The buttons presented for the user.
/// </summary>
- public QuickDialogButtonFlag Buttons = QuickDialogButtonFlag.OkButton;
+ public QuickDialogButtonFlag Buttons = QuickDialogButtonFlag.OkButton | QuickDialogButtonFlag.CancelButton;
public QuickDialogOpenEvent(string title, List<QuickDialogEntry> prompts, int dialogId, QuickDialogButtonFlag buttons)
{
/// </summary>
public string Prompt;
- public QuickDialogEntry(string fieldId, QuickDialogEntryType type, string prompt)
+ /// <summary>
+ /// String to replace the type-specific placeholder with.
+ /// </summary>
+ public string? Placeholder;
+
+ public QuickDialogEntry(string fieldId, QuickDialogEntryType type, string prompt, string? placeholder = null)
{
FieldId = fieldId;
Type = type;
Prompt = prompt;
+ Placeholder = placeholder;
}
}