From 0012fceb66844a1d480031ff0d1ef9ea37b1c604 Mon Sep 17 00:00:00 2001 From: suchmememanyskill <38142618+suchmememanyskill@users.noreply.github.com> Date: Wed, 12 Jun 2024 22:15:43 +0200 Subject: [PATCH] Add form validation --- Launcher/Forms/FormTemplates/TextBox.axaml.cs | 2 + LauncherGamePlugin/Forms/Form.cs | 25 ++++++- LauncherGamePlugin/Forms/FormEntry.cs | 4 ++ LauncherGamePlugin/Forms/FormValidator.cs | 71 +++++++++++++++++++ LauncherGamePlugin/Utils.cs | 4 ++ .../Gui/AddOrEditEmuProfileGui.cs | 63 +++++----------- 6 files changed, 123 insertions(+), 46 deletions(-) create mode 100644 LauncherGamePlugin/Forms/FormValidator.cs diff --git a/Launcher/Forms/FormTemplates/TextBox.axaml.cs b/Launcher/Forms/FormTemplates/TextBox.axaml.cs index 0330290..6680f91 100644 --- a/Launcher/Forms/FormTemplates/TextBox.axaml.cs +++ b/Launcher/Forms/FormTemplates/TextBox.axaml.cs @@ -15,6 +15,8 @@ public TextBox() public TextBox(FormEntry entry) : this() { + TextBlock.IsVisible = !string.IsNullOrEmpty(entry.Name); + TextBlock.Text = entry.Name; TextBlock.HorizontalAlignment = entry.Alignment.ToAvaloniaAlignment(); if (!string.IsNullOrWhiteSpace(entry.Value)) diff --git a/LauncherGamePlugin/Forms/Form.cs b/LauncherGamePlugin/Forms/Form.cs index 260117b..5e0b02e 100644 --- a/LauncherGamePlugin/Forms/Form.cs +++ b/LauncherGamePlugin/Forms/Form.cs @@ -9,6 +9,27 @@ public class Form public Func>? Background { get; set; } = null; public IGame? Game { get; set; } = null; + + public TextBoxElement? ValidationFailureField { get; set; } + + public bool Validate(IApp app) + { + try + { + FormEntries.ForEach(x => x.Validate()); + } + catch (Exception ex) + { + if (ValidationFailureField != null) + { + ValidationFailureField.Name = ex.Message; + } + app.ShowForm(this); + return false; + } + + return true; + } public Form(List entries) { @@ -48,9 +69,9 @@ public static Form CreateDismissibleTextPrompt(string text, IApp app) public static FormEntry TextInput(string label, string value = "") => new TextInputElement(label, value); - public static FormEntry TextBox(string text, FormAlignment alignment = FormAlignment.Default, + public static TextBoxElement TextBox(string text, FormAlignment alignment = FormAlignment.Default, string fontWeight = "") - => new TextBoxElement(text, fontWeight, alignment: alignment); + => new(text, fontWeight, alignment: alignment); public static FormEntry ClickableLinkBox(string text, Action
action, FormAlignment alignment = FormAlignment.Default, string fontWeight = "") diff --git a/LauncherGamePlugin/Forms/FormEntry.cs b/LauncherGamePlugin/Forms/FormEntry.cs index 5b6bf66..21eabaf 100644 --- a/LauncherGamePlugin/Forms/FormEntry.cs +++ b/LauncherGamePlugin/Forms/FormEntry.cs @@ -160,6 +160,7 @@ public abstract class FormEntry public string Value { get; set; } public bool Enabled { get; set; } public Form ContainingForm { get; set; } + public List Validators { get; } = new(); public FormAlignment Alignment; public event Action? OnChange; public void InvokeOnChange() => OnChange?.Invoke(this); @@ -171,4 +172,7 @@ public FormEntry(string name = "", string value = "", FormAlignment alignment = Enabled = enabled; Alignment = alignment; } + + public void Validate() + => Validators.ForEach(x => x.Validate(this)); } \ No newline at end of file diff --git a/LauncherGamePlugin/Forms/FormValidator.cs b/LauncherGamePlugin/Forms/FormValidator.cs new file mode 100644 index 0000000..65bc199 --- /dev/null +++ b/LauncherGamePlugin/Forms/FormValidator.cs @@ -0,0 +1,71 @@ +namespace LauncherGamePlugin.Forms; + +public interface IFormValidator +{ + // Throws on failed validation + public void Validate(FormEntry entry); +} + +public class NotEmptyValidation : IFormValidator +{ + public void Validate(FormEntry entry) + { + if (string.IsNullOrWhiteSpace(entry.Value)) + throw new Exception($"{Utils.OnlyLetters(entry.Name)} is empty"); + } +} + +public class ExistsValidation : IFormValidator +{ + public void Validate(FormEntry entry) + { + if (entry is FilePickerElement filePickerElement) + { + if (!File.Exists(entry.Value)) + throw new Exception($"{Utils.OnlyLetters(filePickerElement.Name)} file does not exist on disk"); + } + + if (entry is FolderPickerElement folderPickerElement) + { + if (!Directory.Exists(entry.Value)) + throw new Exception($"{Utils.OnlyLetters(folderPickerElement.Name)} folder does not exist on disk"); + } + } +} + +public class ContainsValidation : IFormValidator +{ + private string _contians; + + public ContainsValidation(string contains) + { + _contians = contains; + } + + public void Validate(FormEntry entry) + { + if (!entry.Value.Contains(_contians)) + throw new Exception($"{Utils.OnlyLetters(entry.Name)} does not contain {_contians}"); + } +} + +public static class FormEntryExtensions +{ + public static FormEntry NotEmpty(this FormEntry formEntry) + { + formEntry.Validators.Add(new NotEmptyValidation()); + return formEntry; + } + + public static FormEntry Exists(this FormEntry formEntry) + { + formEntry.Validators.Add(new ExistsValidation()); + return formEntry; + } + + public static FormEntry Contains(this FormEntry formEntry, string contains) + { + formEntry.Validators.Add(new ContainsValidation(contains)); + return formEntry; + } +} \ No newline at end of file diff --git a/LauncherGamePlugin/Utils.cs b/LauncherGamePlugin/Utils.cs index 7c3bebc..f4a2840 100644 --- a/LauncherGamePlugin/Utils.cs +++ b/LauncherGamePlugin/Utils.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Runtime.InteropServices; +using System.Text; using LauncherGamePlugin.Enums; namespace LauncherGamePlugin; @@ -220,4 +221,7 @@ public static string TimeSpanAsTimeEstimate(TimeSpan estimatedTime) return estimatedDisplay; } + + public static string OnlyLetters(string input) + => new(input.Where(char.IsLetter).ToArray()); } \ No newline at end of file diff --git a/RemoteDownloaderPlugin/Gui/AddOrEditEmuProfileGui.cs b/RemoteDownloaderPlugin/Gui/AddOrEditEmuProfileGui.cs index d8eee78..9f097f9 100644 --- a/RemoteDownloaderPlugin/Gui/AddOrEditEmuProfileGui.cs +++ b/RemoteDownloaderPlugin/Gui/AddOrEditEmuProfileGui.cs @@ -12,7 +12,6 @@ public class AddOrEditEmuProfileGui private string _args; private string _workDir; private readonly bool _addOrUpdate; - private string _error; public AddOrEditEmuProfileGui(IApp app, Plugin instance) { @@ -24,7 +23,6 @@ public AddOrEditEmuProfileGui(IApp app, Plugin instance) _args = ""; _workDir = ""; _addOrUpdate = false; - _error = ""; } public AddOrEditEmuProfileGui(IApp app, Plugin instance, EmuProfile profile) @@ -42,11 +40,10 @@ public void ShowGui() List formEntries = new() { Form.TextBox(_addOrUpdate ? "Add new emulation platform" : "Edit emulation platform", FormAlignment.Left, "Bold"), - Form.TextInput("Platform:", _platform), - Form.FilePicker("Executable Path:", _path), - Form.TextInput("CLI Args:", _args), - Form.FolderPicker("Working Directory:", _workDir), - + Form.TextInput("Platform:", _platform).NotEmpty(), + Form.FilePicker("Executable Path:", _path).NotEmpty().Exists(), + Form.TextInput("CLI Args:", _args).NotEmpty().Contains("{EXEC}"), + Form.FolderPicker("Working Directory:", _workDir).NotEmpty().Exists(), }; if (_addOrUpdate) @@ -75,51 +72,29 @@ public void ShowGui() { formEntries.Add(Form.Button("Cancel", _ => _app.HideForm(), "Save", Process)); } - - if (!string.IsNullOrWhiteSpace(_error)) - formEntries.Add(Form.TextBox(_error, fontWeight: "Bold")); - - _app.ShowForm(formEntries); + + var errorEntry = Form.TextBox("", FormAlignment.Center); + formEntries.Add(errorEntry); + var form = new Form(formEntries) + { + ValidationFailureField = errorEntry + }; + + _app.ShowForm(form); } public void Process(Form form) { + if (!form.Validate(_app)) + { + return; + } + _platform = form.GetValue("Platform:"); _path = form.GetValue("Executable Path:"); _args = form.GetValue("CLI Args:"); _workDir = form.GetValue("Working Directory:"); - - if (string.IsNullOrWhiteSpace(_platform) || - string.IsNullOrWhiteSpace(_path) || - string.IsNullOrWhiteSpace(_args) || - string.IsNullOrWhiteSpace(_workDir)) - { - _error = "Not all fields are filled"; - ShowGui(); - return; - } - - if (!File.Exists(_path)) - { - _error = "Executable path does not exist"; - ShowGui(); - return; - } - - if (!Directory.Exists(_workDir)) - { - _error = "Working directory does not exist"; - ShowGui(); - return; - } - - if (!_args.Contains("{EXEC}")) - { - _error = "Args does not specify an {EXEC} param"; - ShowGui(); - return; - } - + EmuProfile? existingProfile = _instance.Storage.Data.EmuProfiles.FirstOrDefault(x => x.Platform == _platform); if (existingProfile == null) {