/ src / Ryujinx.Gtk3 / UI / Applet / GtkDynamicTextInputHandler.cs
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  }