mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
use AppWindow API if we can
This commit is contained in:
11
src/Snap.Hutao/Snap.Hutao.Win32/Graphics/Gdi/MONITORINFO.cs
Normal file
11
src/Snap.Hutao/Snap.Hutao.Win32/Graphics/Gdi/MONITORINFO.cs
Normal file
@@ -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<MONITORINFO>() };
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -11,6 +11,7 @@
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.10-beta">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Windows.SDK.Win32Metadata" Version="24.0.1-preview" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -11,10 +11,7 @@ public partial struct WINDOWPLACEMENT
|
||||
{
|
||||
get
|
||||
{
|
||||
return new WINDOWPLACEMENT()
|
||||
{
|
||||
length = (uint)Marshal.SizeOf<WINDOWPLACEMENT>(),
|
||||
};
|
||||
return new() { length = (uint)Marshal.SizeOf<WINDOWPLACEMENT>() };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,11 +67,7 @@ public partial class App : Application
|
||||
get => ApplicationData.Current.LocalSettings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the application is launched.
|
||||
/// Any async operation in this method should be wrapped with try catch
|
||||
/// </summary>
|
||||
/// <param name="args">Details about the launch request and process.</param>
|
||||
/// <inheritdoc/>
|
||||
[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<MainWindow>();
|
||||
Window.Activate();
|
||||
|
||||
logger.LogInformation(EventIds.CommonLog, "Cache folder : {folder}", CacheFolder.Path);
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
||||
private readonly IImageCache imageCache;
|
||||
|
||||
private SpriteVisual? spriteVisual;
|
||||
private bool isShow = true;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的单色图像
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 窗口管理器
|
||||
/// 主要包含了针对窗体的 P/Inoke 逻辑
|
||||
/// </summary>
|
||||
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<WindowManager> logger;
|
||||
|
||||
// We have to explictly hold a reference to the SUBCLASSPROC,
|
||||
// otherwise will casuse System.ExecutionEngineException
|
||||
private SUBCLASSPROC? subClassProc;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的窗口状态管理器
|
||||
/// </summary>
|
||||
/// <param name="window">窗口</param>
|
||||
/// <param name="titleBar">充当标题栏的元素</param>
|
||||
public WindowManager(Window window, UIElement titleBar)
|
||||
{
|
||||
this.window = window;
|
||||
this.titleBar = titleBar;
|
||||
logger = Ioc.Default.GetRequiredService<ILogger<WindowManager>>();
|
||||
|
||||
handle = (HWND)WindowNative.GetWindowHandle(window);
|
||||
|
||||
InitializeWindow();
|
||||
}
|
||||
|
||||
private static RECT RetriveWindowRect()
|
||||
{
|
||||
int left = LocalSetting.GetValueType<int>(SettingKeys.WindowLeft);
|
||||
int top = LocalSetting.GetValueType<int>(SettingKeys.WindowTop);
|
||||
int right = LocalSetting.GetValueType<int>(SettingKeys.WindowRight);
|
||||
int bottom = LocalSetting.GetValueType<int>(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);
|
||||
}
|
||||
}
|
||||
91
src/Snap.Hutao/Snap.Hutao/Core/Windowing/Interop.cs
Normal file
91
src/Snap.Hutao/Snap.Hutao/Core/Windowing/Interop.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Win32/COM 窗体互操作
|
||||
/// </summary>
|
||||
internal static class Interop
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取 <see cref="AppWindow"/>
|
||||
/// </summary>
|
||||
/// <param name="hwnd">窗体句柄</param>
|
||||
/// <returns>AppWindow</returns>
|
||||
public static AppWindow GetAppWindow(HWND hwnd)
|
||||
{
|
||||
WindowId windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
|
||||
return AppWindow.GetFromWindowId(windowId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置窗体位置
|
||||
/// </summary>
|
||||
/// <param name="hwnd">窗体句柄</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换到当前窗体的DPI的尺寸
|
||||
/// </summary>
|
||||
/// <param name="size">尺寸</param>
|
||||
/// <param name="hwnd">窗体句柄</param>
|
||||
/// <returns>对应当前窗体DPI的尺寸</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将矩形转换到屏幕中央
|
||||
/// 当宽高超过时,会裁剪
|
||||
/// </summary>
|
||||
/// <param name="rect">矩形</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
|
||||
using Windows.System;
|
||||
using WinRT;
|
||||
|
||||
namespace Snap.Hutao.Control.HostBackdrop;
|
||||
namespace Snap.Hutao.Core.Windowing;
|
||||
|
||||
/// <summary>
|
||||
/// 系统背景帮助类
|
||||
133
src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowManager.cs
Normal file
133
src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowManager.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 窗口管理器
|
||||
/// 主要包含了针对窗体的 P/Inoke 逻辑
|
||||
/// </summary>
|
||||
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<WindowManager> logger;
|
||||
private readonly WindowSubclassManager subclassManager;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的窗口状态管理器
|
||||
/// </summary>
|
||||
/// <param name="window">窗口</param>
|
||||
/// <param name="titleBar">充当标题栏的元素</param>
|
||||
public WindowManager(Window window, FrameworkElement titleBar)
|
||||
{
|
||||
this.window = window;
|
||||
this.titleBar = titleBar;
|
||||
logger = Ioc.Default.GetRequiredService<ILogger<WindowManager>>();
|
||||
|
||||
handle = (HWND)WindowNative.GetWindowHandle(window);
|
||||
subclassManager = new(handle);
|
||||
|
||||
InitializeWindow();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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<RectInt32> 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;
|
||||
}
|
||||
}
|
||||
45
src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowRect.cs
Normal file
45
src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowRect.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 窗体矩形
|
||||
/// </summary>
|
||||
internal static class WindowRect
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取窗体矩形
|
||||
/// </summary>
|
||||
/// <returns>矩形</returns>
|
||||
public static RECT RetriveWindowRect()
|
||||
{
|
||||
int left = LocalSetting.GetValueType<int>(SettingKeys.WindowLeft);
|
||||
int top = LocalSetting.GetValueType<int>(SettingKeys.WindowTop);
|
||||
int right = LocalSetting.GetValueType<int>(SettingKeys.WindowRight);
|
||||
int bottom = LocalSetting.GetValueType<int>(SettingKeys.WindowBottom);
|
||||
|
||||
return new(left, top, right, bottom);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存窗体矩形
|
||||
/// </summary>
|
||||
/// <param name="handle">窗体句柄</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 窗体子类管理器
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的窗体子类管理器
|
||||
/// </summary>
|
||||
/// <param name="hwnd">窗体句柄</param>
|
||||
public WindowSubclassManager(HWND hwnd)
|
||||
{
|
||||
this.hwnd = hwnd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试设置窗体子类
|
||||
/// </summary>
|
||||
/// <returns>是否设置成功</returns>
|
||||
public bool TrySetWindowSubclass()
|
||||
{
|
||||
subClassProc = new(OnSubclassProcedure);
|
||||
return SetWindowSubclass(hwnd, subClassProc, SubclassId, 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的主窗体
|
||||
@@ -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, "存在未经处理的数据库记录更改");
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的主视图
|
||||
@@ -23,6 +27,9 @@ public sealed partial class MainView : UserControl
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
uISettings = new();
|
||||
uISettings.ColorValuesChanged += OnUISettingsColorValuesChanged;
|
||||
|
||||
infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
||||
infoBarService.Initialize(InfoBarStack);
|
||||
|
||||
@@ -31,4 +38,28 @@ public sealed partial class MainView : UserControl
|
||||
|
||||
navigationService.Navigate<AnnouncementPage>(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<MainView> logger = Ioc.Default.GetRequiredService<ILogger<MainView>>();
|
||||
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(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
TargetType="FlyoutPresenter"
|
||||
BasedOn="{StaticResource DefaultFlyoutPresenterStyle}">
|
||||
<Setter Property="Padding" Value="0,2,0,2"/>
|
||||
<Setter Property="Background" Value="{ThemeResource FlyoutPresenterBackground}" />
|
||||
</Style>
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
<StackPanel>
|
||||
|
||||
Reference in New Issue
Block a user