Skip to content

Commit

Permalink
feat(WASM): implement the PreviewKeyUp/Down in WebAssembly
Browse files Browse the repository at this point in the history
  • Loading branch information
TopProgrammer77 committed Jul 4, 2023
1 parent 2f5776b commit 27553dd
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 39 deletions.
24 changes: 12 additions & 12 deletions src/Uno.UI/Generated/3.0.0.0/Windows.UI.Xaml/UIElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -678,8 +678,8 @@ public bool CanBeScrollAnchor
}
}
#endif
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
public static global::Windows.UI.Xaml.RoutedEvent PreviewKeyDownEvent
{
get
Expand All @@ -688,8 +688,8 @@ public bool CanBeScrollAnchor
}
}
#endif
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
public static global::Windows.UI.Xaml.RoutedEvent PreviewKeyUpEvent
{
get
Expand Down Expand Up @@ -1246,32 +1246,32 @@ public static bool TryStartDirectManipulation( global::Windows.UI.Xaml.Input.Poi
}
}
#endif
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
public event global::Windows.UI.Xaml.Input.KeyEventHandler PreviewKeyDown
{
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
add
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Windows.UI.Xaml.UIElement", "event KeyEventHandler UIElement.PreviewKeyDown");
}
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
remove
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Windows.UI.Xaml.UIElement", "event KeyEventHandler UIElement.PreviewKeyDown");
}
}
#endif
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
public event global::Windows.UI.Xaml.Input.KeyEventHandler PreviewKeyUp
{
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
add
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Windows.UI.Xaml.UIElement", "event KeyEventHandler UIElement.PreviewKeyUp");
}
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
remove
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Windows.UI.Xaml.UIElement", "event KeyEventHandler UIElement.PreviewKeyUp");
Expand Down
8 changes: 5 additions & 3 deletions src/Uno.UI/UI/Xaml/RoutedEventFlag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ public enum RoutedEventFlag : ulong
PointerWheelChanged = 1UL << 7,

// Keyboard
// PreviewKeyDown = 1UL << 12 => Reserved for future usage
PreviewKeyDown = 1UL << 12,
KeyDown = 1UL << 13,
// PreviewKeyUp = 1 >> 14, => Reserved for future usage
PreviewKeyUp = 1UL << 14,
KeyUp = 1UL << 15,
// CharacterReceived = 1UL << 16,
// ProcessKeyboardAccelerators = 1UL << 17, => Reserved for future use (even if it is not an actual standard RoutedEvent)
Expand Down Expand Up @@ -77,7 +77,9 @@ internal static class RoutedEventFlagExtensions
| RoutedEventFlag.PointerWheelChanged;

private const RoutedEventFlag _isKey = // 0b0000_0000_0000_0000___0000_0000_0000_0000___0000_0000_0001_1111___1111_0000_0000_0000
RoutedEventFlag.KeyDown
RoutedEventFlag.PreviewKeyDown
| RoutedEventFlag.PreviewKeyUp
| RoutedEventFlag.KeyDown
| RoutedEventFlag.KeyUp;

private const RoutedEventFlag _isFocus = // 0b0000_0000_0000_0000___0000_0000_0000_0000___0111_1111_0000_0000___0000_0000_0000_0000
Expand Down
35 changes: 24 additions & 11 deletions src/Uno.UI/UI/Xaml/UIElement.EventRegistration.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,9 @@ internal void RegisterEventHandler(
this.Log().Debug($"Registering {eventName} on {this}.");
}

