Browse Source

Now using TextInput event, WM_CHAR and GtkIMContext

pull/105/head
Nikita Tsukanov 11 years ago
parent
commit
70dcfd652a
  1. 45
      src/Gtk/Perspex.Gtk/WindowImpl.cs
  2. 4
      src/Perspex.Controls/MenuItemAccessKeyHandler.cs
  3. 29
      src/Perspex.Controls/TextBox.cs
  4. 2
      src/Perspex.Input/AccessKeyHandler.cs
  5. 5
      src/Perspex.Input/IInputElement.cs
  6. 26
      src/Perspex.Input/InputElement.cs
  7. 2
      src/Perspex.Input/KeyEventArgs.cs
  8. 53
      src/Perspex.Input/KeyboardDevice.cs
  9. 2
      src/Perspex.Input/Perspex.Input.csproj
  10. 8
      src/Perspex.Input/Raw/RawKeyEventArgs.cs
  11. 13
      src/Perspex.Input/Raw/RawTextInputEventArgs.cs
  12. 16
      src/Perspex.Input/TextInputEventArgs.cs
  13. 11
      src/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs
  14. 15
      src/Windows/Perspex.Win32/WindowImpl.cs
  15. 3
      tests/Perspex.Controls.UnitTests/TopLevelTests.cs

45
src/Gtk/Perspex.Gtk/WindowImpl.cs

