make dialog window not evil (#24677)
* 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>
This commit is contained in:
@@ -1,13 +1,8 @@
|
|||||||
using System.Linq;
|
|
||||||
using Content.Client.UserInterface.Controls;
|
using Content.Client.UserInterface.Controls;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
|
||||||
|
|
||||||
namespace Content.Client.Administration;
|
namespace Content.Client.Administration;
|
||||||
|
|
||||||
// mfw they ported input() from BYOND
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This handles the client portion of quick dialogs.
|
/// This handles the client portion of quick dialogs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -21,149 +16,22 @@ public sealed class QuickDialogSystem : EntitySystem
|
|||||||
|
|
||||||
private void OpenDialog(QuickDialogOpenEvent ev)
|
private void OpenDialog(QuickDialogOpenEvent ev)
|
||||||
{
|
{
|
||||||
var window = new FancyWindow()
|
var ok = (ev.Buttons & QuickDialogButtonFlag.OkButton) != 0;
|
||||||
{
|
var cancel = (ev.Buttons & QuickDialogButtonFlag.CancelButton) != 0;
|
||||||
Title = ev.Title
|
var window = new DialogWindow(ev.Title, ev.Prompts, ok: ok, cancel: cancel);
|
||||||
};
|
|
||||||
|
|
||||||
var entryContainer = new BoxContainer()
|
window.OnConfirmed += responses =>
|
||||||
{
|
|
||||||
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 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,
|
RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
|
||||||
new(),
|
responses,
|
||||||
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()
|
|
||||||
{
|
|
||||||
RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
|
|
||||||
promptsDict.Select(x => (x.Key, x.Value.Text)).ToDictionary(x => x.Key, x => x.Text),
|
|
||||||
QuickDialogButtonFlag.OkButton));
|
QuickDialogButtonFlag.OkButton));
|
||||||
alreadyReplied = true;
|
};
|
||||||
window.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool VerifyInt(string input)
|
window.OnCancelled += () =>
|
||||||
{
|
{
|
||||||
return int.TryParse(input, out var _);
|
RaiseNetworkEvent(new QuickDialogResponseEvent(ev.DialogId,
|
||||||
}
|
new(),
|
||||||
|
QuickDialogButtonFlag.CancelButton));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
Content.Client/UserInterface/Controls/DialogWindow.xaml
Normal file
9
Content.Client/UserInterface/Controls/DialogWindow.xaml
Normal file
@@ -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>
|
||||||
147
Content.Client/UserInterface/Controls/DialogWindow.xaml.cs
Normal file
147
Content.Client/UserInterface/Controls/DialogWindow.xaml.cs
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ public sealed class QuickDialogOpenEvent : EntityEventArgs
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The buttons presented for the user.
|
/// The buttons presented for the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public QuickDialogButtonFlag Buttons = QuickDialogButtonFlag.OkButton;
|
public QuickDialogButtonFlag Buttons = QuickDialogButtonFlag.OkButton | QuickDialogButtonFlag.CancelButton;
|
||||||
|
|
||||||
public QuickDialogOpenEvent(string title, List<QuickDialogEntry> prompts, int dialogId, QuickDialogButtonFlag buttons)
|
public QuickDialogOpenEvent(string title, List<QuickDialogEntry> prompts, int dialogId, QuickDialogButtonFlag buttons)
|
||||||
{
|
{
|
||||||
@@ -87,11 +87,17 @@ public sealed class QuickDialogEntry
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string Prompt;
|
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;
|
FieldId = fieldId;
|
||||||
Type = type;
|
Type = type;
|
||||||
Prompt = prompt;
|
Prompt = prompt;
|
||||||
|
Placeholder = placeholder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user