From 542a226cff1c42e24fcd18fc8de9a8840bc58c03 Mon Sep 17 00:00:00 2001 From: Ramez Ragaa Date: Tue, 5 Nov 2024 19:08:24 +0200 Subject: [PATCH] fix(wasm): key tracking when no one is subscribing to Key --- src/Uno.UI/ts/WindowManager.ts | 17 ++++++++++++++++ .../UI/Core/Internal/KeyboardStateTracker.cs | 20 ++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/Uno.UI/ts/WindowManager.ts b/src/Uno.UI/ts/WindowManager.ts index 74d1303ff7d4..ebf2ed9409a1 100644 --- a/src/Uno.UI/ts/WindowManager.ts +++ b/src/Uno.UI/ts/WindowManager.ts @@ -133,6 +133,7 @@ namespace Uno.UI { private static dispatchSuspendingMethod: any; private static getDependencyPropertyValueMethod: any; private static setDependencyPropertyValueMethod: any; + private static keyTrackingMethod: any; private constructor(private containerElementId: string, private loadingElementId: string) { this.initDom(); @@ -1437,6 +1438,7 @@ namespace Uno.UI { WindowManager.dispatchEventMethod = exports.Microsoft.UI.Xaml.UIElement.DispatchEvent; WindowManager.focusInMethod = exports.Microsoft.UI.Xaml.Input.FocusManager.ReceiveFocusNative; WindowManager.dispatchSuspendingMethod = exports.Microsoft.UI.Xaml.Application.DispatchSuspending; + WindowManager.keyTrackingMethod = (globalThis).DotnetExports.Uno.Uno.UI.Core.KeyboardStateTracker.UpdateKeyStateNative; } else { throw `Unable to find dotnet exports`; } @@ -1451,6 +1453,13 @@ namespace Uno.UI { document.body.addEventListener("focusin", this.onfocusin); document.body.appendChild(this.containerElement); + // On WASM, if no one subscribes to key, not only will the event not fire on any UIElement, + // but the browser won't even notify us that a key was pressed/released, and this breaks KeyboardStateTracker + // key tracking, which depends on RaiseEvent being called even if no one is subscribing. Instead, we + // subscribe on the body and make sure to call KeyboardStateTracker ourselves here. + document.body.addEventListener("keydown", this.onBodyKeyDown); + document.body.addEventListener("keyup", this.onBodyKeyUp); + window.addEventListener("resize", x => WindowManager.resize()); window.addEventListener("contextmenu", x => { if (!(x.target instanceof HTMLInputElement) || @@ -1593,6 +1602,14 @@ namespace Uno.UI { public moveWindow(x: number, y: number) { window.moveTo(x, y); } + + private onBodyKeyDown(event: KeyboardEvent) { + WindowManager.keyTrackingMethod(event.key, true); + } + + private onBodyKeyUp(event: KeyboardEvent) { + WindowManager.keyTrackingMethod(event.key, false); + } } if (typeof define === "function") { diff --git a/src/Uno.UWP/UI/Core/Internal/KeyboardStateTracker.cs b/src/Uno.UWP/UI/Core/Internal/KeyboardStateTracker.cs index dcb72a3fc2f1..d6ed5e00d27f 100644 --- a/src/Uno.UWP/UI/Core/Internal/KeyboardStateTracker.cs +++ b/src/Uno.UWP/UI/Core/Internal/KeyboardStateTracker.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices.JavaScript; using Windows.System; using Windows.UI.Core; @@ -13,7 +14,7 @@ namespace Uno.UI.Core; /// In UWP/WinUI, every key has a locked state (not only Caps Lock, etc.). The sequence of states is as follows: /// (None) -> (Down) -> (None) -> (Down + Locked) -> (None + Locked) -> (Down) -> (None) -> etc. /// -internal static class KeyboardStateTracker +internal static partial class KeyboardStateTracker { private static readonly Dictionary _keyStates = new Dictionary(); @@ -106,4 +107,21 @@ internal static void Reset() _keyStates.Clear(); } } + +#if __WASM__ +#pragma warning disable IDE0051 // Remove unused private members + [JSExport] + private static void UpdateKeyStateNative(string key, bool down) +#pragma warning restore IDE0051 // Remove unused private members + { + if (down) + { + OnKeyDown(VirtualKeyHelper.FromKey(key)); + } + else + { + OnKeyUp(VirtualKeyHelper.FromKey(key)); + } + } +#endif }