using Content.Shared.Administration; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Sandboxing; using Robust.Client.UserInterface; using System.Linq; 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, Func)> _promptLines; private delegate (Control, Func) ControlConstructor(Func verifier, string name, QuickDialogEntry entry, bool last, object? value);// are you feeling it now mr krabs? /// /// 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 value = entry.Value; var box = new BoxContainer(); box.AddChild(new Label() { Text = entry.Prompt, HorizontalExpand = true, SizeFlagsStretchRatio = 0.5f }); //var edit = new LineEdit() { HorizontalExpand = true }; // walkthrough: // second item in this tuple is a verifier function for controls who have that option // third item is a backup name in case we have not been provided with one from the server // first item is a function that takes the other two, the QuickDialogEntry for it, a bool of whether it's the last control in the window, the default value for the control // and returns another tuple: // item 1 is the control itself // item 2 is a Func, a """generic""" function that returns whatever user has done with the control (ControlConstructor, Func?, string) notapairanymore = entry.Type switch { QuickDialogEntryType.Integer => (SetupLineEditNumber, VerifyInt, "integer"), QuickDialogEntryType.Float => (SetupLineEditNumber, VerifyFloat, "float"), QuickDialogEntryType.ShortText => (SetupLineEdit, VerifyShortText, "short-text"), QuickDialogEntryType.LongText => (SetupLineEdit, VerifyLongText, "long-text"), QuickDialogEntryType.Hex16 => (SetupLineEditHex, VerifyHex16, "hex16"), QuickDialogEntryType.Boolean => (SetupCheckBox, null, "boolean"), QuickDialogEntryType.Void => (SetupVoid, null, "void"), _ => throw new ArgumentOutOfRangeException() }; var (setup, valid, name) = notapairanymore; // try use placeholder from the caller, fall back to the generic one for whatever type is being validated. var (control, returner) = setup(valid!, entry.Placeholder ?? Loc.GetString($"quick-dialog-ui-{name}"), entry, i == entries.Count - 1, value); // ARE YOU FEELING IT NOW MR KRABS? // yes, valid can be null // yes, i am just going to ignore that // go fuck yourself _promptLines.Add((entry.FieldId, returner)); box.AddChild(control); 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, returner) in _promptLines) { results[field] = returner(); } _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; } private bool VerifyHex16(string input) { return input.Length <= 4 && int.TryParse(input, System.Globalization.NumberStyles.HexNumber, null, out int _); } private (Control, Func) SetupLineEdit(Func valid, string name, QuickDialogEntry entry, bool last, object? value) // oh shit i'm feeling it { var edit = new LineEdit() { HorizontalExpand = true }; edit.IsValid += valid; if(value != null) edit.Text = value.ToString() ?? ""; edit.PlaceHolder = name; // Last text box gets enter confirmation. // Only the last so you don't accidentally confirm early. if (last) edit.OnTextEntered += _ => Confirm(); return (edit, () => {return edit.Text;} ); } private (Control, Func) SetupLineEditNumber(Func valid, string name, QuickDialogEntry entry, bool last, object? value) { var (control, returner) = SetupLineEdit(valid, name, entry, last, value); var le = (LineEdit) control; return (control, ()=> le.Text.Length > 0 ? le.Text : "0"); // Otherwise you'll get kicked for malformed data } private (Control, Func) SetupLineEditHex(Func valid, string name, QuickDialogEntry entry, bool last, object? value) { var (control, returner) = SetupLineEditNumber(valid, name, entry, last, value); var le = (LineEdit) control; if(value is int) { le.Text = ((int)value).ToString("X"); } le.OnTextChanged += e => { e.Control.Text = e.Text.ToUpper(); }; return (control, returner); } private (Control, Func) SetupCheckBox(Func _, string name, QuickDialogEntry entry, bool last, object? value) { var check = new CheckBox() { HorizontalExpand = true, HorizontalAlignment = HAlignment.Right }; check.Text = name; value ??= false; check.Pressed = (bool)value; return (check, () => { return check.Pressed ? "true" : "false"; }); } private (Control, Func) SetupVoid(Func _, string __, QuickDialogEntry ___, bool ____, object? _____) { var control = new Control(); control.Visible = false; return (control, () => "" ); } #endregion }