From ac206db0980fd3483b3dc3c5db571ec512f965ca Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 16 Jan 2023 14:14:53 +0000 Subject: [PATCH] add browser support --- .../Platform/PlatformHotkeyConfiguration.cs | 5 ++++ src/Avalonia.Controls/TopLevel.cs | 20 ++++++++++------ .../BrowserSystemNavigationManager.cs | 24 +++++++++++++++++++ .../Avalonia.Browser/BrowserTopLevelImpl.cs | 5 +++- .../Interop/NavigationHelper.cs | 10 ++++++++ .../webapp/modules/avalonia.ts | 5 +++- .../modules/avalonia/navigationHelper.ts | 14 +++++++++++ 7 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 src/Browser/Avalonia.Browser/BrowserSystemNavigationManager.cs create mode 100644 src/Browser/Avalonia.Browser/Interop/NavigationHelper.cs create mode 100644 src/Browser/Avalonia.Browser/webapp/modules/avalonia/navigationHelper.ts diff --git a/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs index 1fc46dd396..b87ca5eded 100644 --- a/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs +++ b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs @@ -81,6 +81,10 @@ namespace Avalonia.Input.Platform { new KeyGesture(Key.Apps) }; + Back = new List + { + new KeyGesture(Key.Left, KeyModifiers.Alt) + }; } public KeyModifiers CommandModifiers { get; set; } @@ -101,5 +105,6 @@ namespace Avalonia.Input.Platform public List MoveCursorToTheStartOfDocumentWithSelection { get; set; } public List MoveCursorToTheEndOfDocumentWithSelection { get; set; } public List OpenContextMenu { get; set; } + public List Back { get; set; } } } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 66c108b436..c0265e28b9 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -18,6 +18,8 @@ using Avalonia.Rendering; using Avalonia.Styling; using Avalonia.Utilities; using Avalonia.VisualTree; +using Avalonia.Input.Platform; +using System.Linq; namespace Avalonia.Controls { @@ -223,20 +225,24 @@ namespace Avalonia.Controls }; } - var backKeyGesture = new KeyGesture(Key.Left, KeyModifiers.Alt); - _backGestureSubscription = _inputManager?.PreProcess.OfType().Subscribe(e => + _backGestureSubscription = _inputManager?.PreProcess.Subscribe(e => { bool backRequested = false; if (e is RawKeyEventArgs rawKeyEventArgs && rawKeyEventArgs.Type == RawKeyEventType.KeyDown) { - var keyEvent = new KeyEventArgs() + var keymap = AvaloniaLocator.Current.GetService()?.Back; + + if (keymap != null) { - KeyModifiers = (KeyModifiers)rawKeyEventArgs.Modifiers, - Key = rawKeyEventArgs.Key - }; + var keyEvent = new KeyEventArgs() + { + KeyModifiers = (KeyModifiers)rawKeyEventArgs.Modifiers, + Key = rawKeyEventArgs.Key + }; - backRequested = backKeyGesture.Matches(keyEvent); + backRequested = keymap.Any( key => key.Matches(keyEvent)); + } } else if(e is RawPointerEventArgs pointerEventArgs) { diff --git a/src/Browser/Avalonia.Browser/BrowserSystemNavigationManager.cs b/src/Browser/Avalonia.Browser/BrowserSystemNavigationManager.cs new file mode 100644 index 0000000000..cc7d8c3af1 --- /dev/null +++ b/src/Browser/Avalonia.Browser/BrowserSystemNavigationManager.cs @@ -0,0 +1,24 @@ +using System; +using Avalonia.Browser.Interop; +using Avalonia.Interactivity; +using Avalonia.Platform; + +namespace Avalonia.Browser +{ + internal class BrowserSystemNavigationManager : ISystemNavigationManager + { + public event EventHandler? BackRequested; + + public BrowserSystemNavigationManager() + { + NavigationHelper.AddBackHandler(() => + { + var routedEventArgs = new RoutedEventArgs(); + + BackRequested?.Invoke(this, routedEventArgs); + + return routedEventArgs.Handled; + }); + } + } +} diff --git a/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs index 4e6be3268b..90310fb0e1 100644 --- a/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs +++ b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs @@ -19,7 +19,8 @@ using Avalonia.Rendering.Composition; namespace Avalonia.Browser { - internal class BrowserTopLevelImpl : ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost, ITopLevelImplWithStorageProvider + internal class BrowserTopLevelImpl : ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost, ITopLevelImplWithStorageProvider, + ITopLevelWithSystemNavigationManager { private Size _clientSize; private IInputRoot? _inputRoot; @@ -234,5 +235,7 @@ namespace Avalonia.Browser public INativeControlHostImpl? NativeControlHost { get; } public IStorageProvider StorageProvider { get; } = new BrowserStorageProvider(); + + public ISystemNavigationManager SystemNavigationManager { get; } = new BrowserSystemNavigationManager(); } } diff --git a/src/Browser/Avalonia.Browser/Interop/NavigationHelper.cs b/src/Browser/Avalonia.Browser/Interop/NavigationHelper.cs new file mode 100644 index 0000000000..d49b83b5a0 --- /dev/null +++ b/src/Browser/Avalonia.Browser/Interop/NavigationHelper.cs @@ -0,0 +1,10 @@ +using System; +using System.Runtime.InteropServices.JavaScript; + +namespace Avalonia.Browser.Interop; + +internal static partial class NavigationHelper +{ + [JSImport("NavigationHelper.addBackHandler", AvaloniaModule.MainModuleName)] + public static partial void AddBackHandler([JSMarshalAs>] Func backHandlerCallback); +} diff --git a/src/Browser/Avalonia.Browser/webapp/modules/avalonia.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia.ts index 0642bd475d..ab0c85eaa2 100644 --- a/src/Browser/Avalonia.Browser/webapp/modules/avalonia.ts +++ b/src/Browser/Avalonia.Browser/webapp/modules/avalonia.ts @@ -5,6 +5,7 @@ import { AvaloniaDOM } from "./avalonia/dom"; import { Caniuse } from "./avalonia/caniuse"; import { StreamHelper } from "./avalonia/stream"; import { NativeControlHost } from "./avalonia/nativeControlHost"; +import { NavigationHelper } from "./avalonia/navigationHelper"; async function registerAvaloniaModule(api: RuntimeAPI): Promise { api.setModuleImports("avalonia", { @@ -15,7 +16,8 @@ async function registerAvaloniaModule(api: RuntimeAPI): Promise { DpiWatcher, AvaloniaDOM, StreamHelper, - NativeControlHost + NativeControlHost, + NavigationHelper }); } export { @@ -27,6 +29,7 @@ export { AvaloniaDOM, StreamHelper, NativeControlHost, + NavigationHelper, registerAvaloniaModule }; diff --git a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/navigationHelper.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/navigationHelper.ts new file mode 100644 index 0000000000..ee594fe85c --- /dev/null +++ b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/navigationHelper.ts @@ -0,0 +1,14 @@ +export class NavigationHelper { + public static addBackHandler(backHandlerCallback: () => Boolean) { + history.pushState(null, "", window.location.href); + window.onpopstate = () => { + const handled = backHandlerCallback(); + + if (!handled) { + history.back(); + } else { + history.forward(); + } + }; + } +}