]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
make dialog window not evil (#24677)
authordeltanedas <39013340+deltanedas@users.noreply.github.com>
Thu, 1 Feb 2024 12:56:40 +0000 (12:56 +0000)
committerGitHub <noreply@github.com>
Thu, 1 Feb 2024 12:56:40 +0000 (23:56 +1100)
* add Placeholder and make default buttons flags consistent w old behaviour

* DialogWindow ops

* make QuickDialog use DialogWindow

* Update Content.Client/UserInterface/Controls/DialogWindow.xaml

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Content.Client/Administration/QuickDialogSystem.cs
Content.Client/UserInterface/Controls/DialogWindow.xaml [new file with mode: 0644]
Content.Client/UserInterface/Controls/DialogWindow.xaml.cs [new file with mode: 0644]
Content.Shared/Administration/QuickDialogOpenEvent.cs

index 84236c5a3e6b294db043f401e429d5aff662f3de..d015b7769b14c9a7fdaec75d21729f2f6024ca04 100644 (file)
@@ -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
-
 /// <summary>
 /// This handles the client portion of quick dialogs.
 /// </summary>
@@ -21,149 +16,22 @@ public sealed class QuickDialogSystem : EntitySystem
 
     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));
+        };
     }
 }
diff --git a/Content.Client/UserInterface/Controls/DialogWindow.xaml b/Content.Client/UserInterface/Controls/DialogWindow.xaml
new file mode 100644 (file)
index 0000000..4f9a8a4
--- /dev/null
@@ -0,0 +1,9 @@
+<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>
diff --git a/Content.Client/UserInterface/Controls/DialogWindow.xaml.cs b/Content.Client/UserInterface/Controls/DialogWindow.xaml.cs
new file mode 100644 (file)
index 0000000..f50aca5
--- /dev/null
@@ -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
+
+/// <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
+}
index 3479de5736c2d44d40c2732939083bdb81cf6b93..f4e77ab9990adb6224b499ded3033ab9fc1820de 100644 (file)
@@ -26,7 +26,7 @@ public sealed class QuickDialogOpenEvent : EntityEventArgs
     /// <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)
     {
@@ -87,11 +87,17 @@ public sealed class QuickDialogEntry
     /// </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;
     }
 }