Browse Source

Send keypresses to toplevel if no focused control.

- Added `IInputRoot Root` property to `RawInputEventArgs`
- Removed `InputRoot` and `Root` properties from `RawDragEvent` and `RawPointerEvent` as its now in the base class
- `InputRoot` in `RawDragEvent` was incorrectly using the type `IInputElement` instead of `IInputRoot`, fix callers that were using the wrong type
- Pass the input root to all raw input event constructors
- If no control is focused in `KeyboardDevice.ProcessRawEvent` then raise the event on the root
pull/3129/head
Steven Kirk 7 years ago
parent
commit
fb173b30e2
  1. 6
      src/Avalonia.Controls/Platform/InProcessDragSource.cs
  2. 2
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  3. 18
      src/Avalonia.Input/DragDropDevice.cs
  4. 102
      src/Avalonia.Input/KeyboardDevice.cs
  5. 6
      src/Avalonia.Input/Raw/RawDragEvent.cs
  6. 9
      src/Avalonia.Input/Raw/RawInputEventArgs.cs
  7. 3
      src/Avalonia.Input/Raw/RawKeyEventArgs.cs
  8. 8
      src/Avalonia.Input/Raw/RawPointerEventArgs.cs
  9. 11
      src/Avalonia.Input/Raw/RawTextInputEventArgs.cs
  10. 4
      src/Avalonia.Native/WindowImplBase.cs
  11. 4
      src/Avalonia.X11/X11Window.cs
  12. 6
      src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
  13. 4
      src/Windows/Avalonia.Win32/OleDropTarget.cs
  14. 4
      src/Windows/Avalonia.Win32/WindowImpl.cs
  15. 1
      tests/Avalonia.Controls.UnitTests/TopLevelTests.cs

6
src/Avalonia.Controls/Platform/InProcessDragSource.cs

@ -21,7 +21,7 @@ namespace Avalonia.Platform
private DragDropEffects _allowedEffects; private DragDropEffects _allowedEffects;
private IDataObject _draggedData; private IDataObject _draggedData;
private IInputElement _lastRoot; private IInputRoot _lastRoot;
private Point _lastPosition; private Point _lastPosition;
private StandardCursorType _lastCursorType; private StandardCursorType _lastCursorType;
private object _originalCursor; private object _originalCursor;
@ -56,7 +56,7 @@ namespace Avalonia.Platform
return DragDropEffects.None; return DragDropEffects.None;
} }
private DragDropEffects RaiseEventAndUpdateCursor(RawDragEventType type, IInputElement root, Point pt, RawInputModifiers modifiers) private DragDropEffects RaiseEventAndUpdateCursor(RawDragEventType type, IInputRoot root, Point pt, RawInputModifiers modifiers)
{ {
_lastPosition = pt; _lastPosition = pt;
@ -91,7 +91,7 @@ namespace Avalonia.Platform
return StandardCursorType.No; return StandardCursorType.No;
} }
private void UpdateCursor(IInputElement root, DragDropEffects effect) private void UpdateCursor(IInputRoot root, DragDropEffects effect)
{ {
if (_lastRoot != root) if (_lastRoot != root)
{ {

2
src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs

@ -227,6 +227,7 @@ namespace Avalonia.Controls.Remote.Server
Input?.Invoke(new RawKeyEventArgs( Input?.Invoke(new RawKeyEventArgs(
KeyboardDevice, KeyboardDevice,
0, 0,
InputRoot,
key.IsDown ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, key.IsDown ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
(Key)key.Key, (Key)key.Key,
GetAvaloniaRawInputModifiers(key.Modifiers))); GetAvaloniaRawInputModifiers(key.Modifiers)));
@ -241,6 +242,7 @@ namespace Avalonia.Controls.Remote.Server
Input?.Invoke(new RawTextInputEventArgs( Input?.Invoke(new RawTextInputEventArgs(
KeyboardDevice, KeyboardDevice,
0, 0,
InputRoot,
text.Text)); text.Text));
}, DispatcherPriority.Input); }, DispatcherPriority.Input);
} }

18
src/Avalonia.Input/DragDropDevice.cs