if (!_eventHandlers.TryGetValue(eventName, out var registration))
if (!_eventHandlers.TryGetValue((eventName, onCapturePhase), out var registration))
{
_eventHandlers[eventName] = registration = new EventRegistration(
_eventHandlers[(eventName, onCapturePhase)] = registration = new EventRegistration(
this,
eventName,
onCapturePhase,
Expand All @@ -216,9 +216,9 @@ internal void RegisterEventHandler(
registration.Add(handler, invoker);
}

internal void UnregisterEventHandler(string eventName, Delegate handler, GenericEventHandler invoker)
internal void UnregisterEventHandler(string eventName, Delegate handler, GenericEventHandler invoker, bool onCapturePhase = false)
{
if (_eventHandlers.TryGetValue(eventName, out var registration))
if (_eventHandlers.TryGetValue((eventName, onCapturePhase), out var registration))
{
registration.Remove(handler, invoker);
}
Expand All @@ -228,12 +228,12 @@ internal void UnregisterEventHandler(string eventName, Delegate handler, Generic
}
}

internal HtmlEventDispatchResult InternalDispatchEvent(string eventName, EventArgs eventArgs = null, string nativeEventPayload = null)
internal HtmlEventDispatchResult InternalDispatchEvent(string eventName, EventArgs eventArgs = null, string nativeEventPayload = null, bool onCapturePhase = false)
{
var n = eventName;
try
{
return InternalInnerDispatchEvent(eventArgs, nativeEventPayload, n);
return InternalInnerDispatchEvent(eventArgs, nativeEventPayload, n, onCapturePhase);
}
catch (Exception e)
{
Expand All @@ -250,9 +250,9 @@ internal HtmlEventDispatchResult InternalDispatchEvent(string eventName, EventAr
/// See https://github.com/dotnet/runtime/issues/56309
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private HtmlEventDispatchResult InternalInnerDispatchEvent(EventArgs eventArgs, string nativeEventPayload, string n)
private HtmlEventDispatchResult InternalInnerDispatchEvent(EventArgs eventArgs, string nativeEventPayload, string n, bool onCapturePhase = false)
{
if (_eventHandlers.TryGetValue(n, out var registration))
if (_eventHandlers.TryGetValue((n, onCapturePhase), out var registration))
{
return registration.Dispatch(eventArgs, nativeEventPayload);
}
Expand All @@ -278,7 +278,7 @@ private HtmlEventDispatchResult InternalInnerDispatchEvent(EventArgs eventArgs,
#endif
[Preserve]
[EditorBrowsable(EditorBrowsableState.Never)]
public static int DispatchEvent(int handle, string eventName, string eventArgs)
public static int DispatchEvent(int handle, string eventName, string eventArgs, bool onCapturePhase)
{
#if DEBUG
try
Expand All @@ -287,7 +287,7 @@ public static int DispatchEvent(int handle, string eventName, string eventArgs)
// Dispatch to right object, if we can find it
if (GetElementFromHandle(handle) is UIElement element)
{
return (int)element.InternalDispatchEvent(eventName, nativeEventPayload: eventArgs);
return (int)element.InternalDispatchEvent(eventName, nativeEventPayload: eventArgs, onCapturePhase: onCapturePhase);
}
else
{
Expand All @@ -305,7 +305,20 @@ public static int DispatchEvent(int handle, string eventName, string eventArgs)
#endif
}

private readonly Dictionary<string, EventRegistration> _eventHandlers = new Dictionary<string, EventRegistration>(StringComparer.OrdinalIgnoreCase);
public class TupleComparer : IEqualityComparer<(string, bool)>
{
public bool Equals((string, bool) x, (string, bool) y)
{
return StringComparer.OrdinalIgnoreCase.Equals(x.Item1, y.Item1) && x.Item2 == y.Item2;
}

public int GetHashCode((string, bool) obj)
{
return StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Item1) ^ obj.Item2.GetHashCode();
}
}

private readonly Dictionary<(string, bool), EventRegistration> _eventHandlers = new Dictionary<(string, bool), EventRegistration>(new TupleComparer());

internal delegate EventArgs EventArgsParser(object sender, string payload);

Expand Down
28 changes: 28 additions & 0 deletions src/Uno.UI/UI/Xaml/UIElement.RoutedEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,11 @@ partial class UIElement

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

public static RoutedEvent PreviewKeyUpEvent { get; } = new RoutedEvent(RoutedEventFlag.PreviewKeyUp);
#endif
public static RoutedEvent KeyDownEvent { get; } = new RoutedEvent(RoutedEventFlag.KeyDown);

public static RoutedEvent KeyUpEvent { get; } = new RoutedEvent(RoutedEventFlag.KeyUp);
Expand Down Expand Up @@ -434,6 +438,20 @@ public event TypedEventHandler<UIElement, DropCompletedEventArgs> DropCompleted
remove => RemoveHandler(DropCompletedEvent, value);
}

#if __WASM__
public event KeyEventHandler PreviewKeyDown
{
add => AddHandler(PreviewKeyDownEvent, value, false);
remove => RemoveHandler(PreviewKeyDownEvent, value);
}

public event KeyEventHandler PreviewKeyUp
{
add => AddHandler(PreviewKeyUpEvent, value, false);
remove => RemoveHandler(PreviewKeyUpEvent, value);
}
#endif

#if __MACOS__
public new event KeyEventHandler KeyDown
#else
Expand Down Expand Up @@ -748,6 +766,16 @@ private static void TrackKeyState(RoutedEvent routedEvent, RoutedEventArgs args)
{
KeyboardStateTracker.OnKeyUp(keyArgs.OriginalKey);
}
#if __WASM__
else if (routedEvent == PreviewKeyDownEvent)
{
KeyboardStateTracker.OnKeyDown(keyArgs.OriginalKey);
}
else if (routedEvent == PreviewKeyUpEvent)
{
KeyboardStateTracker.OnKeyUp(keyArgs.OriginalKey);
}
#endif
}
}

Expand Down
30 changes: 20 additions & 10 deletions src/Uno.UI/UI/Xaml/UIElement.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -616,22 +616,32 @@ partial void AddKeyHandler(RoutedEvent routedEvent, int handlersCount, object ha
_registeredRoutedEvents |= routedEvent.Flag;

string domEventName;
if (routedEvent.Flag == RoutedEventFlag.KeyDown)
{
domEventName = "keydown";
}
else
{
domEventName = routedEvent.Flag == RoutedEventFlag.KeyUp
? "keyup"
: throw new ArgumentOutOfRangeException(nameof(routedEvent), "Not a keyboard event");
bool onCapturePhase = false;
switch (routedEvent.Flag)
{
case RoutedEventFlag.PreviewKeyDown:
domEventName = "keydown";
onCapturePhase = true;
break;
case RoutedEventFlag.KeyDown:
domEventName = "keydown";
break;
case RoutedEventFlag.PreviewKeyUp:
domEventName = "keyup";
onCapturePhase = true;
break;
case RoutedEventFlag.KeyUp:
domEventName = "keyup";
break;
default:
throw new ArgumentOutOfRangeException(nameof(routedEvent), "Not a keyboard event");
}

RegisterEventHandler(
domEventName,
handler: new RoutedEventHandlerWithHandled((snd, args) => RaiseEvent(routedEvent, args)),
invoker: GenericEventHandlers.RaiseRoutedEventHandlerWithHandled,
onCapturePhase: false,
onCapturePhase,
eventExtractor: HtmlEventExtractor.KeyboardEventExtractor,
payloadConverter: PayloadToKeyArgs
);
Expand Down
6 changes: 3 additions & 3 deletions src/Uno.UI/ts/WindowManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@ namespace Uno.UI {
? `${eventExtractor(event)}`
: "";

const result = this.dispatchEvent(element, eventName, eventPayload);
const result = this.dispatchEvent(element, eventName, eventPayload, onCapturePhase);
if (result & HtmlEventDispatchResult.StopPropagation) {
event.stopPropagation();
}
Expand Down Expand Up @@ -1695,7 +1695,7 @@ namespace Uno.UI {
WindowManager.focusInMethod(-1);
}

private dispatchEvent(element: HTMLElement | SVGElement, eventName: string, eventPayload: string = null): HtmlEventDispatchResult {
private dispatchEvent(element: HTMLElement | SVGElement, eventName: string, eventPayload: string = null, onCapturePhase: boolean = false): HtmlEventDispatchResult {
const htmlId = Number(element.getAttribute("XamlHandle"));

// console.debug(`${element.getAttribute("id")}: Raising event ${eventName}.`);
Expand All @@ -1704,7 +1704,7 @@ namespace Uno.UI {
throw `No attribute XamlHandle on element ${element}. Can't raise event.`;
}

return WindowManager.dispatchEventMethod(htmlId, eventName, eventPayload || "");
return WindowManager.dispatchEventMethod(htmlId, eventName, eventPayload || "", onCapturePhase);
}

private getIsConnectedToRootElement(element: HTMLElement | SVGElement): boolean {
Expand Down

0 comments on commit 27553dd

Please sign in to comment.