diff --git a/src/app/dev/DevToys.Blazor/Components/ContextMenu/ContextMenu.razor.cs b/src/app/dev/DevToys.Blazor/Components/ContextMenu/ContextMenu.razor.cs index d0f3a9676f..1cb2c4c3e4 100644 --- a/src/app/dev/DevToys.Blazor/Components/ContextMenu/ContextMenu.razor.cs +++ b/src/app/dev/DevToys.Blazor/Components/ContextMenu/ContextMenu.razor.cs @@ -4,7 +4,6 @@ namespace DevToys.Blazor.Components; -// TODO: Close the context menu when the window lose focus or get resized. public partial class ContextMenu : StyledComponentBase { private bool _isOpen; diff --git a/src/app/dev/DevToys.Blazor/Core/Services/ContextMenuService.cs b/src/app/dev/DevToys.Blazor/Core/Services/ContextMenuService.cs index aa2eb9552b..c375392891 100644 --- a/src/app/dev/DevToys.Blazor/Core/Services/ContextMenuService.cs +++ b/src/app/dev/DevToys.Blazor/Core/Services/ContextMenuService.cs @@ -3,6 +3,18 @@ public sealed class ContextMenuService { private readonly object _lock = new(); + private readonly IWindowService _windowService; + + public ContextMenuService(IWindowService windowService) + { + Guard.IsNotNull(windowService); + _windowService = windowService; + + _windowService.WindowLostFocus += WindowService_MajorWindowChange; + _windowService.WindowClosing += WindowService_MajorWindowChange; + _windowService.WindowLocationChanged += WindowService_MajorWindowChange; + _windowService.WindowSizeChanged += WindowService_MajorWindowChange; + } internal bool IsContextMenuOpened { get; private set; } @@ -39,4 +51,9 @@ internal void OnCloseContextMenuRequested() { CloseContextMenuRequested?.Invoke(this, EventArgs.Empty); } + + private void WindowService_MajorWindowChange(object? sender, EventArgs e) + { + OnCloseContextMenuRequested(); + } } diff --git a/src/app/dev/DevToys.Blazor/Core/Services/IWindowService.cs b/src/app/dev/DevToys.Blazor/Core/Services/IWindowService.cs new file mode 100644 index 0000000000..f11af2823d --- /dev/null +++ b/src/app/dev/DevToys.Blazor/Core/Services/IWindowService.cs @@ -0,0 +1,12 @@ +namespace DevToys.Blazor.Core.Services; + +public interface IWindowService +{ + event EventHandler? WindowLostFocus; + + event EventHandler? WindowLocationChanged; + + event EventHandler? WindowSizeChanged; + + event EventHandler? WindowClosing; +} diff --git a/src/app/dev/platforms/desktop/DevToys.MacOS/App.xaml.cs b/src/app/dev/platforms/desktop/DevToys.MacOS/App.xaml.cs index 38a1fb1bbb..a185a021c9 100644 --- a/src/app/dev/platforms/desktop/DevToys.MacOS/App.xaml.cs +++ b/src/app/dev/platforms/desktop/DevToys.MacOS/App.xaml.cs @@ -8,4 +8,13 @@ public App() MainPage = new MainPage(); } + + protected override Window CreateWindow(IActivationState? activationState) + { + Window window = base.CreateWindow(activationState); + window.Width = 800; + window.Height = 600; + + return window; + } } diff --git a/src/app/dev/platforms/desktop/DevToys.MacOS/Core/WindowService.cs b/src/app/dev/platforms/desktop/DevToys.MacOS/Core/WindowService.cs new file mode 100644 index 0000000000..b71efd9f3c --- /dev/null +++ b/src/app/dev/platforms/desktop/DevToys.MacOS/Core/WindowService.cs @@ -0,0 +1,40 @@ +using DevToys.Blazor.Core.Services; +using Foundation; + +namespace DevToys.MacOS.Core; + +internal sealed class WindowService : IWindowService +{ + public event EventHandler? WindowLostFocus; + public event EventHandler? WindowLocationChanged; + public event EventHandler? WindowSizeChanged; + public event EventHandler? WindowClosing; + + public WindowService() + { + NSNotificationCenter.DefaultCenter.AddObserver(new NSString("NSWindowDidResignMainNotification"), OnWindowLostFocus); + NSNotificationCenter.DefaultCenter.AddObserver(new NSString("NSWindowWillMoveNotification"), OnWindowLocationChanged); + NSNotificationCenter.DefaultCenter.AddObserver(new NSString("NSWindowWillStartLiveResizeNotification"), OnWindowSizeChanged); + NSNotificationCenter.DefaultCenter.AddObserver(new NSString("NSWindowWillCloseNotification"), OnWindowClosing); + } + + public void OnWindowLostFocus(NSNotification notification) + { + WindowLostFocus?.Invoke(this, EventArgs.Empty); + } + + public void OnWindowLocationChanged(NSNotification notification) + { + WindowLocationChanged?.Invoke(this, EventArgs.Empty); + } + + public void OnWindowSizeChanged(NSNotification notification) + { + WindowSizeChanged?.Invoke(this, EventArgs.Empty); + } + + public void OnWindowClosing(NSNotification notification) + { + WindowClosing?.Invoke(this, EventArgs.Empty); + } +} diff --git a/src/app/dev/platforms/desktop/DevToys.MacOS/MauiProgram.cs b/src/app/dev/platforms/desktop/DevToys.MacOS/MauiProgram.cs index d67632ce92..6ddac3f048 100644 --- a/src/app/dev/platforms/desktop/DevToys.MacOS/MauiProgram.cs +++ b/src/app/dev/platforms/desktop/DevToys.MacOS/MauiProgram.cs @@ -1,11 +1,10 @@ using DevToys.Api; using DevToys.Blazor.Core.Languages; -using DevToys.Blazor.Services; +using DevToys.Blazor.Core.Services; using DevToys.Business.ViewModels; using DevToys.Core.Logging; using DevToys.Core.Mef; using DevToys.MacOS.Core; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Uno.Extensions; using PredefinedSettings = DevToys.Core.Settings.PredefinedSettings; @@ -97,8 +96,9 @@ private ServiceProvider InitializeServices(IServiceCollection serviceCollection) }); serviceCollection.AddSingleton(provider => MefComposer!.Provider); - serviceCollection.TryAddScoped(); - serviceCollection.TryAddScoped(); + serviceCollection.AddSingleton(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); diff --git a/src/app/dev/platforms/desktop/DevToys.Windows/Core/WindowService.cs b/src/app/dev/platforms/desktop/DevToys.Windows/Core/WindowService.cs new file mode 100644 index 0000000000..39a4cd9c6d --- /dev/null +++ b/src/app/dev/platforms/desktop/DevToys.Windows/Core/WindowService.cs @@ -0,0 +1,46 @@ +using System.Windows; +using DevToys.Blazor.Core.Services; + +namespace DevToys.Windows.Core; + +internal sealed class WindowService : IWindowService +{ + private Window? _window; + + public event EventHandler? WindowLostFocus; + public event EventHandler? WindowLocationChanged; + public event EventHandler? WindowSizeChanged; + public event EventHandler? WindowClosing; + + internal void SetWindow(Window window) + { + Guard.IsNull(_window); + Guard.IsNotNull(window); + _window = window; + + _window.LostFocus += Window_LostFocus; + _window.LocationChanged += Window_LocationChanged; + _window.SizeChanged += Window_SizeChanged; + _window.Closing += Window_Closing; + } + + private void Window_LostFocus(object sender, RoutedEventArgs e) + { + WindowLostFocus?.Invoke(this, EventArgs.Empty); + } + + private void Window_LocationChanged(object? sender, EventArgs e) + { + WindowLocationChanged?.Invoke(this, EventArgs.Empty); + } + + private void Window_SizeChanged(object sender, SizeChangedEventArgs e) + { + WindowSizeChanged?.Invoke(this, EventArgs.Empty); + } + + private void Window_Closing(object? sender, CancelEventArgs e) + { + WindowClosing?.Invoke(this, EventArgs.Empty); + } +} diff --git a/src/app/dev/platforms/desktop/DevToys.Windows/MainWindow.xaml b/src/app/dev/platforms/desktop/DevToys.Windows/MainWindow.xaml index 8250991e1c..f26d543a55 100644 --- a/src/app/dev/platforms/desktop/DevToys.Windows/MainWindow.xaml +++ b/src/app/dev/platforms/desktop/DevToys.Windows/MainWindow.xaml @@ -8,6 +8,7 @@ xmlns:controls="clr-namespace:DevToys.Windows.Controls" xmlns:blazor="clr-namespace:Microsoft.AspNetCore.Components.WebView.Wpf;assembly=Microsoft.AspNetCore.Components.WebView.Wpf" mc:Ignorable="d" + Loaded="MainWindow_Loaded" Title="MainWindow" Height="450" Width="800" diff --git a/src/app/dev/platforms/desktop/DevToys.Windows/MainWindow.xaml.cs b/src/app/dev/platforms/desktop/DevToys.Windows/MainWindow.xaml.cs index cfe9153021..cfef667b63 100644 --- a/src/app/dev/platforms/desktop/DevToys.Windows/MainWindow.xaml.cs +++ b/src/app/dev/platforms/desktop/DevToys.Windows/MainWindow.xaml.cs @@ -1,4 +1,5 @@ -using DevToys.Api; +using System; +using DevToys.Api; using DevToys.Blazor.Core.Languages; using DevToys.Blazor.Core.Services; using DevToys.Business.ViewModels; @@ -9,7 +10,6 @@ using DevToys.Windows.Core; using Microsoft.AspNetCore.Components.WebView; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using PredefinedSettings = DevToys.Core.Settings.PredefinedSettings; @@ -23,6 +23,7 @@ public partial class MainWindow : MicaWindowWithOverlay private static MainWindow? mainWindowInstance; private readonly MefComposer _mefComposer; + private readonly ServiceProvider _serviceProvider; private readonly DateTime _uiLoadingTime; private ILogger? _logger; @@ -33,7 +34,7 @@ public MainWindow() DateTime startTime = DateTime.Now; // Initialize services and logging. - ServiceProvider serviceProvider = InitializeServices(); + _serviceProvider = InitializeServices(); // Listen for unhandled exceptions. AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; @@ -58,7 +59,7 @@ LanguageDefinition languageDefinition LanguageManager.Instance.SetCurrentCulture(languageDefinition); // Load the UI. - Resources.Add("services", serviceProvider); + Resources.Add("services", _serviceProvider); InitializeComponent(); _themeListener = _mefComposer.Provider.Import(); @@ -68,6 +69,12 @@ LanguageDefinition languageDefinition blazorWebView.BlazorWebViewInitialized += BlazorWebView_BlazorWebViewInitialized; } + private void MainWindow_Loaded(object sender, System.Windows.RoutedEventArgs e) + { + var windowService = (WindowService)_serviceProvider.GetService()!; + windowService.SetWindow(this); + } + private void BlazorWebView_BlazorWebViewInitializing(object? sender, BlazorWebViewInitializingEventArgs e) { // Set the web view transparent. @@ -113,8 +120,9 @@ private ServiceProvider InitializeServices() }); serviceCollection.AddSingleton(provider => _mefComposer.Provider); - serviceCollection.TryAddScoped(); - serviceCollection.TryAddScoped(); + serviceCollection.AddSingleton(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();