@ -11,7 +11,7 @@ namespace Avalonia.Input
private Interactive _lastTarget = null; private Interactive _lastTarget = null;
private Interactive GetTarget(IInputElement root, Point local) private Interactive GetTarget(IInputRoot root, Point local)
{ {
var target = root.InputHitTest(local)?.GetSelfAndVisualAncestors()?.OfType<Interactive>()?.FirstOrDefault(); var target = root.InputHitTest(local)?.GetSelfAndVisualAncestors()?.OfType<Interactive>()?.FirstOrDefault();
if (target != null && DragDrop.GetAllowDrop(target)) if (target != null && DragDrop.GetAllowDrop(target))
@ -19,7 +19,7 @@ namespace Avalonia.Input
return null; return null;
} }
private DragDropEffects RaiseDragEvent(Interactive target, IInputElement inputRoot, Point point, RoutedEvent<DragEventArgs> routedEvent, DragDropEffects operation, IDataObject data, InputModifiers modifiers) private DragDropEffects RaiseDragEvent(Interactive target, IInputRoot inputRoot, Point point, RoutedEvent<DragEventArgs> routedEvent, DragDropEffects operation, IDataObject data, InputModifiers modifiers)
{ {
if (target == null) if (target == null)
return DragDropEffects.None; return DragDropEffects.None;
@ -38,13 +38,13 @@ namespace Avalonia.Input
return args.DragEffects; return args.DragEffects;
} }
private DragDropEffects DragEnter(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects, InputModifiers modifiers) private DragDropEffects DragEnter(IInputRoot inputRoot, Point point, IDataObject data, DragDropEffects effects, InputModifiers modifiers)
{ {
_lastTarget = GetTarget(inputRoot, point); _lastTarget = GetTarget(inputRoot, point);
return RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DragEnterEvent, effects, data, modifiers); return RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DragEnterEvent, effects, data, modifiers);
} }
private DragDropEffects DragOver(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects, InputModifiers modifiers) private DragDropEffects DragOver(IInputRoot inputRoot, Point point, IDataObject data, DragDropEffects effects, InputModifiers modifiers)
{ {
var target = GetTarget(inputRoot, point); var target = GetTarget(inputRoot, point);
@ -77,7 +77,7 @@ namespace Avalonia.Input
} }
} }
private DragDropEffects Drop(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects, InputModifiers modifiers) private DragDropEffects Drop(IInputRoot inputRoot, Point point, IDataObject data, DragDropEffects effects, InputModifiers modifiers)
{ {
try try
{ {
@ -100,16 +100,16 @@ namespace Avalonia.Input
switch (e.Type) switch (e.Type)
{ {
case RawDragEventType.DragEnter: case RawDragEventType.DragEnter:
e.Effects = DragEnter(e.InputRoot, e.Location, e.Data, e.Effects, e.Modifiers); e.Effects = DragEnter(e.Root, e.Location, e.Data, e.Effects, e.Modifiers);
break; break;
case RawDragEventType.DragOver: case RawDragEventType.DragOver:
e.Effects = DragOver(e.InputRoot, e.Location, e.Data, e.Effects, e.Modifiers); e.Effects = DragOver(e.Root, e.Location, e.Data, e.Effects, e.Modifiers);
break; break;
case RawDragEventType.DragLeave: case RawDragEventType.DragLeave:
DragLeave(e.InputRoot); DragLeave(e.Root);
break; break;
case RawDragEventType.Drop: case RawDragEventType.Drop:
e.Effects = Drop(e.InputRoot, e.Location, e.Data, e.Effects, e.Modifiers); e.Effects = Drop(e.Root, e.Location, e.Data, e.Effects, e.Modifiers);
break; break;
} }
} }

102
src/Avalonia.Input/KeyboardDevice.cs

@ -70,66 +70,60 @@ namespace Avalonia.Input
{ {
if(e.Handled) if(e.Handled)
return; return;
IInputElement element = FocusedElement;
if (element != null) var element = FocusedElement ?? e.Root;
{
var keyInput = e as RawKeyEventArgs;
if (keyInput != null) if (e is RawKeyEventArgs keyInput)
{
switch (keyInput.Type)
{ {
switch (keyInput.Type) case RawKeyEventType.KeyDown:
{ case RawKeyEventType.KeyUp:
case RawKeyEventType.KeyDown: var routedEvent = keyInput.Type == RawKeyEventType.KeyDown
case RawKeyEventType.KeyUp: ? InputElement.KeyDownEvent
var routedEvent = keyInput.Type == RawKeyEventType.KeyDown : InputElement.KeyUpEvent;
? InputElement.KeyDownEvent
: InputElement.KeyUpEvent; KeyEventArgs ev = new KeyEventArgs
{
KeyEventArgs ev = new KeyEventArgs RoutedEvent = routedEvent,
{ Device = this,
RoutedEvent = routedEvent, Key = keyInput.Key,
Device = this, KeyModifiers = KeyModifiersUtils.ConvertToKey(keyInput.Modifiers),
Key = keyInput.Key, Source = element,
KeyModifiers = KeyModifiersUtils.ConvertToKey(keyInput.Modifiers), };
Source = element,
}; IVisual currentHandler = element;
while (currentHandler != null && !ev.Handled && keyInput.Type == RawKeyEventType.KeyDown)
IVisual currentHandler = element; {
while (currentHandler != null && !ev.Handled && keyInput.Type == RawKeyEventType.KeyDown) var bindings = (currentHandler as IInputElement)?.KeyBindings;
{ if (bindings != null)
var bindings = (currentHandler as IInputElement)?.KeyBindings; foreach (var binding in bindings)
if(bindings!=null) {
foreach (var binding in bindings) if (ev.Handled)
{ break;
if(ev.Handled) binding.TryHandle(ev);
break; }
binding.TryHandle(ev); currentHandler = currentHandler.VisualParent;
} }
currentHandler = currentHandler.VisualParent;
} element.RaiseEvent(ev);
e.Handled = ev.Handled;
element.RaiseEvent(ev); break;
e.Handled = ev.Handled;
break;
}
} }
}
var text = e as RawTextInputEventArgs; if (e is RawTextInputEventArgs text)
{
if (text != null) var ev = new TextInputEventArgs()
{ {
var ev = new TextInputEventArgs() Device = this,
{ Text = text.Text,
Device = this, Source = element,
Text = text.Text, RoutedEvent = InputElement.TextInputEvent
Source = element, };
RoutedEvent = InputElement.TextInputEvent
}; element.RaiseEvent(ev);
e.Handled = ev.Handled;
element.RaiseEvent(ev);
e.Handled = ev.Handled;
}
} }
} }
} }

