diff --git a/src/Snap.Hutao/Snap.Hutao.Win32/Graphics/Gdi/MONITORINFO.cs b/src/Snap.Hutao/Snap.Hutao.Win32/Graphics/Gdi/MONITORINFO.cs new file mode 100644 index 00000000..e29a79c4 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao.Win32/Graphics/Gdi/MONITORINFO.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Windows.Win32.Graphics.Gdi; + +public partial struct MONITORINFO +{ + public static MONITORINFO Default + { + get => new() { cbSize = (uint)Marshal.SizeOf() }; + } +} diff --git a/src/Snap.Hutao/Snap.Hutao.Win32/NativeMethods.txt b/src/Snap.Hutao/Snap.Hutao.Win32/NativeMethods.txt index b128e994..a8cb30c9 100644 --- a/src/Snap.Hutao/Snap.Hutao.Win32/NativeMethods.txt +++ b/src/Snap.Hutao/Snap.Hutao.Win32/NativeMethods.txt @@ -1,16 +1,30 @@ -// ComCtl32 -SetWindowSubclass -RemoveWindowSubclass +// Type definition +MINMAXINFO + + +// DefSubclassProc +SetWindowSubclass +RemoveWindowSubclass + + // User32 -SetWindowText GetDpiForWindow + +GetMonitorInfo + GetWindowPlacement SetWindowPlacement -// Type definition -MINMAXINFO +GetWindowRect + +MonitorFromRect + +SetWindowPos + +SetWindowText + // Const value WM_GETMINMAXINFO \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao.Win32/Snap.Hutao.Win32.csproj b/src/Snap.Hutao/Snap.Hutao.Win32/Snap.Hutao.Win32.csproj index cc02b6f9..9081fd0f 100644 --- a/src/Snap.Hutao/Snap.Hutao.Win32/Snap.Hutao.Win32.csproj +++ b/src/Snap.Hutao/Snap.Hutao.Win32/Snap.Hutao.Win32.csproj @@ -11,6 +11,7 @@ all + diff --git a/src/Snap.Hutao/Snap.Hutao.Win32/UI/WindowsAndMessaging/WINDOWPLACEMENT.cs b/src/Snap.Hutao/Snap.Hutao.Win32/UI/WindowsAndMessaging/WINDOWPLACEMENT.cs index 0e7f5119..fcb98c2e 100644 --- a/src/Snap.Hutao/Snap.Hutao.Win32/UI/WindowsAndMessaging/WINDOWPLACEMENT.cs +++ b/src/Snap.Hutao/Snap.Hutao.Win32/UI/WindowsAndMessaging/WINDOWPLACEMENT.cs @@ -11,10 +11,7 @@ public partial struct WINDOWPLACEMENT { get { - return new WINDOWPLACEMENT() - { - length = (uint)Marshal.SizeOf(), - }; + return new() { length = (uint)Marshal.SizeOf() }; } } diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs index 84626a97..70accd7c 100644 --- a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs @@ -67,11 +67,7 @@ public partial class App : Application get => ApplicationData.Current.LocalSettings; } - /// - /// Invoked when the application is launched. - /// Any async operation in this method should be wrapped with try catch - /// - /// Details about the launch request and process. + /// [SuppressMessage("", "VSTHRD100")] protected override async void OnLaunched(LaunchActivatedEventArgs args) { @@ -83,7 +79,6 @@ public partial class App : Application firstInstance.Activated += OnActivated; Window = Ioc.Default.GetRequiredService(); - Window.Activate(); logger.LogInformation(EventIds.CommonLog, "Cache folder : {folder}", CacheFolder.Path); diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs index ce89cc4a..1e6d5eac 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs @@ -31,6 +31,7 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control private readonly IImageCache imageCache; private SpriteVisual? spriteVisual; + private bool isShow = true; /// /// 构造一个新的单色图像 @@ -88,7 +89,7 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control if (exception is HttpRequestException httpRequestException) { - infoBarService.Warning($"GET {uri}\n{httpRequestException}"); + infoBarService.Error(httpRequestException, $"GET {uri}"); } else { @@ -113,7 +114,6 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control } else { - // should hide image.HideAsync(token).SafeForget(logger); } } @@ -149,14 +149,22 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control } } - private Task ShowAsync(CancellationToken token) + private async Task ShowAsync(CancellationToken token) { - return AnimationBuilder.Create().Opacity(1d).StartAsync(this, token); + if (!isShow) + { + await AnimationBuilder.Create().Opacity(1d).StartAsync(this, token); + isShow = true; + } } - private Task HideAsync(CancellationToken token) + private async Task HideAsync(CancellationToken token) { - return AnimationBuilder.Create().Opacity(0d).StartAsync(this, token); + if (isShow) + { + await AnimationBuilder.Create().Opacity(0d).StartAsync(this, token); + isShow = false; + } } private void OnSizeChanged(object sender, SizeChangedEventArgs e) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/WindowManager.cs b/src/Snap.Hutao/Snap.Hutao/Core/WindowManager.cs deleted file mode 100644 index a8435071..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Core/WindowManager.cs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Microsoft.UI; -using Microsoft.UI.Windowing; -using Microsoft.UI.Xaml; -using Snap.Hutao.Control.HostBackdrop; -using Snap.Hutao.Core.Logging; -using Snap.Hutao.Core.Setting; -using Windows.Win32.Foundation; -using Windows.Win32.UI.Shell; -using Windows.Win32.UI.WindowsAndMessaging; -using WinRT.Interop; - -using static Windows.Win32.PInvoke; - -namespace Snap.Hutao.Core; - -/// -/// 窗口管理器 -/// 主要包含了针对窗体的 P/Inoke 逻辑 -/// -internal class WindowManager -{ - private const int MinWidth = 848; - private const int MinHeight = 524; - private const int SubclassId = 101; - - private static readonly Windows.UI.Color SystemBaseLowColor = Windows.UI.Color.FromArgb(0x33, 0xFF, 0xFF, 0xFF); - private static readonly Windows.UI.Color SystemBaseMediumLowColor = Windows.UI.Color.FromArgb(0x66, 0xFF, 0xFF, 0xFF); - private readonly HWND handle; - private readonly Window window; - private readonly UIElement titleBar; - private readonly ILogger logger; - - // We have to explictly hold a reference to the SUBCLASSPROC, - // otherwise will casuse System.ExecutionEngineException - private SUBCLASSPROC? subClassProc; - - /// - /// 构造一个新的窗口状态管理器 - /// - /// 窗口 - /// 充当标题栏的元素 - public WindowManager(Window window, UIElement titleBar) - { - this.window = window; - this.titleBar = titleBar; - logger = Ioc.Default.GetRequiredService>(); - - handle = (HWND)WindowNative.GetWindowHandle(window); - - InitializeWindow(); - } - - private static RECT RetriveWindowRect() - { - int left = LocalSetting.GetValueType(SettingKeys.WindowLeft); - int top = LocalSetting.GetValueType(SettingKeys.WindowTop); - int right = LocalSetting.GetValueType(SettingKeys.WindowRight); - int bottom = LocalSetting.GetValueType(SettingKeys.WindowBottom); - - return new(left, top, right, bottom); - } - - private static void SaveWindowRect(HWND handle) - { - WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Default; - - GetWindowPlacement(handle, ref windowPlacement); - - LocalSetting.Set(SettingKeys.WindowLeft, windowPlacement.rcNormalPosition.left); - LocalSetting.Set(SettingKeys.WindowTop, windowPlacement.rcNormalPosition.top); - LocalSetting.Set(SettingKeys.WindowRight, windowPlacement.rcNormalPosition.right); - LocalSetting.Set(SettingKeys.WindowBottom, windowPlacement.rcNormalPosition.bottom); - } - - private void InitializeWindow() - { - if (false && AppWindowTitleBar.IsCustomizationSupported()) - { - AppWindow appWindow = GetAppWindow(); - AppWindowTitleBar titleBar = appWindow.TitleBar; - titleBar.ExtendsContentIntoTitleBar = true; - - titleBar.ButtonBackgroundColor = Colors.Transparent; - titleBar.ButtonHoverBackgroundColor = SystemBaseLowColor; - titleBar.ButtonPressedBackgroundColor = SystemBaseMediumLowColor; - titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; - - // appWindow.TitleBar.SetDragRectangles(); - appWindow.Title = "胡桃"; - } - else - { - window.ExtendsContentIntoTitleBar = true; - window.SetTitleBar(titleBar); - - SetWindowText(handle, "胡桃"); - } - - window.Closed += OnWindowClosed; - RECT rect = RetriveWindowRect(); - if (rect.Size > 0) - { - WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Create(new(-1, -1), rect, SHOW_WINDOW_CMD.SW_SHOWNORMAL); - SetWindowPlacement(handle, in windowPlacement); - } - - bool micaApplied = new SystemBackdrop(window).TrySetBackdrop(); - logger.LogInformation(EventIds.BackdropState, "Apply {name} : {result}", nameof(SystemBackdrop), micaApplied ? "succeed" : "failed"); - - subClassProc = new(OnSubclassProcedure); - bool subClassApplied = SetWindowSubclass(handle, subClassProc, SubclassId, 0); - logger.LogInformation(EventIds.SubClassing, "Apply {name} : {result}", nameof(SUBCLASSPROC), subClassApplied ? "succeed" : "failed"); - } - - private void OnWindowClosed(object sender, WindowEventArgs args) - { - RemoveWindowSubclass(handle, subClassProc, SubclassId); - subClassProc = null; - SaveWindowRect(handle); - } - - private LRESULT OnSubclassProcedure(HWND hwnd, uint uMsg, WPARAM wParam, LPARAM lParam, nuint uIdSubclass, nuint dwRefData) - { - switch (uMsg) - { - case WM_GETMINMAXINFO: - { - uint dpi = GetDpiForWindow(handle); - float scalingFactor = dpi / 96f; - Win32.Unsafe.SetMinTrackSize(lParam, MinWidth * scalingFactor, MinHeight * scalingFactor); - break; - } - } - - return DefSubclassProc(hwnd, uMsg, wParam, lParam); - } - - private AppWindow GetAppWindow() - { - WindowId windowId = Win32Interop.GetWindowIdFromWindow(handle); - return AppWindow.GetFromWindowId(windowId); - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/Interop.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/Interop.cs new file mode 100644 index 00000000..b8917728 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/Interop.cs @@ -0,0 +1,91 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.UI; +using Microsoft.UI.Windowing; +using System.Numerics; +using Windows.Win32.Foundation; +using Windows.Win32.Graphics.Gdi; +using Windows.Win32.UI.WindowsAndMessaging; +using static Windows.Win32.PInvoke; + +namespace Snap.Hutao.Core.Windowing; + +/// +/// Win32/COM 窗体互操作 +/// +internal static class Interop +{ + /// + /// 获取 + /// + /// 窗体句柄 + /// AppWindow + public static AppWindow GetAppWindow(HWND hwnd) + { + WindowId windowId = Win32Interop.GetWindowIdFromWindow(hwnd); + return AppWindow.GetFromWindowId(windowId); + } + + /// + /// 设置窗体位置 + /// + /// 窗体句柄 + public static void SetWindowPosition(HWND hwnd) + { + if (WindowRect.RetriveWindowRect() is RECT { Size: > 0 } retrived) + { + WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Create(new(-1, -1), retrived, SHOW_WINDOW_CMD.SW_SHOWNORMAL); + SetWindowPlacement(hwnd, in windowPlacement); + } + else + { + // Set first launch size. + Vector2 size = Interop.TransformSizeForWindow(new(1200, 741), hwnd); + SET_WINDOW_POS_FLAGS first = SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOZORDER; + SetWindowPos(hwnd, default, 0, 0, (int)size.X, (int)size.Y, first); + + // Make it centralized + GetWindowRect(hwnd, out RECT rect); + Interop.TransformToCenterScreen(ref rect); + + SET_WINDOW_POS_FLAGS center = SET_WINDOW_POS_FLAGS.SWP_NOSIZE | SET_WINDOW_POS_FLAGS.SWP_NOZORDER | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE; + SetWindowPos(hwnd, default, rect.left, rect.top, 0, 0, center); + } + } + + /// + /// 转换到当前窗体的DPI的尺寸 + /// + /// 尺寸 + /// 窗体句柄 + /// 对应当前窗体DPI的尺寸 + public static Vector2 TransformSizeForWindow(Vector2 size, HWND hwnd) + { + uint dpi = GetDpiForWindow(hwnd); + float scale = (float)dpi / 96; + return new(size.X * scale, size.Y * scale); + } + + /// + /// 将矩形转换到屏幕中央 + /// 当宽高超过时,会裁剪 + /// + /// 矩形 + public static void TransformToCenterScreen(ref RECT rect) + { + HMONITOR hMonitor = MonitorFromRect(rect, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST); + MONITORINFO mi = MONITORINFO.Default; + GetMonitorInfo(hMonitor, ref mi); + + RECT workAreaRect = mi.rcWork; + + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + + rect.left = workAreaRect.left + ((workAreaRect.right - workAreaRect.left - width) / 2); + rect.top = workAreaRect.top + ((workAreaRect.bottom - workAreaRect.top - height) / 2); + rect.right = rect.left + width; + rect.bottom = rect.top + height; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Backdroping/SystemBackdrop.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/SystemBackdrop.cs similarity index 99% rename from src/Snap.Hutao/Snap.Hutao/Control/Backdroping/SystemBackdrop.cs rename to src/Snap.Hutao/Snap.Hutao/Core/Windowing/SystemBackdrop.cs index cc096891..1bca1719 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Backdroping/SystemBackdrop.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/SystemBackdrop.cs @@ -8,7 +8,7 @@ using System.Runtime.InteropServices; using Windows.System; using WinRT; -namespace Snap.Hutao.Control.HostBackdrop; +namespace Snap.Hutao.Core.Windowing; /// /// 系统背景帮助类 diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowManager.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowManager.cs new file mode 100644 index 00000000..e67c31cf --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowManager.cs @@ -0,0 +1,133 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.UI; +using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml; +using Snap.Hutao.Core.Logging; +using System.Collections.Generic; +using Windows.Graphics; +using Windows.UI; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Shell; +using WinRT.Interop; +using static Windows.Win32.PInvoke; + +namespace Snap.Hutao.Core.Windowing; + +/// +/// 窗口管理器 +/// 主要包含了针对窗体的 P/Inoke 逻辑 +/// +internal sealed class WindowManager : IDisposable +{ + private const int MinWidth = 848; + private const int MinHeight = 524; + private const int SubclassId = 101; + + private readonly HWND handle; + private readonly Window window; + private readonly FrameworkElement titleBar; + private readonly ILogger logger; + private readonly WindowSubclassManager subclassManager; + + /// + /// 构造一个新的窗口状态管理器 + /// + /// 窗口 + /// 充当标题栏的元素 + public WindowManager(Window window, FrameworkElement titleBar) + { + this.window = window; + this.titleBar = titleBar; + logger = Ioc.Default.GetRequiredService>(); + + handle = (HWND)WindowNative.GetWindowHandle(window); + subclassManager = new(handle); + + InitializeWindow(); + } + + /// + public void Dispose() + { + WindowRect.SaveWindowRect(handle); + subclassManager.Dispose(); + } + + private void InitializeWindow() + { + if (AppWindowTitleBar.IsCustomizationSupported()) + { + AppWindow appWindow = Interop.GetAppWindow(handle); + appWindow.Title = "胡桃"; + + AppWindowTitleBar appTitleBar = appWindow.TitleBar; + appTitleBar.ExtendsContentIntoTitleBar = true; + + UpdateTitleButtonColor(appTitleBar); + UpdateDragRectangles(appTitleBar); + titleBar.SizeChanged += (s, e) => UpdateDragRectangles(appTitleBar); + titleBar.ActualThemeChanged += (s, e) => UpdateTitleButtonColor(appTitleBar); + + appWindow.Show(true); + } + else + { + SetWindowText(handle, "胡桃"); + window.ExtendsContentIntoTitleBar = true; + window.SetTitleBar(titleBar); + + window.Activate(); + } + + Interop.SetWindowPosition(handle); + + bool micaApplied = new SystemBackdrop(window).TrySetBackdrop(); + logger.LogInformation(EventIds.BackdropState, "Apply {name} : {result}", nameof(SystemBackdrop), micaApplied ? "succeed" : "failed"); + + bool subClassApplied = subclassManager.TrySetWindowSubclass(); + logger.LogInformation(EventIds.SubClassing, "Apply {name} : {result}", nameof(SUBCLASSPROC), subClassApplied ? "succeed" : "failed"); + } + + private void UpdateDragRectangles(AppWindowTitleBar appTitleBar) + { + uint dpi = GetDpiForWindow(handle); + + // double scaleAdjustment = (uint)((((long)dpi * 100) + (96 >> 1)) / 96) / 100.0; + double scale = Math.Round(dpi / 96d, 2, MidpointRounding.AwayFromZero); + + List dragRectsList = new(); + + // 48 is the navigation button leftInset + RectInt32 dragRect = new((int)(48 * scale), 0, (int)(titleBar.ActualWidth * scale), (int)(titleBar.ActualHeight * scale)); + dragRectsList.Add(dragRect); + + RectInt32[] dragRects = dragRectsList.ToArray(); + + appTitleBar.SetDragRectangles(dragRects); + } + + [SuppressMessage("", "CA1822")] + private void UpdateTitleButtonColor(AppWindowTitleBar appTitleBar) + { + appTitleBar.ButtonBackgroundColor = Colors.Transparent; + appTitleBar.ButtonInactiveBackgroundColor = Colors.Transparent; + + Color systemBaseLowColor = (Color)App.Current.Resources["SystemBaseLowColor"]; + appTitleBar.ButtonHoverBackgroundColor = systemBaseLowColor; + + Color systemBaseMediumLowColor = (Color)App.Current.Resources["SystemBaseMediumLowColor"]; + appTitleBar.ButtonPressedBackgroundColor = systemBaseMediumLowColor; + + // The Foreground doesn't accept Alpha channel. So we translate it to gray. + byte light = (byte)((systemBaseMediumLowColor.R + systemBaseMediumLowColor.G + systemBaseMediumLowColor.B) / 3); + byte result = (byte)((systemBaseMediumLowColor.A / 255.0) * light); + appTitleBar.ButtonInactiveForegroundColor = Color.FromArgb(0XFF, result, result, result); + + Color systemBaseHighColor = (Color)App.Current.Resources["SystemBaseHighColor"]; + appTitleBar.ButtonForegroundColor = systemBaseHighColor; + appTitleBar.ButtonHoverForegroundColor = systemBaseHighColor; + appTitleBar.ButtonPressedForegroundColor = systemBaseHighColor; + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowRect.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowRect.cs new file mode 100644 index 00000000..fba720e6 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowRect.cs @@ -0,0 +1,45 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core.Setting; +using Windows.Win32.Foundation; +using Windows.Win32.UI.WindowsAndMessaging; +using static Windows.Win32.PInvoke; + +namespace Snap.Hutao.Core.Windowing; + +/// +/// 窗体矩形 +/// +internal static class WindowRect +{ + /// + /// 获取窗体矩形 + /// + /// 矩形 + public static RECT RetriveWindowRect() + { + int left = LocalSetting.GetValueType(SettingKeys.WindowLeft); + int top = LocalSetting.GetValueType(SettingKeys.WindowTop); + int right = LocalSetting.GetValueType(SettingKeys.WindowRight); + int bottom = LocalSetting.GetValueType(SettingKeys.WindowBottom); + + return new(left, top, right, bottom); + } + + /// + /// 保存窗体矩形 + /// + /// 窗体句柄 + public static void SaveWindowRect(HWND handle) + { + WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Default; + + GetWindowPlacement(handle, ref windowPlacement); + + LocalSetting.Set(SettingKeys.WindowLeft, windowPlacement.rcNormalPosition.left); + LocalSetting.Set(SettingKeys.WindowTop, windowPlacement.rcNormalPosition.top); + LocalSetting.Set(SettingKeys.WindowRight, windowPlacement.rcNormalPosition.right); + LocalSetting.Set(SettingKeys.WindowBottom, windowPlacement.rcNormalPosition.bottom); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclassManager.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclassManager.cs new file mode 100644 index 00000000..55ef323d --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclassManager.cs @@ -0,0 +1,68 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Windows.Win32.Foundation; +using Windows.Win32.UI.Shell; + +using static Windows.Win32.PInvoke; + +namespace Snap.Hutao.Core.Windowing; + +/// +/// 窗体子类管理器 +/// +internal class WindowSubclassManager : IDisposable +{ + private const int SubclassId = 101; + + private const int MinWidth = 848; + private const int MinHeight = 524; + + private readonly HWND hwnd; + + // We have to explictly hold a reference to the SUBCLASSPROC, + // otherwise will casuse System.ExecutionEngineException + private SUBCLASSPROC? subClassProc; + + /// + /// 构造一个新的窗体子类管理器 + /// + /// 窗体句柄 + public WindowSubclassManager(HWND hwnd) + { + this.hwnd = hwnd; + } + + /// + /// 尝试设置窗体子类 + /// + /// 是否设置成功 + public bool TrySetWindowSubclass() + { + subClassProc = new(OnSubclassProcedure); + return SetWindowSubclass(hwnd, subClassProc, SubclassId, 0); + } + + /// + public void Dispose() + { + RemoveWindowSubclass(hwnd, subClassProc, SubclassId); + subClassProc = null; + } + + private LRESULT OnSubclassProcedure(HWND hwnd, uint uMsg, WPARAM wParam, LPARAM lParam, nuint uIdSubclass, nuint dwRefData) + { + switch (uMsg) + { + case WM_GETMINMAXINFO: + { + uint dpi = GetDpiForWindow(hwnd); + float scalingFactor = dpi / 96f; + Win32.Unsafe.SetMinTrackSize(lParam, MinWidth * scalingFactor, MinHeight * scalingFactor); + break; + } + } + + return DefSubclassProc(hwnd, uMsg, wParam, lParam); + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs index f94a4e7a..54e81d12 100644 --- a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs @@ -3,7 +3,7 @@ using Microsoft.UI.Xaml; using Snap.Hutao.Context.Database; -using Snap.Hutao.Core; +using Snap.Hutao.Core.Windowing; namespace Snap.Hutao; @@ -14,6 +14,7 @@ namespace Snap.Hutao; public sealed partial class MainWindow : Window { private readonly AppDbContext appDbContext; + private readonly WindowManager windowManager; /// /// 构造一个新的主窗体 @@ -24,11 +25,13 @@ public sealed partial class MainWindow : Window { this.appDbContext = appDbContext; InitializeComponent(); - _ = new WindowManager(this, TitleBarView.DragableArea); + windowManager = new WindowManager(this, (FrameworkElement)TitleBarView.DragableArea); } private void MainWindowClosed(object sender, WindowEventArgs args) { + windowManager.Dispose(); + // save userdata datebase int changes = appDbContext.SaveChanges(); Verify.Operation(changes == 0, "存在未经处理的数据库记录更改"); diff --git a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs index 15a4bb68..65581190 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs @@ -1,10 +1,13 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using Snap.Hutao.Core.Logging; using Snap.Hutao.Service.Abstraction; using Snap.Hutao.Service.Navigation; using Snap.Hutao.View.Page; +using Windows.UI.ViewManagement; namespace Snap.Hutao.View; @@ -15,6 +18,7 @@ public sealed partial class MainView : UserControl { private readonly INavigationService navigationService; private readonly IInfoBarService infoBarService; + private readonly UISettings uISettings; /// /// 构造一个新的主视图 @@ -23,6 +27,9 @@ public sealed partial class MainView : UserControl { InitializeComponent(); + uISettings = new(); + uISettings.ColorValuesChanged += OnUISettingsColorValuesChanged; + infoBarService = Ioc.Default.GetRequiredService(); infoBarService.Initialize(InfoBarStack); @@ -31,4 +38,28 @@ public sealed partial class MainView : UserControl navigationService.Navigate(INavigationAwaiter.Default, true); } + + private void OnUISettingsColorValuesChanged(UISettings sender, object args) + { + Program.UIDispatcherQueue.TryEnqueue(UpdateTheme); + } + + private void UpdateTheme() + { + if (RequestedTheme.ToString() == App.Current.RequestedTheme.ToString()) + { + return; + } + + ILogger logger = Ioc.Default.GetRequiredService>(); + logger.LogInformation(EventIds.CommonLog, "Element Theme [{element}] App Theme [{app}]", RequestedTheme, App.Current.RequestedTheme); + + // Update controls' theme which presents in the PopupRoot + RequestedTheme = App.Current.RequestedTheme switch + { + ApplicationTheme.Light => ElementTheme.Light, + ApplicationTheme.Dark => ElementTheme.Dark, + _ => throw Must.NeverHappen(), + }; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs index 408d3882..e962abd0 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs @@ -94,8 +94,10 @@ openInWebview: function(url){ location.href = url }}"; { await WebView.EnsureCoreWebView2Async(); - await WebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(MihoyoSDKDefinition); + WebView.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false; WebView.CoreWebView2.WebMessageReceived += OnWebMessageReceived; + + await WebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(MihoyoSDKDefinition); } catch (Exception ex) { diff --git a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml index dda1c033..45528d92 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml @@ -61,6 +61,7 @@ TargetType="FlyoutPresenter" BasedOn="{StaticResource DefaultFlyoutPresenterStyle}"> +