GtkDynamicTextInputHandler.cs
1 using Gtk; 2 using Ryujinx.HLE.UI; 3 using Ryujinx.Input.GTK3; 4 using Ryujinx.UI.Widgets; 5 using System.Threading; 6 7 namespace Ryujinx.UI.Applet 8 { 9 /// <summary> 10 /// Class that forwards key events to a GTK Entry so they can be processed into text. 11 /// </summary> 12 internal class GtkDynamicTextInputHandler : IDynamicTextInputHandler 13 { 14 private readonly Window _parent; 15 private readonly OffscreenWindow _inputToTextWindow = new(); 16 private readonly RawInputToTextEntry _inputToTextEntry = new(); 17 18 private bool _canProcessInput; 19 20 public event DynamicTextChangedHandler TextChangedEvent; 21 public event KeyPressedHandler KeyPressedEvent; 22 public event KeyReleasedHandler KeyReleasedEvent; 23 24 public bool TextProcessingEnabled 25 { 26 get 27 { 28 return Volatile.Read(ref _canProcessInput); 29 } 30 31 set 32 { 33 Volatile.Write(ref _canProcessInput, value); 34 } 35 } 36 37 public GtkDynamicTextInputHandler(Window parent) 38 { 39 _parent = parent; 40 _parent.KeyPressEvent += HandleKeyPressEvent; 41 _parent.KeyReleaseEvent += HandleKeyReleaseEvent; 42 43 _inputToTextWindow.Add(_inputToTextEntry); 44 45 _inputToTextEntry.TruncateMultiline = true; 46 47 // Start with input processing turned off so the text box won't accumulate text 48 // if the user is playing on the keyboard. 49 _canProcessInput = false; 50 } 51 52 [GLib.ConnectBefore()] 53 private void HandleKeyPressEvent(object o, KeyPressEventArgs args) 54 { 55 var key = (Ryujinx.Common.Configuration.Hid.Key)GTK3MappingHelper.ToInputKey(args.Event.Key); 56 57 if (!(KeyPressedEvent?.Invoke(key)).GetValueOrDefault(true)) 58 { 59 return; 60 } 61 62 if (_canProcessInput) 63 { 64 _inputToTextEntry.SendKeyPressEvent(o, args); 65 _inputToTextEntry.GetSelectionBounds(out int selectionStart, out int selectionEnd); 66 TextChangedEvent?.Invoke(_inputToTextEntry.Text, selectionStart, selectionEnd, _inputToTextEntry.OverwriteMode); 67 } 68 } 69 70 [GLib.ConnectBefore()] 71 private void HandleKeyReleaseEvent(object o, KeyReleaseEventArgs args) 72 { 73 var key = (Ryujinx.Common.Configuration.Hid.Key)GTK3MappingHelper.ToInputKey(args.Event.Key); 74 75 if (!(KeyReleasedEvent?.Invoke(key)).GetValueOrDefault(true)) 76 { 77 return; 78 } 79 80 if (_canProcessInput) 81 { 82 // TODO (caian): This solution may have problems if the pause is sent after a key press 83 // and before a key release. But for now GTK Entry does not seem to use release events. 84 _inputToTextEntry.SendKeyReleaseEvent(o, args); 85 _inputToTextEntry.GetSelectionBounds(out int selectionStart, out int selectionEnd); 86 TextChangedEvent?.Invoke(_inputToTextEntry.Text, selectionStart, selectionEnd, _inputToTextEntry.OverwriteMode); 87 } 88 } 89 90 public void SetText(string text, int cursorBegin) 91 { 92 _inputToTextEntry.Text = text; 93 _inputToTextEntry.Position = cursorBegin; 94 } 95 96 public void SetText(string text, int cursorBegin, int cursorEnd) 97 { 98 _inputToTextEntry.Text = text; 99 _inputToTextEntry.SelectRegion(cursorBegin, cursorEnd); 100 } 101 102 public void Dispose() 103 { 104 _parent.KeyPressEvent -= HandleKeyPressEvent; 105 _parent.KeyReleaseEvent -= HandleKeyReleaseEvent; 106 } 107 } 108 }