6
src/Avalonia.Input/Raw/RawDragEvent.cs

@ -2,7 +2,6 @@
{ {
public class RawDragEvent : RawInputEventArgs public class RawDragEvent : RawInputEventArgs
{ {
public IInputElement InputRoot { get; }
public Point Location { get; set; } public Point Location { get; set; }
public IDataObject Data { get; } public IDataObject Data { get; }
public DragDropEffects Effects { get; set; } public DragDropEffects Effects { get; set; }
@ -10,11 +9,10 @@
public InputModifiers Modifiers { get; } public InputModifiers Modifiers { get; }
public RawDragEvent(IDragDropDevice inputDevice, RawDragEventType type, public RawDragEvent(IDragDropDevice inputDevice, RawDragEventType type,
IInputElement inputRoot, Point location, IDataObject data, DragDropEffects effects, RawInputModifiers modifiers) IInputRoot root, Point location, IDataObject data, DragDropEffects effects, RawInputModifiers modifiers)
:base(inputDevice, 0) :base(inputDevice, 0, root)
{ {
Type = type; Type = type;
InputRoot = inputRoot;
Location = location; Location = location;
Data = data; Data = data;
Effects = effects; Effects = effects;

9
src/Avalonia.Input/Raw/RawInputEventArgs.cs

@ -21,12 +21,14 @@ namespace Avalonia.Input.Raw
/// </summary> /// </summary>
/// <param name="device">The associated device.</param> /// <param name="device">The associated device.</param>
/// <param name="timestamp">The event timestamp.</param> /// <param name="timestamp">The event timestamp.</param>
public RawInputEventArgs(IInputDevice device, ulong timestamp) /// <param name="root">The root from which the event originates.</param>
public RawInputEventArgs(IInputDevice device, ulong timestamp, IInputRoot root)
{ {
Contract.Requires<ArgumentNullException>(device != null); Contract.Requires<ArgumentNullException>(device != null);
Device = device; Device = device;
Timestamp = timestamp; Timestamp = timestamp;
Root = root;
} }
/// <summary> /// <summary>
@ -34,6 +36,11 @@ namespace Avalonia.Input.Raw
/// </summary> /// </summary>
public IInputDevice Device { get; } public IInputDevice Device { get; }
/// <summary>
/// Gets the root from which the event originates.
/// </summary>
public IInputRoot Root { get; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the event was handled. /// Gets or sets a value indicating whether the event was handled.
/// </summary> /// </summary>

3
src/Avalonia.Input/Raw/RawKeyEventArgs.cs

@ -14,9 +14,10 @@ namespace Avalonia.Input.Raw
public RawKeyEventArgs( public RawKeyEventArgs(
IKeyboardDevice device, IKeyboardDevice device,
ulong timestamp, ulong timestamp,
IInputRoot root,
RawKeyEventType type, RawKeyEventType type,
Key key, RawInputModifiers modifiers) Key key, RawInputModifiers modifiers)
: base(device, timestamp) : base(device, timestamp, root)
{ {
Key = key; Key = key;
Type = type; Type = type;

8
src/Avalonia.Input/Raw/RawPointerEventArgs.cs

@ -44,22 +44,16 @@ namespace Avalonia.Input.Raw
RawPointerEventType type, RawPointerEventType type,
Point position, Point position,
RawInputModifiers inputModifiers) RawInputModifiers inputModifiers)
: base(device, timestamp) : base(device, timestamp, root)
{ {
Contract.Requires<ArgumentNullException>(device != null); Contract.Requires<ArgumentNullException>(device != null);
Contract.Requires<ArgumentNullException>(root != null); Contract.Requires<ArgumentNullException>(root != null);
Root = root;
Position = position; Position = position;
Type = type; Type = type;
InputModifiers = inputModifiers; InputModifiers = inputModifiers;
} }
/// <summary>
/// Gets the root from which the event originates.
/// </summary>
public IInputRoot Root { get; }
/// <summary> /// <summary>
/// Gets the mouse position, in client DIPs. /// Gets the mouse position, in client DIPs.
/// </summary> /// </summary>

11
src/Avalonia.Input/Raw/RawTextInputEventArgs.cs

@ -5,11 +5,16 @@ namespace Avalonia.Input.Raw
{ {
public class RawTextInputEventArgs : RawInputEventArgs public class RawTextInputEventArgs : RawInputEventArgs
{ {
public string Text { get; set; } public RawTextInputEventArgs(
IKeyboardDevice device,
public RawTextInputEventArgs(IKeyboardDevice device, ulong timestamp, string text) : base(device, timestamp) ulong timestamp,
IInputRoot root,
string text)
: base(device, timestamp, root)
{ {
Text = text; Text = text;
} }
public string Text { get; set; }
} }
} }

4
src/Avalonia.Native/WindowImplBase.cs

@ -204,7 +204,7 @@ namespace Avalonia.Native
{ {
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
var args = new RawTextInputEventArgs(_keyboard, timeStamp, text); var args = new RawTextInputEventArgs(_keyboard, timeStamp, _inputRoot, text);
Input?.Invoke(args); Input?.Invoke(args);
@ -215,7 +215,7 @@ namespace Avalonia.Native
{ {
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
var args = new RawKeyEventArgs(_keyboard, timeStamp, (RawKeyEventType)type, (Key)key, (RawInputModifiers)modifiers); var args = new RawKeyEventArgs(_keyboard, timeStamp, _inputRoot, (RawKeyEventType)type, (Key)key, (RawInputModifiers)modifiers);
Input?.Invoke(args); Input?.Invoke(args);

4
src/Avalonia.X11/X11Window.cs

@ -455,7 +455,7 @@ namespace Avalonia.X11
key = (X11Key)XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, index ? 0 : 1).ToInt32(); key = (X11Key)XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, index ? 0 : 1).ToInt32();
ScheduleInput(new RawKeyEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), ScheduleInput(new RawKeyEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), _inputRoot,
ev.type == XEventName.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, ev.type == XEventName.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
X11KeyTransform.ConvertKey(key), TranslateModifiers(ev.KeyEvent.state)), ref ev); X11KeyTransform.ConvertKey(key), TranslateModifiers(ev.KeyEvent.state)), ref ev);
@ -470,7 +470,7 @@ namespace Avalonia.X11
if (text[0] < ' ' || text[0] == 0x7f) //Control codes or DEL if (text[0] < ' ' || text[0] == 0x7f) //Control codes or DEL
return; return;
} }
ScheduleInput(new RawTextInputEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), text), ScheduleInput(new RawTextInputEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), _inputRoot, text),
ref ev); ref ev);
} }
} }

