/ src / Ryujinx / UI / Applet / SwkbdAppletDialog.axaml.cs
SwkbdAppletDialog.axaml.cs
  1  using Avalonia.Controls;
  2  using Avalonia.Input;
  3  using Avalonia.Interactivity;
  4  using Avalonia.Media;
  5  using FluentAvalonia.UI.Controls;
  6  using Ryujinx.Ava.Common.Locale;
  7  using Ryujinx.Ava.UI.Helpers;
  8  using Ryujinx.HLE.HOS.Applets;
  9  using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard;
 10  using System;
 11  using System.Linq;
 12  using System.Threading.Tasks;
 13  
 14  namespace Ryujinx.Ava.UI.Controls
 15  {
 16      internal partial class SwkbdAppletDialog : UserControl
 17      {
 18          private Predicate<int> _checkLength = _ => true;
 19          private Predicate<string> _checkInput = _ => true;
 20          private int _inputMax;
 21          private int _inputMin;
 22          private readonly string _placeholder;
 23  
 24          private ContentDialog _host;
 25  
 26          public SwkbdAppletDialog(string mainText, string secondaryText, string placeholder, string message)
 27          {
 28              MainText = mainText;
 29              SecondaryText = secondaryText;
 30              Message = message ?? "";
 31              DataContext = this;
 32              _placeholder = placeholder;
 33              InitializeComponent();
 34  
 35              Input.Watermark = _placeholder;
 36  
 37              Input.AddHandler(TextInputEvent, Message_TextInput, RoutingStrategies.Tunnel, true);
 38          }
 39  
 40          public SwkbdAppletDialog()
 41          {
 42              DataContext = this;
 43              InitializeComponent();
 44          }
 45  
 46          protected override void OnGotFocus(GotFocusEventArgs e)
 47          {
 48              // FIXME: This does not work. Might be a bug in Avalonia with DialogHost
 49              //        Currently focus will be redirected to the overlay window instead.
 50              Input.Focus();
 51          }
 52  
 53          public string Message { get; set; } = "";
 54          public string MainText { get; set; } = "";
 55          public string SecondaryText { get; set; } = "";
 56  
 57          public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, SoftwareKeyboardUIArgs args)
 58          {
 59              ContentDialog contentDialog = new();
 60  
 61              UserResult result = UserResult.Cancel;
 62  
 63              SwkbdAppletDialog content = new(args.HeaderText, args.SubtitleText, args.GuideText, args.InitialText);
 64  
 65              string input = string.Empty;
 66  
 67              content.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax);
 68              content.SetInputValidation(args.KeyboardMode);
 69  
 70              content._host = contentDialog;
 71              contentDialog.Title = title;
 72              contentDialog.PrimaryButtonText = args.SubmitText;
 73              contentDialog.IsPrimaryButtonEnabled = content._checkLength(content.Message.Length);
 74              contentDialog.SecondaryButtonText = "";
 75              contentDialog.CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel];
 76              contentDialog.Content = content;
 77  
 78              void Handler(ContentDialog sender, ContentDialogClosedEventArgs eventArgs)
 79              {
 80                  if (eventArgs.Result == ContentDialogResult.Primary)
 81                  {
 82                      result = UserResult.Ok;
 83                      input = content.Input.Text;
 84                  }
 85              }
 86  
 87              contentDialog.Closed += Handler;
 88  
 89              await ContentDialogHelper.ShowAsync(contentDialog);
 90  
 91              return (result, input);
 92          }
 93  
 94          private void ApplyValidationInfo(string text)
 95          {
 96              Error.IsVisible = !string.IsNullOrEmpty(text);
 97              Error.Text = text;
 98          }
 99  
100          public void SetInputLengthValidation(int min, int max)
101          {
102              _inputMin = Math.Min(min, max);
103              _inputMax = Math.Max(min, max);
104  
105              Error.IsVisible = false;
106              Error.FontStyle = FontStyle.Italic;
107  
108              string validationInfoText = "";
109  
110              if (_inputMin <= 0 && _inputMax == int.MaxValue) // Disable.
111              {
112                  Error.IsVisible = false;
113  
114                  _checkLength = length => true;
115              }
116              else if (_inputMin > 0 && _inputMax == int.MaxValue)
117              {
118                  validationInfoText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinCharacters, _inputMin);
119  
120                  _checkLength = length => _inputMin <= length;
121              }
122              else
123              {
124                  validationInfoText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinRangeCharacters, _inputMin, _inputMax);
125  
126                  _checkLength = length => _inputMin <= length && length <= _inputMax;
127              }
128  
129              ApplyValidationInfo(validationInfoText);
130              Message_TextInput(this, new TextInputEventArgs());
131          }
132  
133          private void SetInputValidation(KeyboardMode mode)
134          {
135              string validationInfoText = Error.Text;
136              string localeText;
137              switch (mode)
138              {
139                  case KeyboardMode.Numeric:
140                      localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeNumeric);
141                      validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText);
142                      _checkInput = text => text.All(NumericCharacterValidation.IsNumeric);
143                      break;
144                  case KeyboardMode.Alphabet:
145                      localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeAlphabet);
146                      validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText);
147                      _checkInput = text => text.All(value => !CJKCharacterValidation.IsCJK(value));
148                      break;
149                  case KeyboardMode.ASCII:
150                      localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeASCII);
151                      validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText);
152                      _checkInput = text => text.All(char.IsAscii);
153                      break;
154                  default:
155                      _checkInput = _ => true;
156                      break;
157              }
158  
159              ApplyValidationInfo(validationInfoText);
160              Message_TextInput(this, new TextInputEventArgs());
161          }
162  
163          private void Message_TextInput(object sender, TextInputEventArgs e)
164          {
165              if (_host != null)
166              {
167                  _host.IsPrimaryButtonEnabled = _checkLength(Message.Length) && _checkInput(Message);
168              }
169          }
170  
171          private void Message_KeyUp(object sender, KeyEventArgs e)
172          {
173              if (e.Key == Key.Enter && _host.IsPrimaryButtonEnabled)
174              {
175                  _host.Hide(ContentDialogResult.Primary);
176              }
177              else
178              {
179                  _host.IsPrimaryButtonEnabled = _checkLength(Message.Length) && _checkInput(Message);
180              }
181          }
182      }
183  }