From d511c845b70a8771de7d64369e24ab3f1ed1c325 Mon Sep 17 00:00:00 2001 From: WilliamWsyHK Date: Sun, 4 Jun 2023 11:30:24 +0800 Subject: [PATCH] Check KeyboardMode in GUI (#4343) * Update SoftwareKeyboard to send KeyboardMode to UI * Update GTK UI to check text against KeyboardMode * Update Ava UI to check text against KeyboardMode * Restructure input validation * true when text is not empty * Add English validation text for SoftwareKeyboardMode * Add Chinese validation text for SoftwareKeyboardMode * Update base on feedback --------- Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com> --- src/Ryujinx.Ava/Assets/Locales/en_US.json | 3 + src/Ryujinx.Ava/Assets/Locales/zh_CN.json | 3 + src/Ryujinx.Ava/Assets/Locales/zh_TW.json | 3 + .../UI/Applet/SwkbdAppletDialog.axaml.cs | 59 +++++++++++++++---- .../Applets/SoftwareKeyboard/KeyboardMode.cs | 2 +- .../SoftwareKeyboardApplet.cs | 1 + .../SoftwareKeyboardUiArgs.cs | 3 + src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs | 1 + src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs | 56 ++++++++++++++---- 9 files changed, 109 insertions(+), 22 deletions(-) diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index 8f4965e142..79765db16e 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -544,6 +544,9 @@ "SwkbdMinCharacters": "Must be at least {0} characters long", "SwkbdMinRangeCharacters": "Must be {0}-{1} characters long", "SoftwareKeyboard": "Software Keyboard", + "SoftwareKeyboardModeNumbersOnly": "Must be numbers only", + "SoftwareKeyboardModeAlphabet": "Must be alphabets only", + "SoftwareKeyboardModeASCII": "Must be ASCII text only", "DialogControllerAppletMessagePlayerRange": "Application requests {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.", "DialogControllerAppletMessage": "Application requests exactly {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.", "DialogControllerAppletDockModeSet": "Docked mode set. Handheld is also invalid.\n\n", diff --git a/src/Ryujinx.Ava/Assets/Locales/zh_CN.json b/src/Ryujinx.Ava/Assets/Locales/zh_CN.json index 25dc3cbaf2..b47dda975e 100644 --- a/src/Ryujinx.Ava/Assets/Locales/zh_CN.json +++ b/src/Ryujinx.Ava/Assets/Locales/zh_CN.json @@ -527,6 +527,9 @@ "SwkbdMinCharacters": "至少应为 {0} 个字长", "SwkbdMinRangeCharacters": "必须为 {0}-{1} 个字长", "SoftwareKeyboard": "软件键盘", + "SoftwareKeyboardModeNumbersOnly": "只接受数字", + "SoftwareKeyboardModeAlphabet": "只接受英文字母", + "SoftwareKeyboardModeASCII": "只接受 ASCII 符号", "DialogControllerAppletMessagePlayerRange": "游戏需要 {0} 个玩家并满足以下要求:\n\n手柄类型:{1}\n\n玩家类型:{2}\n\n{3}请打开设置窗口,重新配置手柄输入;或者关闭返回。", "DialogControllerAppletMessage": "游戏需要刚好 {0} 个玩家并满足以下要求:\n\n手柄类型:{1}\n\n玩家类型:{2}\n\n{3}请打开设置窗口,重新配置手柄输入;或者关闭返回。", "DialogControllerAppletDockModeSet": "目前处于主机模式,无法使用掌机操作方式", diff --git a/src/Ryujinx.Ava/Assets/Locales/zh_TW.json b/src/Ryujinx.Ava/Assets/Locales/zh_TW.json index 940282a0ff..e943691ce3 100644 --- a/src/Ryujinx.Ava/Assets/Locales/zh_TW.json +++ b/src/Ryujinx.Ava/Assets/Locales/zh_TW.json @@ -527,6 +527,9 @@ "SwkbdMinCharacters": "至少應為 {0} 個字長", "SwkbdMinRangeCharacters": "必須為 {0}-{1} 個字長", "SoftwareKeyboard": "軟體鍵盤", + "SoftwareKeyboardModeNumbersOnly": "只接受數字", + "SoftwareKeyboardModeAlphabet": "只接受英文字母", + "SoftwareKeyboardModeASCII": "只接受 ASCII 符號", "DialogControllerAppletMessagePlayerRange": "本遊戲需要 {0} 個玩家持有:\n\n類型:{1}\n\n玩家:{2}\n\n{3}請打開設定畫面,配置手把,或者關閉本視窗。", "DialogControllerAppletMessage": "本遊戲需要剛好 {0} 個玩家持有:\n\n類型:{1}\n\n玩家:{2}\n\n{3}請打開設定畫面,配置手把,或者關閉本視窗。", "DialogControllerAppletDockModeSet": "現在處於主機模式,無法使用掌機操作方式\n\n", diff --git a/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs b/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs index cb69e96b7d..04bc461930 100644 --- a/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs +++ b/src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml.cs @@ -9,14 +9,17 @@ using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; using Ryujinx.HLE.HOS.Applets; +using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; using System; +using System.Linq; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Controls { internal partial class SwkbdAppletDialog : UserControl { - private Predicate _checkLength; + private Predicate _checkLength = _ => true; + private Predicate _checkInput = _ => true; private int _inputMax; private int _inputMin; private string _placeholder; @@ -35,8 +38,6 @@ namespace Ryujinx.Ava.UI.Controls Input.Watermark = _placeholder; Input.AddHandler(TextInputEvent, Message_TextInput, RoutingStrategies.Tunnel, true); - - SetInputLengthValidation(0, int.MaxValue); // Disable by default. } public SwkbdAppletDialog() @@ -67,6 +68,7 @@ namespace Ryujinx.Ava.UI.Controls string input = string.Empty; content.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax); + content.SetInputValidation(args.KeyboardMode); content._host = contentDialog; contentDialog.Title = title; @@ -91,6 +93,12 @@ namespace Ryujinx.Ava.UI.Controls return (result, input); } + private void ApplyValidationInfo(string text) + { + Error.IsVisible = !string.IsNullOrEmpty(text); + Error.Text = text; + } + public void SetInputLengthValidation(int min, int max) { _inputMin = Math.Min(min, max); @@ -99,6 +107,8 @@ namespace Ryujinx.Ava.UI.Controls Error.IsVisible = false; Error.FontStyle = FontStyle.Italic; + string validationInfoText = ""; + if (_inputMin <= 0 && _inputMax == int.MaxValue) // Disable. { Error.IsVisible = false; @@ -107,21 +117,48 @@ namespace Ryujinx.Ava.UI.Controls } else if (_inputMin > 0 && _inputMax == int.MaxValue) { - Error.IsVisible = true; - - Error.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinCharacters, _inputMin); + validationInfoText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinCharacters, _inputMin); _checkLength = length => _inputMin <= length; } else { - Error.IsVisible = true; - - Error.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinRangeCharacters, _inputMin, _inputMax); + validationInfoText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinRangeCharacters, _inputMin, _inputMax); _checkLength = length => _inputMin <= length && length <= _inputMax; } + ApplyValidationInfo(validationInfoText); + Message_TextInput(this, new TextInputEventArgs()); + } + + private void SetInputValidation(KeyboardMode mode) + { + string validationInfoText = Error.Text; + string localeText; + switch (mode) + { + case KeyboardMode.NumbersOnly: + localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeNumbersOnly); + validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText); + _checkInput = text => text.All(char.IsDigit); + break; + case KeyboardMode.Alphabet: + localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeAlphabet); + validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText); + _checkInput = text => text.All(char.IsAsciiLetter); + break; + case KeyboardMode.ASCII: + localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeASCII); + validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText); + _checkInput = text => text.All(char.IsAscii); + break; + default: + _checkInput = _ => true; + break; + } + + ApplyValidationInfo(validationInfoText); Message_TextInput(this, new TextInputEventArgs()); } @@ -129,7 +166,7 @@ namespace Ryujinx.Ava.UI.Controls { if (_host != null) { - _host.IsPrimaryButtonEnabled = _checkLength(Message.Length); + _host.IsPrimaryButtonEnabled = _checkLength(Message.Length) && _checkInput(Message); } } @@ -141,7 +178,7 @@ namespace Ryujinx.Ava.UI.Controls } else { - _host.IsPrimaryButtonEnabled = _checkLength(Message.Length); + _host.IsPrimaryButtonEnabled = _checkLength(Message.Length) && _checkInput(Message); } } } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs index f512050e1a..01b3c9634d 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs @@ -3,7 +3,7 @@ /// /// Identifies the variant of keyboard displayed on screen. /// - enum KeyboardMode : uint + public enum KeyboardMode : uint { /// /// A full alpha-numeric keyboard. diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs index 278ea56c22..4b484e802a 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs @@ -209,6 +209,7 @@ namespace Ryujinx.HLE.HOS.Applets // Call the configured GUI handler to get user's input. var args = new SoftwareKeyboardUiArgs { + KeyboardMode = _keyboardForegroundConfig.Mode, HeaderText = StripUnicodeControlCodes(_keyboardForegroundConfig.HeaderText), SubtitleText = StripUnicodeControlCodes(_keyboardForegroundConfig.SubtitleText), GuideText = StripUnicodeControlCodes(_keyboardForegroundConfig.GuideText), diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs index d24adec333..d67a440947 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUiArgs.cs @@ -1,7 +1,10 @@ +using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; + namespace Ryujinx.HLE.HOS.Applets { public struct SoftwareKeyboardUiArgs { + public KeyboardMode KeyboardMode; public string HeaderText; public string SubtitleText; public string InitialText; diff --git a/src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs b/src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs index d81cbe3c5e..b7577b85d1 100644 --- a/src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs +++ b/src/Ryujinx/Ui/Applet/GtkHostUiHandler.cs @@ -106,6 +106,7 @@ namespace Ryujinx.Ui.Applet swkbdDialog.OkButton.Label = args.SubmitText; swkbdDialog.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax); + swkbdDialog.SetInputValidation(args.KeyboardMode); if (swkbdDialog.Run() == (int)ResponseType.Ok) { diff --git a/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs b/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs index 7c14f0e8d2..28067b7514 100644 --- a/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs +++ b/src/Ryujinx/Ui/Applet/SwkbdAppletDialog.cs @@ -1,5 +1,7 @@ using Gtk; +using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; using System; +using System.Linq; namespace Ryujinx.Ui.Applet { @@ -7,8 +9,12 @@ namespace Ryujinx.Ui.Applet { private int _inputMin; private int _inputMax; + private KeyboardMode _mode; - private Predicate _checkLength; + private string _validationInfoText = ""; + + private Predicate _checkLength = _ => true; + private Predicate _checkInput = _ => true; private readonly Label _validationInfo; @@ -38,8 +44,12 @@ namespace Ryujinx.Ui.Applet ((Box)MessageArea).PackEnd(_validationInfo, true, true, 0); ((Box)MessageArea).PackEnd(InputEntry, true, true, 4); + } - SetInputLengthValidation(0, int.MaxValue); // Disable by default. + private void ApplyValidationInfo() + { + _validationInfo.Visible = !string.IsNullOrEmpty(_validationInfoText); + _validationInfo.Markup = _validationInfoText; } public void SetInputLengthValidation(int min, int max) @@ -53,23 +63,49 @@ namespace Ryujinx.Ui.Applet { _validationInfo.Visible = false; - _checkLength = (length) => true; + _checkLength = _ => true; } else if (_inputMin > 0 && _inputMax == int.MaxValue) { - _validationInfo.Visible = true; - _validationInfo.Markup = $"Must be at least {_inputMin} characters long"; + _validationInfoText = $"Must be at least {_inputMin} characters long. "; - _checkLength = (length) => _inputMin <= length; + _checkLength = length => _inputMin <= length; } else { - _validationInfo.Visible = true; - _validationInfo.Markup = $"Must be {_inputMin}-{_inputMax} characters long"; + _validationInfoText = $"Must be {_inputMin}-{_inputMax} characters long. "; - _checkLength = (length) => _inputMin <= length && length <= _inputMax; + _checkLength = length => _inputMin <= length && length <= _inputMax; } + ApplyValidationInfo(); + OnInputChanged(this, EventArgs.Empty); + } + + public void SetInputValidation(KeyboardMode mode) + { + _mode = mode; + + switch (mode) + { + case KeyboardMode.NumbersOnly: + _validationInfoText += "Must be numbers only."; + _checkInput = text => text.All(char.IsDigit); + break; + case KeyboardMode.Alphabet: + _validationInfoText += "Must be alphabets only."; + _checkInput = text => text.All(char.IsAsciiLetter); + break; + case KeyboardMode.ASCII: + _validationInfoText += "Must be ASCII text only."; + _checkInput = text => text.All(char.IsAscii); + break; + default: + _checkInput = _ => true; + break; + } + + ApplyValidationInfo(); OnInputChanged(this, EventArgs.Empty); } @@ -83,7 +119,7 @@ namespace Ryujinx.Ui.Applet private void OnInputChanged(object sender, EventArgs e) { - OkButton.Sensitive = _checkLength(InputEntry.Text.Length); + OkButton.Sensitive = _checkLength(InputEntry.Text.Length) && _checkInput(InputEntry.Text); } } } \ No newline at end of file