diff --git a/Content.Client/Administration/QuickDialogSystem.cs b/Content.Client/Administration/QuickDialogSystem.cs
index 84236c5a3e..d015b7769b 100644
--- a/Content.Client/Administration/QuickDialogSystem.cs
+++ b/Content.Client/Administration/QuickDialogSystem.cs
@@ -1,13 +1,8 @@
-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
-
///
/// This handles the client portion of quick dialogs.
///
@@ -21,149 +16,22 @@ public sealed class QuickDialogSystem : EntitySystem
private void OpenDialog(QuickDialogOpenEvent ev)
{
- var window = new FancyWindow()
- {
- Title = ev.Title
- };
+ 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 entryContainer = new BoxContainer()
- {
- Orientation = BoxContainer.LayoutOrientation.Vertical,
- Margin = new Thickness(8),
- };
-
- var promptsDict = new Dictionary();
-
- 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 buttonsBox = new BoxContainer()
- {
- 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));
- }
- };
-
- entryContainer.AddChild(buttonsBox);
-
- window.ContentsContainer.AddChild(entryContainer);
-
- window.MinWidth *= 2; // Just double it.
-
- window.OpenCentered();
-
- return;
-
- void Confirm()
+ window.OnConfirmed += responses =>
{
RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
- promptsDict.Select(x => (x.Key, x.Value.Text)).ToDictionary(x => x.Key, x => x.Text),
+ responses,
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;
+ window.OnCancelled += () =>
+ {
+ RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
+ new(),
+ QuickDialogButtonFlag.CancelButton));
+ };
}
}
diff --git a/Content.Client/UserInterface/Controls/DialogWindow.xaml b/Content.Client/UserInterface/Controls/DialogWindow.xaml
new file mode 100644
index 0000000000..4f9a8a49f9
--- /dev/null
+++ b/Content.Client/UserInterface/Controls/DialogWindow.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/UserInterface/Controls/DialogWindow.xaml.cs b/Content.Client/UserInterface/Controls/DialogWindow.xaml.cs
new file mode 100644
index 0000000000..f50aca581b
--- /dev/null
+++ b/Content.Client/UserInterface/Controls/DialogWindow.xaml.cs
@@ -0,0 +1,147 @@
+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
+
+///
+/// Client-side dialog with multiple prompts.
+/// Used by admin tools quick dialog system among other things.
+///
+[GenerateTypedNameReferences]
+public sealed partial class DialogWindow : FancyWindow
+{
+ ///
+ /// 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.
+ ///
+ public Action>? OnConfirmed;
+
+ ///
+ /// Action for when the cancel button is pressed or the window is closed.
+ ///
+ public Action? OnCancelled;
+
+ ///
+ /// 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.
+ ///
+ private bool _finished;
+
+ private List<(string, LineEdit)> _promptLines;
+
+ ///
+ /// Create and open a new dialog with some prompts.
+ ///
+ /// String to use for the window title.
+ /// Quick dialog entries to create prompts with.
+ /// Whether to have an Ok button.
+ /// Whether to have a Cancel button. Closing the window will still cancel it.
+ ///
+ /// Won't do anything on its own, you need to handle or network with and .
+ ///
+ public DialogWindow(string title, List 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) 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();
+ 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
+}
diff --git a/Content.Shared/Administration/QuickDialogOpenEvent.cs b/Content.Shared/Administration/QuickDialogOpenEvent.cs
index 3479de5736..f4e77ab999 100644
--- a/Content.Shared/Administration/QuickDialogOpenEvent.cs
+++ b/Content.Shared/Administration/QuickDialogOpenEvent.cs
@@ -26,7 +26,7 @@ public sealed class QuickDialogOpenEvent : EntityEventArgs
///
/// The buttons presented for the user.
///
- public QuickDialogButtonFlag Buttons = QuickDialogButtonFlag.OkButton;
+ public QuickDialogButtonFlag Buttons = QuickDialogButtonFlag.OkButton | QuickDialogButtonFlag.CancelButton;
public QuickDialogOpenEvent(string title, List prompts, int dialogId, QuickDialogButtonFlag buttons)
{
@@ -87,11 +87,17 @@ public sealed class QuickDialogEntry
///
public string Prompt;
- public QuickDialogEntry(string fieldId, QuickDialogEntryType type, string prompt)
+ ///
+ /// String to replace the type-specific placeholder with.
+ ///
+ public string? Placeholder;
+
+ public QuickDialogEntry(string fieldId, QuickDialogEntryType type, string prompt, string? placeholder = null)
{
FieldId = fieldId;
Type = type;
Prompt = prompt;
+ Placeholder = placeholder;
}
}