diff --git a/src/Files.App/Data/Items/WindowEx.cs b/src/Files.App/Data/Items/WindowEx.cs deleted file mode 100644 index b19f3ba449f8..000000000000 --- a/src/Files.App/Data/Items/WindowEx.cs +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright (c) Files Community -// Licensed under the MIT License. - -using Microsoft.UI.Composition.SystemBackdrops; -using Microsoft.UI.Windowing; -using Microsoft.UI.Xaml; -using System.Runtime.InteropServices; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Windows.Storage; -using Windows.Win32; -using Windows.Win32.Foundation; -using Windows.Win32.Graphics.Gdi; -using Windows.Win32.UI.WindowsAndMessaging; - -namespace Files.App.Data.Items -{ - /// - /// Represents base class to extend its features. - /// - public unsafe partial class WindowEx : Window, IDisposable - { - private bool _isInitialized; - private readonly WNDPROC _oldWndProc; - private readonly WNDPROC _newWndProc; - - private readonly ApplicationDataContainer _applicationDataContainer = ApplicationData.Current.LocalSettings; - - /// - /// Gets hWnd of this . - /// - public nint WindowHandle { get; } - - /// - /// Gets min width of this . - /// - public int MinWidth { get; } - - /// - /// Gets min height of this . - /// - public int MinHeight { get; } - - private bool _IsMaximizable = true; - /// - /// Gets or sets a value that indicates whether this can be maximizable. - /// - public bool IsMaximizable - { - get => _IsMaximizable; - set - { - _IsMaximizable = value; - - if (AppWindow.Presenter is OverlappedPresenter overlapped) - overlapped.IsMaximizable = value; - } - } - - private bool _IsMinimizable = true; - /// - /// Gets or sets a value that indicates whether this can be minimizable. - /// - public bool IsMinimizable - { - get => _IsMinimizable; - set - { - _IsMinimizable = value; - - if (AppWindow.Presenter is OverlappedPresenter overlapped) - overlapped.IsMinimizable = value; - } - } - - /// - /// Initializes class. - /// - /// Min width to set when initialized. - /// Min height to set when initialized. - public unsafe WindowEx(int minWidth = 400, int minHeight = 300) - { - WindowHandle = WinRT.Interop.WindowNative.GetWindowHandle(this); - MinWidth = minWidth; - MinHeight = minHeight; - IsMaximizable = true; - IsMinimizable = true; - - _newWndProc = new(NewWindowProc); - var pNewWndProc = Marshal.GetFunctionPointerForDelegate(_newWndProc); - var pOldWndProc = PInvoke.SetWindowLongPtr(new(WindowHandle), WINDOW_LONG_PTR_INDEX.GWL_WNDPROC, pNewWndProc); - _oldWndProc = Marshal.GetDelegateForFunctionPointer(pOldWndProc); - - Closed += WindowEx_Closed; - } - - private unsafe void StoreWindowPlacementData() - { - // Save window placement only for MainWindow - if (!GetType().Name.Equals(nameof(MainWindow), StringComparison.OrdinalIgnoreCase)) - return; - - // Store monitor info - using var data = new SystemIO.MemoryStream(); - using var sw = new SystemIO.BinaryWriter(data); - - var monitors = GetAllMonitorInfo(); - int nMonitors = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CMONITORS); - sw.Write(nMonitors); - - foreach (var monitor in monitors) - { - sw.Write(monitor.Item1); - sw.Write(monitor.Item2.Left); - sw.Write(monitor.Item2.Top); - sw.Write(monitor.Item2.Right); - sw.Write(monitor.Item2.Bottom); - } - - WINDOWPLACEMENT placement = default; - PInvoke.GetWindowPlacement(new(WindowHandle), ref placement); - - int structSize = Marshal.SizeOf(typeof(WINDOWPLACEMENT)); - IntPtr buffer = Marshal.AllocHGlobal(structSize); - Marshal.StructureToPtr(placement, buffer, false); - byte[] placementData = new byte[structSize]; - Marshal.Copy(buffer, placementData, 0, structSize); - Marshal.FreeHGlobal(buffer); - - sw.Write(placementData); - sw.Flush(); - - var values = GetDataStore(out _, true); - - if (_applicationDataContainer.Containers.ContainsKey("WinUIEx")) - _applicationDataContainer.Values.Remove("WinUIEx"); - - values["MainWindowPlacementData"] = Convert.ToBase64String(data.ToArray()); - } - - private void RestoreWindowPlacementData() - { - // Save window placement only for MainWindow - if (!GetType().Name.Equals(nameof(MainWindow), StringComparison.OrdinalIgnoreCase)) - return; - - var values = GetDataStore(out var oldDataExists, false); - - byte[]? data = null; - if (values.TryGetValue(oldDataExists ? "WindowPersistance_FilesMainWindow" : "MainWindowPlacementData", out object? value)) - { - if (value is string base64) - data = Convert.FromBase64String(base64); - } - - if (data is null) - return; - - SystemIO.BinaryReader br = new(new SystemIO.MemoryStream(data)); - - // Check if monitor layout changed since we stored position - var monitors = GetAllMonitorInfo(); - int monitorCount = br.ReadInt32(); - int nMonitors = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CMONITORS); - if (monitorCount != nMonitors) - return; - - for (int i = 0; i < monitorCount; i++) - { - var pMonitor = monitors[i]; - br.ReadString(); - if (pMonitor.Item2.Left != br.ReadDouble() || - pMonitor.Item2.Top != br.ReadDouble() || - pMonitor.Item2.Right != br.ReadDouble() || - pMonitor.Item2.Bottom != br.ReadDouble()) - return; - } - - int structSize = Marshal.SizeOf(typeof(WINDOWPLACEMENT)); - byte[] placementData = br.ReadBytes(structSize); - IntPtr buffer = Marshal.AllocHGlobal(structSize); - Marshal.Copy(placementData, 0, buffer, structSize); - var windowPlacementData = (WINDOWPLACEMENT)Marshal.PtrToStructure(buffer, typeof(WINDOWPLACEMENT))!; - - Marshal.FreeHGlobal(buffer); - - // Ignore anything by maximized or normal - if (windowPlacementData.showCmd == (SHOW_WINDOW_CMD)0x0002 /*SW_INVALIDATE*/ && - windowPlacementData.flags == WINDOWPLACEMENT_FLAGS.WPF_RESTORETOMAXIMIZED) - windowPlacementData.showCmd = SHOW_WINDOW_CMD.SW_MAXIMIZE; - else if (windowPlacementData.showCmd != SHOW_WINDOW_CMD.SW_MAXIMIZE) - windowPlacementData.showCmd = SHOW_WINDOW_CMD.SW_NORMAL; - - PInvoke.SetWindowPlacement(new(WindowHandle), in windowPlacementData); - - return; - } - - private IPropertySet GetDataStore(out bool oldDataExists, bool useNewStore = true) - { - IPropertySet values; - oldDataExists = false; - - if (_applicationDataContainer.Containers.TryGetValue("Files", out var dataContainer)) - { - values = dataContainer.Values; - } - else if (!useNewStore && _applicationDataContainer.Containers.TryGetValue("WinUIEx", out var oldDataContainer)) - { - values = oldDataContainer.Values; - oldDataExists = true; - } - else - { - values = _applicationDataContainer.CreateContainer( - "Files", - ApplicationDataCreateDisposition.Always).Values; - } - - return values; - } - - private unsafe List> GetAllMonitorInfo() - { - List> monitors = []; - - MONITORENUMPROC monitorEnumProc = new((HMONITOR monitor, HDC deviceContext, RECT* rect, LPARAM data) => - { - MONITORINFOEXW info = default; - info.monitorInfo.cbSize = (uint)Marshal.SizeOf(); - - PInvoke.GetMonitorInfo(monitor, (MONITORINFO*)&info); - - monitors.Add(new( - info.szDevice.ToString(), - new(new Point(rect->left, rect->top), new Point(rect->right, rect->bottom)))); - - return true; - }); - - var pMonitorEnumProc = Marshal.GetFunctionPointerForDelegate(monitorEnumProc); - var pfnMonitorEnumProc = (delegate* unmanaged[Stdcall])pMonitorEnumProc; - - LPARAM lParam = default; - BOOL fRes = PInvoke.EnumDisplayMonitors(new(nint.Zero), (RECT*)null, pfnMonitorEnumProc, lParam); - if (!fRes) - Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error()); - - return monitors; - } - - private LRESULT NewWindowProc(HWND param0, uint param1, WPARAM param2, LPARAM param3) - { - switch (param1) - { - case 0x0018 /*WM_SHOWWINDOW*/ when param2 == (WPARAM)1 && !_isInitialized: - { - _isInitialized = true; - RestoreWindowPlacementData(); - break; - } - case 0x0024: /*WM_GETMINMAXINFO*/ - { - var dpi = PInvoke.GetDpiForWindow(param0); - float scalingFactor = (float)dpi / 96; - - var minMaxInfo = Marshal.PtrToStructure(param3); - minMaxInfo.ptMinTrackSize.X = (int)(MinWidth * scalingFactor); - minMaxInfo.ptMinTrackSize.Y = (int)(MinHeight * scalingFactor); - Marshal.StructureToPtr(minMaxInfo, param3, true); - break; - } - } - - var pWindProc = Marshal.GetFunctionPointerForDelegate(_oldWndProc); - var pfnWndProc = (delegate* unmanaged[Stdcall])pWindProc; - - return PInvoke.CallWindowProc(pfnWndProc, param0, param1, param2, param3); - } - - private void WindowEx_Closed(object sender, WindowEventArgs args) - { - StoreWindowPlacementData(); - } - - public void Dispose() - { - Closed -= WindowEx_Closed; - } - } -} diff --git a/src/Files.App/MainWindow.xaml b/src/Files.App/MainWindow.xaml index 101a403aba79..376ae4f1d5b7 100644 --- a/src/Files.App/MainWindow.xaml +++ b/src/Files.App/MainWindow.xaml @@ -1,10 +1,8 @@  - diff --git a/src/Files.App/MainWindow.xaml.cs b/src/Files.App/MainWindow.xaml.cs index 6dcfc8b56310..2abe332d706b 100644 --- a/src/Files.App/MainWindow.xaml.cs +++ b/src/Files.App/MainWindow.xaml.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging; using Microsoft.UI; using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Animation; using System.IO; @@ -15,7 +16,7 @@ namespace Files.App { - public sealed partial class MainWindow : WinUIEx.WindowEx + public sealed partial class MainWindow : Window { private static MainWindow? _Instance; public static MainWindow Instance => _Instance ??= new(); @@ -28,18 +29,21 @@ public MainWindow() { InitializeComponent(); - WindowHandle = WinUIEx.WindowExtensions.GetWindowHandle(this); - MinHeight = 316; - MinWidth = 416; + WindowHandle = WinRT.Interop.WindowNative.GetWindowHandle(this); ExtendsContentIntoTitleBar = true; Title = "Files"; - PersistenceId = "FilesMainWindow"; AppWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent; AppWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent; AppWindow.TitleBar.ButtonPressedBackgroundColor = Colors.Transparent; AppWindow.TitleBar.ButtonHoverBackgroundColor = Colors.Transparent; AppWindow.SetIcon(AppLifecycleHelper.AppIconPath); + if (AppWindow.Presenter.TryCast() is { } presenter) + { + presenter.PreferredMinimumHeight = 316; + presenter.PreferredMinimumWidth = 416; + } + WinUIEx.WindowManager.Get(this).WindowMessageReceived += WindowManager_WindowMessageReceived; } @@ -201,8 +205,9 @@ public async Task InitializeApplicationAsync(object activatedEventArgs) Win32Helper.BringToForegroundEx(new(WindowHandle)); } - if (Windows.Win32.PInvoke.IsIconic(new(WindowHandle))) - WinUIEx.WindowExtensions.Restore(Instance); // Restore window if minimized + if (Windows.Win32.PInvoke.IsIconic(new(WindowHandle)) && + AppWindow.Presenter.TryCast() is { } presenter) + presenter.Restore(); // Restore window if minimized } private Frame? EnsureWindowIsInitialized() diff --git a/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs b/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs index 5bfc5f15a7e2..0ca4d6535916 100644 --- a/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs @@ -36,7 +36,7 @@ public static nint GetWindowHandle(Window w) => WinRT.Interop.WindowNative.GetWindowHandle(w); private static TaskCompletionSource? PropertiesWindowsClosingTCS; - private static readonly BlockingCollection WindowCache = []; + private static readonly BlockingCollection WindowCache = []; /// /// Open properties window @@ -103,7 +103,16 @@ public static void OpenPropertiesWindow(object item, IShellPage associatedInstan if (!WindowCache.TryTake(out var propertiesWindow)) { - propertiesWindow = new(460, 550); + propertiesWindow = new(); + + if (propertiesWindow.AppWindow.Presenter.TryCast() is { } presenter) + { + presenter.PreferredMinimumHeight = 460; + presenter.PreferredMinimumWidth = 550; + presenter.IsMinimizable = false; + presenter.IsMaximizable = false; + } + propertiesWindow.Closed += PropertiesWindow_Closed; } @@ -111,13 +120,11 @@ public static void OpenPropertiesWindow(object item, IShellPage associatedInstan var height = Convert.ToInt32(500 * App.AppModel.AppWindowDPI); propertiesWindow.AppWindow.Resize(new (width, height)); - propertiesWindow.IsMinimizable = false; - propertiesWindow.IsMaximizable = false; propertiesWindow.Content = frame; propertiesWindow.SystemBackdrop = new AppSystemBackdrop(true); var appWindow = propertiesWindow.AppWindow; - appWindow.Title = "Properties".GetLocalizedResource(); + appWindow.Title = Strings.Properties.GetLocalizedResource(); appWindow.TitleBar.ExtendsContentIntoTitleBar = true; appWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent; appWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent; @@ -158,7 +165,7 @@ public static void OpenPropertiesWindow(object item, IShellPage associatedInstan // So instead of destroying the Window object, cache it and reuse it as a workaround. private static void PropertiesWindow_Closed(object sender, WindowEventArgs args) { - if (!App.AppModel.IsMainWindowClosed && sender is WindowEx window) + if (!App.AppModel.IsMainWindowClosed && sender is Window window) { args.Handled = true;