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 6 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 IDataObject _draggedData;
private IInputElement _lastRoot;
private IInputRoot _lastRoot;
private Point _lastPosition;
private StandardCursorType _lastCursorType;
private object _originalCursor;
@ -56,7 +56,7 @@ namespace Avalonia.Platform
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;
@ -91,7 +91,7 @@ namespace Avalonia.Platform
return StandardCursorType.No;
}
private void UpdateCursor(IInputElement root, DragDropEffects effect)
private void UpdateCursor(IInputRoot root, DragDropEffects effect)
{
if (_lastRoot != root)
{

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

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

18
src/Avalonia.Input/DragDropDevice.cs

@ -11,7 +11,7 @@ namespace Avalonia.Input
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();
if (target != null && DragDrop.GetAllowDrop(target))
@ -19,7 +19,7 @@ namespace Avalonia.Input
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)
return DragDropEffects.None;
@ -38,13 +38,13 @@ namespace Avalonia.Input
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);
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);
@ -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
{
@ -100,16 +100,16 @@ namespace Avalonia.Input
switch (e.Type)
{
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;
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;
case RawDragEventType.DragLeave:
DragLeave(e.InputRoot);
DragLeave(e.Root);
break;
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;
}
}

102
src/Avalonia.Input/KeyboardDevice.cs

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

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

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

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

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

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

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

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

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

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

@ -5,11 +5,16 @@ namespace Avalonia.Input.Raw
{
public class RawTextInputEventArgs : RawInputEventArgs
{
public string Text { get; set; }
public RawTextInputEventArgs(IKeyboardDevice device, ulong timestamp, string text) : base(device, timestamp)
public RawTextInputEventArgs(
IKeyboardDevice device,
ulong timestamp,
IInputRoot root,
string text)
: base(device, timestamp, root)
{
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);
var args = new RawTextInputEventArgs(_keyboard, timeStamp, text);
var args = new RawTextInputEventArgs(_keyboard, timeStamp, _inputRoot, text);
Input?.Invoke(args);
@ -215,7 +215,7 @@ namespace Avalonia.Native
{
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);

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();
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,
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
return;
}
ScheduleInput(new RawTextInputEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), text),
ScheduleInput(new RawTextInputEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), _inputRoot, text),
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 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,
GetModifiers(null)));
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,
GetModifiers(null)));
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)
{

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

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

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

@ -508,6 +508,7 @@ namespace Avalonia.Win32
e = new RawKeyEventArgs(
WindowsKeyboardDevice.Instance,
timestamp,
_owner,
RawKeyEventType.KeyDown,
KeyInterop.KeyFromVirtualKey(ToInt32(wParam)), WindowsKeyboardDevice.Instance.Modifiers);
break;
@ -521,6 +522,7 @@ namespace Avalonia.Win32
e = new RawKeyEventArgs(
WindowsKeyboardDevice.Instance,
timestamp,
_owner,
RawKeyEventType.KeyUp,
KeyInterop.KeyFromVirtualKey(ToInt32(wParam)), WindowsKeyboardDevice.Instance.Modifiers);
break;
@ -528,7 +530,7 @@ namespace Avalonia.Win32
// Ignore control chars
if (ToInt32(wParam) >= 32)
{
e = new RawTextInputEventArgs(WindowsKeyboardDevice.Instance, timestamp,
e = new RawTextInputEventArgs(WindowsKeyboardDevice.Instance, timestamp, _owner,
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(
new Mock<IKeyboardDevice>().Object,
0,
target,
RawKeyEventType.KeyDown,
Key.A, RawInputModifiers.None);
impl.Object.Input(input);

Loading…
Cancel
Save