@ -4,6 +4,9 @@
// </copyright>
// -----------------------------------------------------------------------
using Gdk;
using Perspex.Input;
namespace Perspex.Gtk
{
using System;
@ -22,25 +25,33 @@ namespace Perspex.Gtk
private Size clientSize;
private Gtk.IMContext imContext;
private uint lastKeyEventTimestamp;
public WindowImpl()
: base(Gtk.WindowType.Toplevel)
{
this.DefaultSize = new Gdk.Size(640, 480);
this.Events = Gdk.EventMask.PointerMotionMask |
Gdk.EventMask.ButtonPressMask |
Gdk.EventMask.ButtonReleaseMask;
this.windowHandle = new PlatformHandle(this.Handle, "GtkWindow");
Init();
}
public WindowImpl(Gtk.WindowType type)
: base(type)
{
Init();
}
private void Init()
{
this.Events = Gdk.EventMask.PointerMotionMask |
Gdk.EventMask.ButtonPressMask |
Gdk.EventMask.ButtonReleaseMask;
Gdk.EventMask.ButtonPressMask |
Gdk.EventMask.ButtonReleaseMask;
this.windowHandle = new PlatformHandle(this.Handle, "GtkWindow");
this.imContext = new Gtk.IMMulticontext();
this.imContext.Commit += ImContext_Commit;
}
public Size ClientSize
{
get;
@ -148,19 +159,29 @@ namespace Perspex.Gtk
this.Closed();
}
protected override bool OnKeyPressEvent(Gdk.EventKey evnt)
private bool ProcessKeyEvent(Gdk.EventKey evnt)
{
var keyChar = (char)Gdk.Keyval.ToUnicode ((uint)evnt.Key);
var keyText = keyChar == 0 ? string.Empty : new string (keyChar, 1);
this.lastKeyEventTimestamp = evnt.Time;
if (this.imContext.FilterKeypress(evnt))
return true;
var e = new RawKeyEventArgs(
GtkKeyboardDevice.Instance,
evnt.Time,
RawKeyEventType.KeyDown,
GtkKeyboardDevice.ConvertKey(evnt.Key), keyText);
evnt.Type == EventType.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
GtkKeyboardDevice.ConvertKey(evnt.Key));
this.Input(e);
return true;
}
protected override bool OnKeyPressEvent(Gdk.EventKey evnt) => ProcessKeyEvent(evnt);
protected override bool OnKeyReleaseEvent(EventKey evnt) => ProcessKeyEvent(evnt);
private void ImContext_Commit(object o, Gtk.CommitArgs args)
{
this.Input(new RawTextInputEventArgs(GtkKeyboardDevice.Instance, this.lastKeyEventTimestamp, args.Str));
}
protected override bool OnExposeEvent(Gdk.EventExpose evnt)
{
this.Paint(evnt.Area.ToPerspex(), this.GetHandle(evnt.Window));

4
src/Perspex.Controls/MenuItemAccessKeyHandler.cs

@ -53,7 +53,7 @@ namespace Perspex.Controls
this.owner = owner;
this.owner.AddHandler(InputElement.KeyDownEvent, this.OnKeyDown);
this.owner.AddHandler(InputElement.TextInputEvent, this.OnTextInput);
}
/// <summary>
@ -90,7 +90,7 @@ namespace Perspex.Controls
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event args.</param>
protected virtual void OnKeyDown(object sender, KeyEventArgs e)
protected virtual void OnTextInput(object sender, TextInputEventArgs e)
{
if (!string.IsNullOrWhiteSpace(e.Text))
{

29
src/Perspex.Controls/TextBox.cs

@ -128,12 +128,27 @@ namespace Perspex.Controls
this.presenter.HideCaret();
}
protected override void OnTextInput(TextInputEventArgs e)
{
string text = this.Text ?? string.Empty;
int caretIndex = this.CaretIndex;
if (!string.IsNullOrEmpty(e.Text))
{
this.DeleteSelection();
caretIndex = this.CaretIndex;
text = this.Text ?? string.Empty;
this.Text = text.Substring(0, caretIndex) + e.Text + text.Substring(caretIndex);
++this.CaretIndex;
this.SelectionStart = this.SelectionEnd = this.CaretIndex;
}
base.OnTextInput(e);
}
protected override void OnKeyDown(KeyEventArgs e)
{
string text = this.Text ?? string.Empty;
int caretIndex = this.CaretIndex;
bool movement = false;
bool textEntered = false;
bool handled = true;
var modifiers = e.Device.Modifiers;
@ -220,16 +235,6 @@ namespace Perspex.Controls
break;
default:
if (!string.IsNullOrEmpty(e.Text) && !modifiers.HasFlag(ModifierKeys.Control) && !modifiers.HasFlag(ModifierKeys.Alt))
{
this.DeleteSelection();
caretIndex = this.CaretIndex;
text = this.Text ?? string.Empty;
this.Text = text.Substring(0, caretIndex) + e.Text + text.Substring(caretIndex);
++this.CaretIndex;
textEntered = true;
}
break;
}
@ -237,7 +242,7 @@ namespace Perspex.Controls
{
this.SelectionEnd = this.CaretIndex;
}
else if (movement || textEntered)
else if (movement)
{
this.SelectionStart = this.SelectionEnd = this.CaretIndex;
}

2
src/Perspex.Input/AccessKeyHandler.cs

@ -150,7 +150,7 @@ namespace Perspex.Input
{
// If any other key is pressed with the Alt key held down, or the main menu is open,
// find all controls who have registered that access key.
var text = e.Text.ToUpper();
var text = e.Key.ToString().ToUpper();
var matches = this.registered
.Where(x => x.Item1 == text && x.Item2.IsEffectivelyVisible)
.Select(x => x.Item2);

5
src/Perspex.Input/IInputElement.cs

@ -34,6 +34,11 @@ namespace Perspex.Input
/// </summary>
event EventHandler<KeyEventArgs> KeyUp;
/// <summary>
/// Occurs when a user typed some text while the control has focus.
/// </summary>
event EventHandler<TextInputEventArgs> TextInput;
/// <summary>
/// Occurs when the pointer enters the control.
/// </summary>

26
src/Perspex.Input/InputElement.cs

@ -81,6 +81,14 @@ namespace Perspex.Input
"KeyUp",
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="TextInput"/> event.
/// </summary>
public static readonly RoutedEvent<TextInputEventArgs> TextInputEvent =
RoutedEvent.Register<InputElement, TextInputEventArgs>(
"TextInput",
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="PointerEnter"/> event.
/// </summary>
@ -136,6 +144,7 @@ namespace Perspex.Input
LostFocusEvent.AddClassHandler<InputElement>(x => x.OnLostFocus);
KeyDownEvent.AddClassHandler<InputElement>(x => x.OnKeyDown);
KeyUpEvent.AddClassHandler<InputElement>(x => x.OnKeyUp);
TextInputEvent.AddClassHandler<InputElement>(x => x.OnTextInput);
PointerEnterEvent.AddClassHandler<InputElement>(x => x.OnPointerEnter);
PointerLeaveEvent.AddClassHandler<InputElement>(x => x.OnPointerLeave);
PointerMovedEvent.AddClassHandler<InputElement>(x => x.OnPointerMoved);
@ -180,6 +189,15 @@ namespace Perspex.Input
remove { this.RemoveHandler(KeyUpEvent, value); }
}
/// <summary>
/// Occurs when a user typed some text while the control has focus.
/// </summary>
public event EventHandler<TextInputEventArgs> TextInput
{
add { this.AddHandler(TextInputEvent, value); }
remove { this.RemoveHandler(TextInputEvent, value); }
}
/// <summary>
/// Occurs when the pointer enters the control.
/// </summary>
@ -377,6 +395,14 @@ namespace Perspex.Input
{
}
/// <summary>
/// Called before the <see cref="TextInput"/> event occurs.
/// </summary>
/// <param name="e">The event args.</param>
protected virtual void OnTextInput(TextInputEventArgs e)
{
}
/// <summary>
/// Called before the <see cref="PointerEnter"/> event occurs.
/// </summary>

2
src/Perspex.Input/KeyEventArgs.cs

@ -14,7 +14,5 @@ namespace Perspex.Input
public IKeyboardDevice Device { get; set; }
public Key Key { get; set; }
public string Text { get; set; }
}
}

