Skip to content

Commit

Permalink
feat: add support for PreviewKey<Down|Up> on skia
Browse files Browse the repository at this point in the history
  • Loading branch information
ramezgerges committed Mar 18, 2024
1 parent 682d6ad commit 5c34158
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ private void SetupEvent(FrameworkElement elt)
global::System.Diagnostics.Debug.WriteLine($"{elt.Name} - [KEYUP] {e.Key}");
_output.Text += $"{elt.Name} - [KEYUP] {e.Key}\r\n";
};
#if __WASM__
#if __WASM__ || __SKIA__
elt.PreviewKeyDown += (snd, e) =>
{
Console.WriteLine($"{elt.Name} - [PREVIEWKEYDOWN] {e.Key}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,15 +274,15 @@ public int CharacterSpacing
// Skipping already declared method Microsoft.UI.Xaml.Controls.Control.OnManipulationCompleted(Microsoft.UI.Xaml.Input.ManipulationCompletedRoutedEventArgs)
// Skipping already declared method Microsoft.UI.Xaml.Controls.Control.OnKeyUp(Microsoft.UI.Xaml.Input.KeyRoutedEventArgs)
// Skipping already declared method Microsoft.UI.Xaml.Controls.Control.OnKeyDown(Microsoft.UI.Xaml.Input.KeyRoutedEventArgs)
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || false || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || false || false || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__NETSTD_REFERENCE__", "__MACOS__")]
protected virtual void OnPreviewKeyDown(global::Microsoft.UI.Xaml.Input.KeyRoutedEventArgs e)
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Xaml.Controls.Control", "void Control.OnPreviewKeyDown(KeyRoutedEventArgs e)");
}
#endif
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || false || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || false || false || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__NETSTD_REFERENCE__", "__MACOS__")]
protected virtual void OnPreviewKeyUp(global::Microsoft.UI.Xaml.Input.KeyRoutedEventArgs e)
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Xaml.Controls.Control", "void Control.OnPreviewKeyUp(KeyRoutedEventArgs e)");
Expand Down
24 changes: 12 additions & 12 deletions src/Uno.UI/Generated/3.0.0.0/Microsoft.UI.Xaml/UIElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -695,8 +695,8 @@ public double RasterizationScale
// Skipping already declared property PointerPressedEvent
// Skipping already declared property PointerReleasedEvent
// Skipping already declared property PointerWheelChangedEvent
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || false || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || false || false || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__NETSTD_REFERENCE__", "__MACOS__")]
public static global::Microsoft.UI.Xaml.RoutedEvent PreviewKeyDownEvent
{
get
Expand All @@ -705,8 +705,8 @@ public double RasterizationScale
}
}
#endif
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || false || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || false || false || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__NETSTD_REFERENCE__", "__MACOS__")]
public static global::Microsoft.UI.Xaml.RoutedEvent PreviewKeyUpEvent
{
get
Expand Down Expand Up @@ -1277,32 +1277,32 @@ public static bool TryStartDirectManipulation(global::Microsoft.UI.Xaml.Input.Po
// Skipping already declared event Microsoft.UI.Xaml.UIElement.PointerPressed
// Skipping already declared event Microsoft.UI.Xaml.UIElement.PointerReleased
// Skipping already declared event Microsoft.UI.Xaml.UIElement.PointerWheelChanged
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || false || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || false || false || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__NETSTD_REFERENCE__", "__MACOS__")]
public event global::Microsoft.UI.Xaml.Input.KeyEventHandler PreviewKeyDown
{
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__NETSTD_REFERENCE__", "__MACOS__")]
add
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Xaml.UIElement", "event KeyEventHandler UIElement.PreviewKeyDown");
}
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__NETSTD_REFERENCE__", "__MACOS__")]
remove
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Xaml.UIElement", "event KeyEventHandler UIElement.PreviewKeyDown");
}
}
#endif
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || false || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || false || false || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__NETSTD_REFERENCE__", "__MACOS__")]
public event global::Microsoft.UI.Xaml.Input.KeyEventHandler PreviewKeyUp
{
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__NETSTD_REFERENCE__", "__MACOS__")]
add
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Xaml.UIElement", "event KeyEventHandler UIElement.PreviewKeyUp");
}
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__NETSTD_REFERENCE__", "__MACOS__")]
remove
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Xaml.UIElement", "event KeyEventHandler UIElement.PreviewKeyUp");
Expand Down
6 changes: 3 additions & 3 deletions src/Uno.UI/UI/Xaml/Controls/Control/Control.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,7 @@ protected virtual void OnDragEnter(global::Microsoft.UI.Xaml.DragEventArgs e) {
protected virtual void OnDragOver(global::Microsoft.UI.Xaml.DragEventArgs e) { }
protected virtual void OnDragLeave(global::Microsoft.UI.Xaml.DragEventArgs e) { }
protected virtual void OnDrop(global::Microsoft.UI.Xaml.DragEventArgs e) { }
#if __WASM__
#if __WASM__ || __SKIA__
protected virtual void OnPreviewKeyDown(KeyRoutedEventArgs e) { }
protected virtual void OnPreviewKeyUp(KeyRoutedEventArgs e) { }
#endif
Expand Down Expand Up @@ -1081,7 +1081,7 @@ protected virtual void OnLostFocus(RoutedEventArgs e) { }

private static readonly DragEventHandler OnDropHandler =
(object sender, global::Microsoft.UI.Xaml.DragEventArgs args) => ((Control)sender).OnDrop(args);
#if __WASM__
#if __WASM__ || __SKIA__
private static readonly KeyEventHandler OnPreviewKeyDownHandler =
(object sender, KeyRoutedEventArgs args) => ((Control)sender).OnPreviewKeyDown(args);

Expand Down Expand Up @@ -1223,7 +1223,7 @@ internal static RoutedEventFlag EvaluateImplementedControlRoutedEvents(Type type
{
result |= RoutedEventFlag.Drop;
}
#if __WASM__
#if __WASM__ || __SKIA__
if (GetIsEventOverrideImplemented(type, nameof(OnPreviewKeyDown), _keyArgsType))
{
result |= RoutedEventFlag.PreviewKeyDown;
Expand Down
51 changes: 12 additions & 39 deletions src/Uno.UI/UI/Xaml/Internal/InputManager.Keyboard.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,60 +44,33 @@ public void Init(object host)
CoreWindow.GetForCurrentThreadSafe()?.SetKeyboardInputSource(_source);
}

_source.KeyDown += (s, e) => InitiateKeyDownBubblingFlow(e);
_source.KeyUp += (s, e) => InitiateKeyUpBubblingFlow(e);
_source.KeyDown += (_, e) => OnKey(e, true);
_source.KeyUp += (_, e) => OnKey(e, false);
}

private void InitiateKeyDownBubblingFlow(KeyEventArgs args)
private void OnKey(KeyEventArgs args, bool down)
{
var originalSource = FocusManager.GetFocusedElement(_inputManager.ContentRoot.XamlRoot) as UIElement ?? _inputManager.ContentRoot.VisualTree.RootElement;

if (originalSource is null)
{
return;
}

var krea = new KeyRoutedEventArgs(originalSource, args.VirtualKey, args.KeyboardModifiers, args.KeyStatus, args.UnicodeKey)
var args1 = new KeyRoutedEventArgs(originalSource, args.VirtualKey, args.KeyboardModifiers, args.KeyStatus, args.UnicodeKey)
{
CanBubbleNatively = false
};
originalSource.RaiseEvent(UIElement.KeyDownEvent, krea);
args.Handled = krea.Handled;

if (this.Log().IsEnabled(LogLevel.Trace))
var args2 = new KeyRoutedEventArgs(originalSource, args.VirtualKey, args.KeyboardModifiers, args.KeyStatus, args.UnicodeKey)
{
this.Log().Trace(
$"CoreWindow_KeyDown(vk: {args.VirtualKey}, " +
$"IsExtendedKey: {args.KeyStatus.IsExtendedKey}, " +
$"IsKeyReleased: {args.KeyStatus.IsKeyReleased}, " +
$"IsMenuKeyDown: {args.KeyStatus.IsMenuKeyDown}, " +
$"RepeatCount: {args.KeyStatus.RepeatCount}, " +
$"ScanCode: {args.KeyStatus.ScanCode})"
);
}
}
CanBubbleNatively = false
};

