diff --git a/src/Avalonia.Controls/MenuItemAccessKeyHandler.cs b/src/Avalonia.Controls/MenuItemAccessKeyHandler.cs
index 002946bb79d..78cbc6c2641 100644
--- a/src/Avalonia.Controls/MenuItemAccessKeyHandler.cs
+++ b/src/Avalonia.Controls/MenuItemAccessKeyHandler.cs
@@ -12,7 +12,7 @@ namespace Avalonia.Controls
///
/// Handles access keys within a
///
- public class MenuItemAccessKeyHandler : IAccessKeyHandler
+ public class MenuItemAccessKeyHandler : IAccessKeyHandler, ITextInputHandler
{
///
/// The registered access keys.
@@ -50,7 +50,16 @@ public void SetOwner(IInputRoot owner)
_owner = owner;
- _owner.AddHandler(InputElement.TextInputEvent, OnTextInput);
+ _owner.AddHandler(InputElement.TextInputHandlerSelectionEvent, OnTextInputHandlerSelection, RoutingStrategies.Bubble);
+ }
+
+ private void OnTextInputHandlerSelection(object sender, TextInputHandlerSelectionEventArgs e)
+ {
+ if (!e.Handled && e.Handler == null)
+ {
+ e.Handler = this;
+ e.Handled = true;
+ }
}
///
@@ -84,20 +93,18 @@ public void Unregister(IInputElement element)
///
/// Handles a key being pressed in the menu.
+ /// We are currently using "text" input event, which we shouldn't
///
- /// The event sender.
- /// The event args.
- protected virtual void OnTextInput(object sender, TextInputEventArgs e)
+ // TODO: Replace this with KeyDown event
+ void ITextInputHandler.OnTextEntered (uint timestamp, string text)
{
- if (!string.IsNullOrWhiteSpace(e.Text))
+ if (!string.IsNullOrWhiteSpace(text))
{
- var text = e.Text.ToUpper();
+ text = text.ToUpperInvariant();
var focus = _registered
.FirstOrDefault(x => x.Item1 == text && x.Item2.IsEffectivelyVisible)?.Item2;
focus?.RaiseEvent(new RoutedEventArgs(AccessKeyHandler.AccessKeyPressedEvent));
-
- e.Handled = true;
}
}
}
diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs
index dc2884b36b8..d2d07d35271 100644
--- a/src/Avalonia.Controls/TextBox.cs
+++ b/src/Avalonia.Controls/TextBox.cs
@@ -18,7 +18,8 @@
namespace Avalonia.Controls
{
- public class TextBox : TemplatedControl, UndoRedoHelper.IUndoRedoHost
+ public class TextBox : TemplatedControl, UndoRedoHelper.IUndoRedoHost,
+ ITextInputHandler
{
public static readonly StyledProperty AcceptsReturnProperty =
AvaloniaProperty.Register(nameof(AcceptsReturn));
@@ -308,13 +309,20 @@ protected override void OnLostFocus(RoutedEventArgs e)
_presenter?.HideCaret();
}
- protected override void OnTextInput(TextInputEventArgs e)
+ protected override void OnTextInputHandlerSelection(TextInputHandlerSelectionEventArgs e)
{
if (!e.Handled)
{
- HandleTextInput(e.Text);
+ e.Handler = this;
e.Handled = true;
}
+ base.OnTextInputHandlerSelection(e);
+ }
+
+ void ITextInputHandler.OnTextEntered (uint timestamp, string text)
+ {
+ if (!RaiseCompatibleTextInput(text))
+ HandleTextInput(text);
}
private void HandleTextInput(string input)
diff --git a/src/Avalonia.Diagnostics/ViewModels/EventOwnerTreeNode.cs b/src/Avalonia.Diagnostics/ViewModels/EventOwnerTreeNode.cs
index 06749184002..b360b0ea18d 100644
--- a/src/Avalonia.Diagnostics/ViewModels/EventOwnerTreeNode.cs
+++ b/src/Avalonia.Diagnostics/ViewModels/EventOwnerTreeNode.cs
@@ -18,7 +18,7 @@ internal class EventOwnerTreeNode : EventTreeNodeBase
Button.ClickEvent,
InputElement.KeyDownEvent,
InputElement.KeyUpEvent,
- InputElement.TextInputEvent,
+ InputElement.TextInputHandlerSelectionEvent,
InputElement.PointerReleasedEvent,
InputElement.PointerPressedEvent,
};
diff --git a/src/Avalonia.Input/IInputElement.cs b/src/Avalonia.Input/IInputElement.cs
index c9924dbffbd..b151796355f 100644
--- a/src/Avalonia.Input/IInputElement.cs
+++ b/src/Avalonia.Input/IInputElement.cs
@@ -33,9 +33,9 @@ public interface IInputElement : IInteractive, IVisual
event EventHandler KeyUp;
///
- /// Occurs when a user typed some text while the control has focus.
+ /// Occurs when text input system asks for a handler to be selected
///
- event EventHandler TextInput;
+ event EventHandler TextInputHandlerSelection;
///
/// Occurs when the pointer enters the control.
diff --git a/src/Avalonia.Input/ITextInputHandler.cs b/src/Avalonia.Input/ITextInputHandler.cs
new file mode 100644
index 00000000000..559dffd1393
--- /dev/null
+++ b/src/Avalonia.Input/ITextInputHandler.cs
@@ -0,0 +1,7 @@
+namespace Avalonia.Input
+{
+ public interface ITextInputHandler
+ {
+ void OnTextEntered(uint timestamp, string text);
+ }
+}
diff --git a/src/Avalonia.Input/InputElement.cs b/src/Avalonia.Input/InputElement.cs
index e7fea94d3d8..a8dac0d05d9 100644
--- a/src/Avalonia.Input/InputElement.cs
+++ b/src/Avalonia.Input/InputElement.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
@@ -85,11 +86,11 @@ public class InputElement : Interactive, IInputElement
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
///
- /// Defines the event.
+ /// Defines the event.
///
- public static readonly RoutedEvent TextInputEvent =
- RoutedEvent.Register(
- "TextInput",
+ public static readonly RoutedEvent TextInputHandlerSelectionEvent =
+ RoutedEvent.Register(
+ "TextInputHandlerSelection",
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
///
@@ -160,7 +161,7 @@ static InputElement()
LostFocusEvent.AddClassHandler(x => x.OnLostFocus);
KeyDownEvent.AddClassHandler(x => x.OnKeyDown);
KeyUpEvent.AddClassHandler(x => x.OnKeyUp);
- TextInputEvent.AddClassHandler(x => x.OnTextInput);
+ TextInputHandlerSelectionEvent.AddClassHandler(x => x.OnTextInputHandlerSelection);
PointerEnterEvent.AddClassHandler(x => x.OnPointerEnterCore);
PointerLeaveEvent.AddClassHandler(x => x.OnPointerLeaveCore);
PointerMovedEvent.AddClassHandler(x => x.OnPointerMoved);
@@ -210,12 +211,12 @@ public event EventHandler KeyUp
}
///
- /// Occurs when a user typed some text while the control has focus.
+ /// Occurs when text input system asks for a handler to be selected
///
- public event EventHandler TextInput
+ public event EventHandler TextInputHandlerSelection
{
- add { AddHandler(TextInputEvent, value); }
- remove { RemoveHandler(TextInputEvent, value); }
+ add { AddHandler(TextInputHandlerSelectionEvent, value); }
+ remove { RemoveHandler(TextInputHandlerSelectionEvent, value); }
}
///
@@ -430,14 +431,6 @@ protected virtual void OnKeyUp(KeyEventArgs e)
{
}
- ///
- /// Called before the event occurs.
- ///
- /// The event args.
- protected virtual void OnTextInput(TextInputEventArgs e)
- {
- }
-
///
/// Called before the event occurs.
///
@@ -540,5 +533,121 @@ private void UpdateIsEnabledCore(InputElement parent)
child.UpdateIsEnabledCore(this);
}
}
+
+ #region OnTextInput compatibility layer
+
+ private static Dictionary s_compatibleTextInputRegistry = new Dictionary();
+ private CompatibleTextInputHandler _compatibleTextInputHandler;
+ class CompatibleTextInputHandler : ITextInputHandler
+ {
+ private readonly InputElement _parent;
+
+ public CompatibleTextInputHandler(InputElement parent)
+ {
+ _parent = parent;
+ }
+
+ public EventHandler Event;
+ public void OnTextEntered(uint timestamp, string text)
+ {
+ _parent.RaiseCompatibleTextInput(text);
+ }
+ }
+
+ ///
+ /// Called before the event occurs.
+ ///
+ /// The event args.
+ protected virtual void OnTextInputHandlerSelection(TextInputHandlerSelectionEventArgs e)
+ {
+ if (_compatibleTextInputHandler != null && !e.Handled)
+ {
+ e.Handler = _compatibleTextInputHandler;
+ e.Handled = true;
+ }
+ }
+
+ [Obsolete("Use TextInputHandlerSelection")]
+ public event EventHandler TextInput
+ {
+ add
+ {
+ if (_compatibleTextInputHandler == null)
+ {
+ _compatibleTextInputHandler = new CompatibleTextInputHandler(this);
+ _compatibleTextInputHandler.Event += value;
+ }
+ }
+ remove
+ {
+ if (_compatibleTextInputHandler != null)
+ {
+ _compatibleTextInputHandler.Event -= value;
+ if (_compatibleTextInputHandler.Event == null)
+ _compatibleTextInputHandler = null;
+ }
+ }
+ }
+
+ protected internal bool RaiseCompatibleTextInput(string text)
+ {
+ if (_compatibleTextInputHandler != null)
+ {
+ var ev = new TextInputEventArgs
+ {
+ Text = text,
+ Source = this
+ };
+ _compatibleTextInputHandler.Event?.Invoke(this, ev);
+ if(!ev.Handled)
+ OnTextInput(ev);
+ return ev.Handled;
+ }
+
+ return false;
+ }
+
+ [Obsolete("Use TextInputHandlerSelection")]
+ protected virtual void OnTextInput(TextInputEventArgs e)
+ {
+
+ }
+
+ static bool CheckIfOverridden(MethodInfo method, Type target)
+ {
+ while (true)
+ {
+ var baseMethod = method.GetBaseDefinition();
+ if(baseMethod == method || baseMethod == null)
+ break;
+ method = baseMethod;
+ }
+
+ var fromType = method.DeclaringType;
+ while (fromType != target && target != null)
+ {
+ var found = target.GetMethod(method.Name,
+ BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
+ null, method.GetParameters().Select(p => p.ParameterType).ToArray(), null);
+ if (found != null && found.DeclaringType != fromType)
+ return true;
+
+ target = target.BaseType;
+ }
+
+ return false;
+ }
+
+ public InputElement()
+ {
+ var type = GetType();
+ if (!s_compatibleTextInputRegistry.TryGetValue(type, out var needsCompat))
+ s_compatibleTextInputRegistry[type] = needsCompat =
+ CheckIfOverridden(new Action(OnTextInput).Method, type);
+ if (needsCompat)
+ TextInput += (o, e) => OnTextInput(e);
+ }
+
+ #endregion
}
}
diff --git a/src/Avalonia.Input/KeyboardDevice.cs b/src/Avalonia.Input/KeyboardDevice.cs
index 00606bd9b17..4936b46aa9c 100644
--- a/src/Avalonia.Input/KeyboardDevice.cs
+++ b/src/Avalonia.Input/KeyboardDevice.cs
@@ -115,20 +115,23 @@ public void ProcessRawEvent(RawInputEventArgs e)
}
}
+
+ // Compatibility layer with backends that still use RawTextInputEventArgs
var text = e as RawTextInputEventArgs;
if (text != null)
{
- var ev = new TextInputEventArgs()
+ var ev = new TextInputHandlerSelectionEventArgs
{
- Device = this,
- Text = text.Text,
Source = element,
- RoutedEvent = InputElement.TextInputEvent
+ RoutedEvent = InputElement.TextInputHandlerSelectionEvent
};
-
element.RaiseEvent(ev);
- e.Handled = ev.Handled;
+ if (ev.Handled && ev.Handler != null)
+ {
+ ev.Handler.OnTextEntered(e.Timestamp, text.Text);
+ e.Handled = ev.Handled;
+ }
}
}
}
diff --git a/src/Avalonia.Input/Raw/RawTextInputEventArgs.cs b/src/Avalonia.Input/Raw/RawTextInputEventArgs.cs
index 6e427c3751f..563e2cf5c27 100644
--- a/src/Avalonia.Input/Raw/RawTextInputEventArgs.cs
+++ b/src/Avalonia.Input/Raw/RawTextInputEventArgs.cs
@@ -1,8 +1,11 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
+using System;
+
namespace Avalonia.Input.Raw
{
+ [Obsolete("Use RawTextInputHandlerSelectionEventArgs")]
public class RawTextInputEventArgs : RawInputEventArgs
{
public string Text { get; set; }
diff --git a/src/Avalonia.Input/TextInputEventArgs.cs b/src/Avalonia.Input/TextInputEventArgs.cs
index b7203b54d58..5ef97ee69eb 100644
--- a/src/Avalonia.Input/TextInputEventArgs.cs
+++ b/src/Avalonia.Input/TextInputEventArgs.cs
@@ -1,10 +1,12 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
+using System;
using Avalonia.Interactivity;
namespace Avalonia.Input
{
+ [Obsolete("Use TextInputHandlerSelection")]
public class TextInputEventArgs : RoutedEventArgs
{
public IKeyboardDevice Device { get; set; }
diff --git a/src/Avalonia.Input/TextInputHandlerSelectionEventArgs.cs b/src/Avalonia.Input/TextInputHandlerSelectionEventArgs.cs
new file mode 100644
index 00000000000..203b5672f89
--- /dev/null
+++ b/src/Avalonia.Input/TextInputHandlerSelectionEventArgs.cs
@@ -0,0 +1,9 @@
+using Avalonia.Interactivity;
+
+namespace Avalonia.Input
+{
+ public class TextInputHandlerSelectionEventArgs : RoutedEventArgs
+ {
+ public ITextInputHandler Handler { get; set; }
+ }
+}
diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
index 0d87f6d0fe0..75bd985ab84 100644
--- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
@@ -114,12 +114,7 @@ public void Typing_Beginning_With_0_Should_Not_Modify_Text_When_Bound_To_Int()
Assert.Equal("0", target.Text);
target.CaretIndex = 1;
- target.RaiseEvent(new TextInputEventArgs
- {
- RoutedEvent = InputElement.TextInputEvent,
- Text = "2",
- });
-
+ RaiseTextEvent(target, "2");
Assert.Equal("02", target.Text);
}
}
@@ -417,11 +412,12 @@ private void RaiseKeyEvent(TextBox textBox, Key key, InputModifiers inputModifie
private void RaiseTextEvent(TextBox textBox, string text)
{
- textBox.RaiseEvent(new TextInputEventArgs
+ var ev = new TextInputHandlerSelectionEventArgs
{
- RoutedEvent = InputElement.TextInputEvent,
- Text = text
- });
+ RoutedEvent = InputElement.TextInputHandlerSelectionEvent
+ };
+ textBox.RaiseEvent(ev);
+ ev.Handler?.OnTextEntered(0, text);
}
private class Class1 : NotifyingBase