53
src/Perspex.Input/KeyboardDevice.cs

@ -22,7 +22,7 @@ namespace Perspex.Input
public KeyboardDevice()
{
this.InputManager.RawEventReceived
.OfType<RawKeyEventArgs>()
.OfType<RawInputEventArgs>()
.Where(x => x.Device == this)
.Subscribe(this.ProcessRawEvent);
}
@ -93,30 +93,45 @@ namespace Perspex.Input
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void ProcessRawEvent(RawKeyEventArgs e)
private void ProcessRawEvent(RawInputEventArgs e)
{
IInputElement element = this.FocusedElement;
if (element != null)
{
switch (e.Type)
var keyInput = e as RawKeyEventArgs;
if (keyInput != null)
{
case RawKeyEventType.KeyDown:
case RawKeyEventType.KeyUp:
var routedEvent = e.Type == RawKeyEventType.KeyDown ?
InputElement.KeyDownEvent : InputElement.KeyUpEvent;
KeyEventArgs ev = new KeyEventArgs
{
RoutedEvent = routedEvent,
Device = this,
Key = e.Key,
Text = e.Text,
Source = element,
};
element.RaiseEvent(ev);
break;
switch (keyInput.Type)
{
case RawKeyEventType.KeyDown:
case RawKeyEventType.KeyUp:
var routedEvent = keyInput.Type == RawKeyEventType.KeyDown
? InputElement.KeyDownEvent
: InputElement.KeyUpEvent;
KeyEventArgs ev = new KeyEventArgs
{
RoutedEvent = routedEvent,
Device = this,
Key = keyInput.Key,
Source = element,
};
element.RaiseEvent(ev);
break;
}
}
var text = e as RawTextInputEventArgs;
if (text != null)
{
element.RaiseEvent(new TextInputEventArgs()
{
Device = this,
Text = text.Text,
Source = element,
RoutedEvent = InputElement.TextInputEvent
});
}
}
}

2
src/Perspex.Input/Perspex.Input.csproj

@ -66,6 +66,7 @@
</Compile>
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="AccessKeyHandler.cs" />
<Compile Include="Raw\RawTextInputEventArgs.cs" />
<Compile Include="NavigationMethod.cs" />
<Compile Include="IInputRoot.cs" />
<Compile Include="IMainMenu.cs" />
@ -100,6 +101,7 @@
<Compile Include="Raw\RawMouseWheelEventArgs.cs" />
<Compile Include="Raw\RawSizeEventArgs.cs" />
<Compile Include="KeyboardNavigationMode.cs" />
<Compile Include="TextInputEventArgs.cs" />
<Compile Include="VectorEventArgs.cs" />
<Compile Include="KeyEventArgs.cs" />
<Compile Include="MouseDevice.cs" />

8
src/Perspex.Input/Raw/RawKeyEventArgs.cs

@ -18,19 +18,15 @@ namespace Perspex.Input.Raw
IKeyboardDevice device,
uint timestamp,
RawKeyEventType type,
Key key,
string text)
Key key)
: base(device, timestamp)
{
this.Key = key;
this.Type = type;
this.Text = text;
}
public Key Key { get; set; }
public string Text { get; set; }
public RawKeyEventType Type { get; set; }
}
}