private void InitiateKeyUpBubblingFlow(KeyEventArgs args)
{
var originalSource = FocusManager.GetFocusedElement(_inputManager.ContentRoot.XamlRoot) as UIElement ?? _inputManager.ContentRoot.VisualTree.RootElement;
originalSource.RaiseTunnelingEvent(down ? UIElement.PreviewKeyDownEvent : UIElement.PreviewKeyUpEvent, args1);
args2.Handled = args1.Handled; // WinUI doesn't reuse the same args object, but copies the Handled value
originalSource.RaiseEvent(down ? UIElement.KeyDownEvent : UIElement.KeyUpEvent, args2);

if (originalSource is null)
{
return;
}

originalSource.RaiseEvent(
UIElement.KeyUpEvent,
new KeyRoutedEventArgs(originalSource, args.VirtualKey, args.KeyboardModifiers, args.KeyStatus, args.UnicodeKey)
{
CanBubbleNatively = false
}
);
args.Handled = args2.Handled;

if (this.Log().IsEnabled(LogLevel.Trace))
{
this.Log().Trace(
$"CoreWindow_KeyUp(vk: {args.VirtualKey}, " +
$"CoreWindow_KeyDown(vk: {args.VirtualKey}, " +
$"IsExtendedKey: {args.KeyStatus.IsExtendedKey}, " +
$"IsKeyReleased: {args.KeyStatus.IsKeyReleased}, " +
$"IsMenuKeyDown: {args.KeyStatus.IsMenuKeyDown}, " +
Expand Down
62 changes: 52 additions & 10 deletions src/Uno.UI/UI/Xaml/UIElement.RoutedEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ partial class UIElement

/* ** */
internal /* ** */ static RoutedEvent DropCompletedEvent { get; } = new RoutedEvent(RoutedEventFlag.DropCompleted);
#if __WASM__

#if __WASM__ || __SKIA__
public static RoutedEvent PreviewKeyDownEvent { get; } = new RoutedEvent(RoutedEventFlag.PreviewKeyDown);

public static RoutedEvent PreviewKeyUpEvent { get; } = new RoutedEvent(RoutedEventFlag.PreviewKeyUp);
Expand Down Expand Up @@ -440,7 +441,7 @@ public event TypedEventHandler<UIElement, DropCompletedEventArgs> DropCompleted
remove => RemoveHandler(DropCompletedEvent, value);
}

#if __WASM__
#if __WASM__ || __SKIA__
public event KeyEventHandler PreviewKeyDown
{
add => AddHandler(PreviewKeyDownEvent, value, false);
Expand Down Expand Up @@ -674,6 +675,13 @@ internal bool RaiseEvent(RoutedEvent routedEvent, RoutedEventArgs args, Bubbling
throw new InvalidOperationException($"Flag not defined for routed event {routedEvent.Name}.");
}

#if !__WASM__
if (routedEvent.IsTunnelingEvent)
{
throw new InvalidOperationException($"Tunneling event {routedEvent.Name} should be raised through {nameof(RaiseTunnelingEvent)}");
}
#endif

// TODO: This is just temporary workaround before proper
// keyboard event infrastructure is implemented everywhere
// (issue #6074)
Expand Down Expand Up @@ -763,23 +771,57 @@ internal bool RaiseEvent(RoutedEvent routedEvent, RoutedEventArgs args, Bubbling
return RaiseOnParent(routedEvent, args, parent, ctx);
}

private static void TrackKeyState(RoutedEvent routedEvent, RoutedEventArgs args)
/// <summary>
/// Raise a tunneling routed event starting from the root and tunneling down to this element.
/// </summary>
internal void RaiseTunnelingEvent(RoutedEvent routedEvent, RoutedEventArgs args)
{
if (args is KeyRoutedEventArgs keyArgs)
if (!routedEvent.Flag.IsTunnelingEvent())
{
throw new InvalidOperationException($"Event {routedEvent.Name} is not marked as a tunneling event.");
}

// TODO: This is just temporary workaround before proper
// keyboard event infrastructure is implemented everywhere
// (issue #6074)
// The key states will be tracked again in an accompanying bubbling event (e.g. KeyDown for PreviewKeyDown),
// but this is fine, since it's idempotent.
if (routedEvent.IsKeyEvent)
{
if (routedEvent == KeyDownEvent)
TrackKeyState(routedEvent, args);
}

// On WinUI, if one of the event handlers reparents this element, the tunneling still goes through the
// original path.
foreach (var p in this.GetAllParents().Reverse())
{
if (p is not UIElement parent)
{
KeyboardStateTracker.OnKeyDown(keyArgs.OriginalKey);
break;
}
else if (routedEvent == KeyUpEvent)

if (parent._eventHandlerStore.TryGetValue(routedEvent, out var handlers))
{
KeyboardStateTracker.OnKeyUp(keyArgs.OriginalKey);
foreach (var handler in handlers.ToArray())
{
if (IsHandled(args) || handler.HandledEventsToo)
{
parent.InvokeHandler(handler.Handler, args);
}
}
}
else if (routedEvent == PreviewKeyDownEvent)
}
}

private static void TrackKeyState(RoutedEvent routedEvent, RoutedEventArgs args)
{
if (args is KeyRoutedEventArgs keyArgs)
{
if (routedEvent == KeyDownEvent || routedEvent == PreviewKeyDownEvent)
{
KeyboardStateTracker.OnKeyDown(keyArgs.OriginalKey);
}
else if (routedEvent == PreviewKeyUpEvent)
else if (routedEvent == KeyUpEvent || routedEvent == PreviewKeyUpEvent)
{
KeyboardStateTracker.OnKeyUp(keyArgs.OriginalKey);
}
Expand Down

0 comments on commit 5c34158

Please sign in to comment.