diff --git a/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/KeyboardService.cs b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/KeyboardService.cs index a6e38287a5..94971195f9 100644 --- a/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/KeyboardService.cs +++ b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/KeyboardService.cs @@ -42,9 +42,9 @@ public partial class KeyboardService : IDisposable _settingsService.SettingsChanged += SettingsChanged; } - public void SetProcessCommand(HWND hwnd, ProcessCommand processCommand) + public void SetProcessCommand(IntPtr hwnd, ProcessCommand processCommand) { - _hwnd = hwnd; + _hwnd = new HWND(hwnd); _keyboardListener.SetProcessCommand(processCommand); SetupHotkeys(); } diff --git a/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/NavigationManager.cs b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/NavigationManager.cs new file mode 100644 index 0000000000..51fac9ac44 --- /dev/null +++ b/src/modules/Deux/UI/Microsoft.CommandPalette.UI.Services/NavigationManager.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CommandPalette.UI.Services; + +internal class NavigationManager +{ +} diff --git a/src/modules/Deux/UI/Microsoft.CommandPalette.UI/Pages/ShellPage.xaml.cs b/src/modules/Deux/UI/Microsoft.CommandPalette.UI/Pages/ShellPage.xaml.cs index 65c5d3b0ab..ef4c193401 100644 --- a/src/modules/Deux/UI/Microsoft.CommandPalette.UI/Pages/ShellPage.xaml.cs +++ b/src/modules/Deux/UI/Microsoft.CommandPalette.UI/Pages/ShellPage.xaml.cs @@ -11,6 +11,7 @@ using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.UI.Models.Events; using Microsoft.CommandPalette.UI.Models.Messages; using Microsoft.CommandPalette.UI.Services; +using Microsoft.CommandPalette.UI.Services.Helpers; using Microsoft.CommandPalette.UI.ViewModels; using Microsoft.CommandPalette.ViewModels; using Microsoft.Extensions.Logging; @@ -160,6 +161,71 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page, }); } + public void Receive(GoBackMessage message) + { + _ = DispatcherQueue.TryEnqueue(() => GoBack(message.WithAnimation, message.FocusSearch)); + } + + private void GoBack(bool withAnimation = true, bool focusSearch = true) + { + HideDetails(); + + viewModel.CancelNavigation(); + + // Note: That we restore the VM state below in RootFrame_Navigated call back after this occurs. + // In the future, we may want to manage the back stack ourselves vs. relying on Frame + // We could replace Frame with a ContentPresenter, but then have to manage transition animations ourselves. + // However, then we have more fine-grained control on the back stack, managing the VM cache, and not + // having that all be a black box, though then we wouldn't cache the XAML page itself, but sometimes that is a drawback. + // However, we do a good job here, see ForwardStack.Clear below, and BackStack.Clear above about managing that. + if (withAnimation) + { + RootFrame.GoBack(); + } + else + { + RootFrame.GoBack(_noAnimation); + } + + // Don't store pages we're navigating away from in the Frame cache + // TODO: In the future we probably want a short cache (3-5?) of recent VMs in case the user re-navigates + // back to a recent page they visited (like the Pokedex) so we don't have to reload it from scratch. + // That'd be retrieved as we re-navigate in the PerformCommandMessage logic above + RootFrame.ForwardStack.Clear(); + + if (!RootFrame.CanGoBack) + { + viewModel.GoHome(); + } + + if (focusSearch) + { + SearchBox.Focus(Microsoft.UI.Xaml.FocusState.Programmatic); + SearchBox.SelectSearch(); + } + } + + public void Receive(GoHomeMessage message) + { + _ = DispatcherQueue.TryEnqueue(() => GoHome(withAnimation: message.WithAnimation, focusSearch: message.FocusSearch)); + } + + private void GoHome(bool withAnimation = true, bool focusSearch = true) + { + while (RootFrame.CanGoBack) + { + // don't focus on each step, just at the end + GoBack(withAnimation, focusSearch: false); + } + + // focus search box, even if we were already home + if (focusSearch) + { + SearchBox.Focus(Microsoft.UI.Xaml.FocusState.Programmatic); + SearchBox.SelectSearch(); + } + } + public void Receive(ShowToastMessage message) { DispatcherQueue.TryEnqueue(() => @@ -242,21 +308,21 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page, public void Receive(ShowDetailsMessage message) { - if (ViewModel is not null && - ViewModel.CurrentPage is not null) + if (viewModel is not null && + viewModel.CurrentPage is not null) { - if (ViewModel.CurrentPage.PageContext.TryGetTarget(out var pageContext)) + if (viewModel.CurrentPage.PageContext.TryGetTarget(out var pageContext)) { Task.Factory.StartNew( () => { // TERRIBLE HACK TODO GH #245 // There's weird wacky bugs with debounce currently. - if (!ViewModel.IsDetailsVisible) + if (!viewModel.IsDetailsVisible) { - ViewModel.Details = message.Details; + viewModel.Details = message.Details; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HasHeroImage))); - ViewModel.IsDetailsVisible = true; + viewModel.IsDetailsVisible = true; return; } @@ -267,15 +333,15 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page, _debounceTimer.Debounce( () => { - ViewModel.Details = message.Details; + viewModel.Details = message.Details; // Trigger a re-evaluation of whether we have a hero image based on // the current theme PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HasHeroImage))); }, interval: TimeSpan.FromMilliseconds(50), - immediate: ViewModel.IsDetailsVisible == false); - ViewModel.IsDetailsVisible = true; + immediate: viewModel.IsDetailsVisible == false); + viewModel.IsDetailsVisible = true; }, CancellationToken.None, TaskCreationOptions.None, @@ -290,8 +356,8 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page, private void HideDetails() { - ViewModel.Details = null; - ViewModel.IsDetailsVisible = false; + viewModel.Details = null; + viewModel.IsDetailsVisible = false; } public void Receive(ClearSearchMessage message) => SearchBox.ClearSearch();