13
src/Perspex.Input/Raw/RawTextInputEventArgs.cs

@ -0,0 +1,13 @@
namespace Perspex.Input.Raw
{
public class RawTextInputEventArgs : RawInputEventArgs
{
public string Text { get; set; }
public RawTextInputEventArgs(IKeyboardDevice device, uint timestamp, string text) : base(device, timestamp)
{
Text = text;
}
}
}

16
src/Perspex.Input/TextInputEventArgs.cs

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Perspex.Interactivity;
namespace Perspex.Input
{
public class TextInputEventArgs : RoutedEventArgs
{
public IKeyboardDevice Device { get; set; }
public string Text { get; set; }
}
}

11
src/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs

@ -508,10 +508,10 @@ namespace Perspex.Win32.Interop
IntPtr hInstance,
IntPtr lpParam);
[DllImport("user32.dll")]
[DllImport("user32.dll", EntryPoint = "DefWindowProcW")]
public static extern IntPtr DefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
[DllImport("user32.dll", EntryPoint = "DispatchMessageW")]
public static extern IntPtr DispatchMessage(ref MSG lpmsg);
[DllImport("user32.dll", SetLastError = true)]
@ -538,7 +538,7 @@ namespace Perspex.Win32.Interop
[DllImport("user32.dll")]
public static extern bool GetKeyboardState(byte[] lpKeyState);
[DllImport("user32.dll")]
[DllImport("user32.dll", EntryPoint = "GetMessageW")]
public static extern sbyte GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
[DllImport("user32.dll")]
@ -565,6 +565,9 @@ namespace Perspex.Win32.Interop
[DllImport("user32.dll")]
public static extern bool IsWindowEnabled(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool IsWindowUnicode(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool KillTimer(IntPtr hWnd, IntPtr uIDEvent);
@ -577,7 +580,7 @@ namespace Perspex.Win32.Interop
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "RegisterClassExW")]
public static extern ushort RegisterClassEx(ref WNDCLASSEX lpwcx);
[DllImport("user32.dll")]

15
src/Windows/Perspex.Win32/WindowImpl.cs

@ -4,6 +4,8 @@
// </copyright>
// -----------------------------------------------------------------------
using Perspex.Input;
namespace Perspex.Win32
{
using System;
@ -203,6 +205,8 @@ namespace Perspex.Win32
[SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")]
protected virtual IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
bool unicode = UnmanagedMethods.IsWindowUnicode(hWnd);
const double WheelDelta = 120.0;
uint timestamp = unchecked((uint)UnmanagedMethods.GetMessageTime());
@ -252,8 +256,7 @@ namespace Perspex.Win32
WindowsKeyboardDevice.Instance,
timestamp,
RawKeyEventType.KeyDown,
KeyInterop.KeyFromVirtualKey((int)wParam),
WindowsKeyboardDevice.Instance.StringFromVirtualKey((uint)wParam));
KeyInterop.KeyFromVirtualKey((int)wParam));
break;
case UnmanagedMethods.WindowsMessage.WM_KEYUP:
@ -263,10 +266,12 @@ namespace Perspex.Win32
WindowsKeyboardDevice.Instance,
timestamp,
RawKeyEventType.KeyUp,
KeyInterop.KeyFromVirtualKey((int)wParam),
WindowsKeyboardDevice.Instance.StringFromVirtualKey((uint)wParam));
KeyInterop.KeyFromVirtualKey((int)wParam));
break;
case UnmanagedMethods.WindowsMessage.WM_CHAR:
e = new RawTextInputEventArgs(WindowsKeyboardDevice.Instance, timestamp,
new string((char) wParam.ToInt32(), 1));
break;
case UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN:
e = new RawMouseEventArgs(
WindowsMouseDevice.Instance,

3
tests/Perspex.Controls.UnitTests/TopLevelTests.cs

@ -300,8 +300,7 @@ namespace Perspex.Controls.UnitTests
new Mock<IKeyboardDevice>().Object,
0,
RawKeyEventType.KeyDown,
Key.A,
"A");
Key.A);
impl.Object.Input(input);
var inputManagerMock = Mock.Get(InputManager.Instance);

Loading…
Cancel
Save