6
src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs

@ -212,17 +212,17 @@ namespace Avalonia.Win32.Interop.Wpf
protected override void OnMouseLeave(MouseEventArgs e) => MouseEvent(RawPointerEventType.LeaveWindow, e); protected override void OnMouseLeave(MouseEventArgs e) => MouseEvent(RawPointerEventType.LeaveWindow, e);
protected override void OnKeyDown(KeyEventArgs e) protected override void OnKeyDown(KeyEventArgs e)
=> _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint) e.Timestamp, RawKeyEventType.KeyDown, => _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint) e.Timestamp, _inputRoot, RawKeyEventType.KeyDown,
(Key) e.Key, (Key) e.Key,
GetModifiers(null))); GetModifiers(null)));
protected override void OnKeyUp(KeyEventArgs e) protected override void OnKeyUp(KeyEventArgs e)
=> _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint)e.Timestamp, RawKeyEventType.KeyUp, => _ttl.Input?.Invoke(new RawKeyEventArgs(_keyboard, (uint)e.Timestamp, _inputRoot, RawKeyEventType.KeyUp,
(Key)e.Key, (Key)e.Key,
GetModifiers(null))); GetModifiers(null)));
protected override void OnTextInput(TextCompositionEventArgs e) protected override void OnTextInput(TextCompositionEventArgs e)
=> _ttl.Input?.Invoke(new RawTextInputEventArgs(_keyboard, (uint) e.Timestamp, e.Text)); => _ttl.Input?.Invoke(new RawTextInputEventArgs(_keyboard, (uint) e.Timestamp, _inputRoot, e.Text));
void ITopLevelImpl.SetCursor(IPlatformHandle cursor) void ITopLevelImpl.SetCursor(IPlatformHandle cursor)
{ {

4
src/Windows/Avalonia.Win32/OleDropTarget.cs

@ -8,13 +8,13 @@ namespace Avalonia.Win32
{ {
class OleDropTarget : IDropTarget class OleDropTarget : IDropTarget
{ {
private readonly IInputElement _target; private readonly IInputRoot _target;
private readonly ITopLevelImpl _tl; private readonly ITopLevelImpl _tl;
private readonly IDragDropDevice _dragDevice; private readonly IDragDropDevice _dragDevice;
private IDataObject _currentDrag = null; private IDataObject _currentDrag = null;
public OleDropTarget(ITopLevelImpl tl, IInputElement target) public OleDropTarget(ITopLevelImpl tl, IInputRoot target)
{ {
_dragDevice = AvaloniaLocator.Current.GetService<IDragDropDevice>(); _dragDevice = AvaloniaLocator.Current.GetService<IDragDropDevice>();
_tl = tl; _tl = tl;

4
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -508,6 +508,7 @@ namespace Avalonia.Win32
e = new RawKeyEventArgs( e = new RawKeyEventArgs(
WindowsKeyboardDevice.Instance, WindowsKeyboardDevice.Instance,
timestamp, timestamp,
_owner,
RawKeyEventType.KeyDown, RawKeyEventType.KeyDown,
KeyInterop.KeyFromVirtualKey(ToInt32(wParam)), WindowsKeyboardDevice.Instance.Modifiers); KeyInterop.KeyFromVirtualKey(ToInt32(wParam)), WindowsKeyboardDevice.Instance.Modifiers);
break; break;
@ -521,6 +522,7 @@ namespace Avalonia.Win32
e = new RawKeyEventArgs( e = new RawKeyEventArgs(
WindowsKeyboardDevice.Instance, WindowsKeyboardDevice.Instance,
timestamp, timestamp,
_owner,
RawKeyEventType.KeyUp, RawKeyEventType.KeyUp,
KeyInterop.KeyFromVirtualKey(ToInt32(wParam)), WindowsKeyboardDevice.Instance.Modifiers); KeyInterop.KeyFromVirtualKey(ToInt32(wParam)), WindowsKeyboardDevice.Instance.Modifiers);
break; break;
@ -528,7 +530,7 @@ namespace Avalonia.Win32
// Ignore control chars // Ignore control chars
if (ToInt32(wParam) >= 32) if (ToInt32(wParam) >= 32)
{ {
e = new RawTextInputEventArgs(WindowsKeyboardDevice.Instance, timestamp, e = new RawTextInputEventArgs(WindowsKeyboardDevice.Instance, timestamp, _owner,
new string((char)ToInt32(wParam), 1)); new string((char)ToInt32(wParam), 1));
} }

1
tests/Avalonia.Controls.UnitTests/TopLevelTests.cs

@ -182,6 +182,7 @@ namespace Avalonia.Controls.UnitTests
var input = new RawKeyEventArgs( var input = new RawKeyEventArgs(
new Mock<IKeyboardDevice>().Object, new Mock<IKeyboardDevice>().Object,
0, 0,
target,
RawKeyEventType.KeyDown, RawKeyEventType.KeyDown,
Key.A, RawInputModifiers.None); Key.A, RawInputModifiers.None);
impl.Object.Input(input); impl.Object.Input(input);

Loading…
Cancel
Save