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 }