mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
refactor window controller
This commit is contained in:
45
src/Snap.Hutao/Snap.Hutao/Core/Windowing/CompactRect.cs
Normal file
45
src/Snap.Hutao/Snap.Hutao/Core/Windowing/CompactRect.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Windows.Graphics;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.Windowing;
|
||||||
|
|
||||||
|
internal readonly struct CompactRect
|
||||||
|
{
|
||||||
|
private readonly short x;
|
||||||
|
private readonly short y;
|
||||||
|
private readonly short width;
|
||||||
|
private readonly short height;
|
||||||
|
|
||||||
|
private CompactRect(int x, int y, int width, int height)
|
||||||
|
{
|
||||||
|
this.x = (short)x;
|
||||||
|
this.y = (short)y;
|
||||||
|
this.width = (short)width;
|
||||||
|
this.height = (short)height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator RectInt32(CompactRect rect)
|
||||||
|
{
|
||||||
|
return new(rect.x, rect.y, rect.width, rect.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static explicit operator CompactRect(RectInt32 rect)
|
||||||
|
{
|
||||||
|
return new(rect.X, rect.Y, rect.Width, rect.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unsafe explicit operator CompactRect(ulong value)
|
||||||
|
{
|
||||||
|
Unsafe.SkipInit(out CompactRect rect);
|
||||||
|
*(ulong*)&rect = value;
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unsafe implicit operator ulong(CompactRect rect)
|
||||||
|
{
|
||||||
|
return *(ulong*)▭
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey.cs
Normal file
27
src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Windows.Win32.Foundation;
|
||||||
|
using static Windows.Win32.PInvoke;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.Windowing;
|
||||||
|
|
||||||
|
internal static class HotKey
|
||||||
|
{
|
||||||
|
private const int DefaultId = 100000;
|
||||||
|
|
||||||
|
public static bool Register(in HWND hwnd)
|
||||||
|
{
|
||||||
|
return RegisterHotKey(hwnd, DefaultId, default, (uint)Windows.Win32.UI.Input.KeyboardAndMouse.VIRTUAL_KEY.VK_F8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Unregister(in HWND hwnd)
|
||||||
|
{
|
||||||
|
return UnregisterHotKey(hwnd, DefaultId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool OnHotKeyPressed()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using Microsoft.UI.Windowing;
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Snap.Hutao.Core.Setting;
|
|
||||||
using Snap.Hutao.Win32;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Windows.Graphics;
|
|
||||||
using Windows.Win32.UI.WindowsAndMessaging;
|
|
||||||
using static Windows.Win32.PInvoke;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.Windowing;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 窗体持久化
|
|
||||||
/// </summary>
|
|
||||||
[HighQuality]
|
|
||||||
internal static class Persistence
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 设置窗体位置
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TWindow">窗体类型</typeparam>
|
|
||||||
/// <param name="window">选项窗口</param>
|
|
||||||
public static void RecoverOrInit<TWindow>(TWindow window)
|
|
||||||
where TWindow : Window, IWindowOptionsSource
|
|
||||||
{
|
|
||||||
WindowOptions options = window.WindowOptions;
|
|
||||||
|
|
||||||
// Set first launch size
|
|
||||||
double scale = options.GetWindowScale();
|
|
||||||
SizeInt32 transformedSize = options.InitSize.Scale(scale);
|
|
||||||
RectInt32 rect = StructMarshal.RectInt32(transformedSize);
|
|
||||||
|
|
||||||
if (options.PersistSize)
|
|
||||||
{
|
|
||||||
RectInt32 persistedRect = (CompactRect)LocalSetting.Get(SettingKeys.WindowRect, (CompactRect)rect);
|
|
||||||
if (persistedRect.Size() >= options.InitSize.Size())
|
|
||||||
{
|
|
||||||
rect = persistedRect;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TransformToCenterScreen(ref rect);
|
|
||||||
window.AppWindow.MoveAndResize(rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 保存窗体的位置
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="window">窗口</param>
|
|
||||||
/// <typeparam name="TWindow">窗体类型</typeparam>
|
|
||||||
public static void Save<TWindow>(TWindow window)
|
|
||||||
where TWindow : Window, IWindowOptionsSource
|
|
||||||
{
|
|
||||||
WINDOWPLACEMENT windowPlacement = StructMarshal.WINDOWPLACEMENT();
|
|
||||||
GetWindowPlacement(window.WindowOptions.Hwnd, ref windowPlacement);
|
|
||||||
|
|
||||||
// prevent save value when we are maximized.
|
|
||||||
if (!windowPlacement.showCmd.HasFlag(SHOW_WINDOW_CMD.SW_SHOWMAXIMIZED))
|
|
||||||
{
|
|
||||||
LocalSetting.Set(SettingKeys.WindowRect, (CompactRect)window.AppWindow.GetRect());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void TransformToCenterScreen(ref RectInt32 rect)
|
|
||||||
{
|
|
||||||
DisplayArea displayArea = DisplayArea.GetFromRect(rect, DisplayAreaFallback.Primary);
|
|
||||||
RectInt32 workAreaRect = displayArea.WorkArea;
|
|
||||||
|
|
||||||
rect.X = workAreaRect.X + ((workAreaRect.Width - rect.Width) / 2);
|
|
||||||
rect.Y = workAreaRect.Y + ((workAreaRect.Height - rect.Height) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly struct CompactRect
|
|
||||||
{
|
|
||||||
private readonly short x;
|
|
||||||
private readonly short y;
|
|
||||||
private readonly short width;
|
|
||||||
private readonly short height;
|
|
||||||
|
|
||||||
private CompactRect(int x, int y, int width, int height)
|
|
||||||
{
|
|
||||||
this.x = (short)x;
|
|
||||||
this.y = (short)y;
|
|
||||||
this.width = (short)width;
|
|
||||||
this.height = (short)height;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static implicit operator RectInt32(CompactRect rect)
|
|
||||||
{
|
|
||||||
return new(rect.x, rect.y, rect.width, rect.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static explicit operator CompactRect(RectInt32 rect)
|
|
||||||
{
|
|
||||||
return new(rect.X, rect.Y, rect.Width, rect.Height);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unsafe explicit operator CompactRect(ulong value)
|
|
||||||
{
|
|
||||||
Unsafe.SkipInit(out CompactRect rect);
|
|
||||||
*(ulong*)&rect = value;
|
|
||||||
return rect;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unsafe implicit operator ulong(CompactRect rect)
|
|
||||||
{
|
|
||||||
return *(ulong*)▭
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
|
||||||
using Microsoft.UI;
|
using Microsoft.UI;
|
||||||
using Microsoft.UI.Composition.SystemBackdrops;
|
using Microsoft.UI.Composition.SystemBackdrops;
|
||||||
using Microsoft.UI.Windowing;
|
using Microsoft.UI.Windowing;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Media;
|
using Microsoft.UI.Xaml.Media;
|
||||||
using Snap.Hutao.Message;
|
using Snap.Hutao.Core.Setting;
|
||||||
using Snap.Hutao.Service;
|
using Snap.Hutao.Service;
|
||||||
using Snap.Hutao.Win32;
|
using Snap.Hutao.Win32;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -15,59 +14,48 @@ using Windows.Graphics;
|
|||||||
using Windows.UI;
|
using Windows.UI;
|
||||||
using Windows.Win32.Foundation;
|
using Windows.Win32.Foundation;
|
||||||
using Windows.Win32.Graphics.Dwm;
|
using Windows.Win32.Graphics.Dwm;
|
||||||
|
using Windows.Win32.UI.WindowsAndMessaging;
|
||||||
using static Windows.Win32.PInvoke;
|
using static Windows.Win32.PInvoke;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.Windowing;
|
namespace Snap.Hutao.Core.Windowing;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 扩展窗口
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TWindow">窗体类型</typeparam>
|
|
||||||
[SuppressMessage("", "CA1001")]
|
[SuppressMessage("", "CA1001")]
|
||||||
internal sealed class ExtendedWindow<TWindow> : IRecipient<FlyoutStateChangedMessage>
|
internal sealed class WindowController
|
||||||
where TWindow : Window, IWindowOptionsSource
|
|
||||||
{
|
{
|
||||||
private readonly TWindow window;
|
private readonly Window window;
|
||||||
|
private readonly WindowOptions options;
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
private readonly WindowSubclass<TWindow> subclass;
|
private readonly WindowSubclass subclass;
|
||||||
|
|
||||||
private ExtendedWindow(TWindow window, IServiceProvider serviceProvider)
|
public WindowController(Window window, in WindowOptions options, IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
this.window = window;
|
this.window = window;
|
||||||
|
this.options = options;
|
||||||
this.serviceProvider = serviceProvider;
|
this.serviceProvider = serviceProvider;
|
||||||
|
|
||||||
subclass = new(window);
|
subclass = new(window, options);
|
||||||
|
|
||||||
InitializeWindow();
|
InitializeCore();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private static void TransformToCenterScreen(ref RectInt32 rect)
|
||||||
/// 初始化
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="window">窗口</param>
|
|
||||||
/// <param name="serviceProvider">服务提供器</param>
|
|
||||||
/// <returns>实例</returns>
|
|
||||||
public static ExtendedWindow<TWindow> Initialize(TWindow window, IServiceProvider serviceProvider)
|
|
||||||
{
|
{
|
||||||
return new(window, serviceProvider);
|
DisplayArea displayArea = DisplayArea.GetFromRect(rect, DisplayAreaFallback.Primary);
|
||||||
|
RectInt32 workAreaRect = displayArea.WorkArea;
|
||||||
|
|
||||||
|
rect.X = workAreaRect.X + ((workAreaRect.Width - rect.Width) / 2);
|
||||||
|
rect.Y = workAreaRect.Y + ((workAreaRect.Height - rect.Height) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
private void InitializeCore()
|
||||||
public void Receive(FlyoutStateChangedMessage message)
|
|
||||||
{
|
|
||||||
UpdateDragRectangles(message.IsOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeWindow()
|
|
||||||
{
|
{
|
||||||
RuntimeOptions hutaoOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
RuntimeOptions hutaoOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||||
|
|
||||||
WindowOptions options = window.WindowOptions;
|
|
||||||
window.AppWindow.Title = SH.AppNameAndVersion.Format(hutaoOptions.Version);
|
window.AppWindow.Title = SH.AppNameAndVersion.Format(hutaoOptions.Version);
|
||||||
window.AppWindow.SetIcon(Path.Combine(hutaoOptions.InstalledLocation, "Assets/Logo.ico"));
|
window.AppWindow.SetIcon(Path.Combine(hutaoOptions.InstalledLocation, "Assets/Logo.ico"));
|
||||||
ExtendsContentIntoTitleBar();
|
ExtendsContentIntoTitleBar();
|
||||||
|
|
||||||
Persistence.RecoverOrInit(window);
|
RecoverOrInitWindowSize();
|
||||||
UpdateImmersiveDarkMode(options.TitleBar, default!);
|
UpdateImmersiveDarkMode(options.TitleBar, default!);
|
||||||
|
|
||||||
// appWindow.Show(true);
|
// appWindow.Show(true);
|
||||||
@@ -81,12 +69,47 @@ internal sealed class ExtendedWindow<TWindow> : IRecipient<FlyoutStateChangedMes
|
|||||||
|
|
||||||
subclass.Initialize();
|
subclass.Initialize();
|
||||||
|
|
||||||
serviceProvider.GetRequiredService<IMessenger>().Register(this);
|
|
||||||
|
|
||||||
window.Closed += OnWindowClosed;
|
window.Closed += OnWindowClosed;
|
||||||
options.TitleBar.ActualThemeChanged += UpdateImmersiveDarkMode;
|
options.TitleBar.ActualThemeChanged += UpdateImmersiveDarkMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RecoverOrInitWindowSize()
|
||||||
|
{
|
||||||
|
// Set first launch size
|
||||||
|
double scale = options.GetWindowScale();
|
||||||
|
SizeInt32 transformedSize = options.InitSize.Scale(scale);
|
||||||
|
RectInt32 rect = StructMarshal.RectInt32(transformedSize);
|
||||||
|
|
||||||
|
if (options.PersistSize)
|
||||||
|
{
|
||||||
|
RectInt32 persistedRect = (CompactRect)LocalSetting.Get(SettingKeys.WindowRect, (CompactRect)rect);
|
||||||
|
if (persistedRect.Size() >= options.InitSize.Size())
|
||||||
|
{
|
||||||
|
rect = persistedRect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TransformToCenterScreen(ref rect);
|
||||||
|
window.AppWindow.MoveAndResize(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveOrSkipWindowSize()
|
||||||
|
{
|
||||||
|
if (!options.PersistSize)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WINDOWPLACEMENT windowPlacement = StructMarshal.WINDOWPLACEMENT();
|
||||||
|
GetWindowPlacement(options.Hwnd, ref windowPlacement);
|
||||||
|
|
||||||
|
// prevent save value when we are maximized.
|
||||||
|
if (!windowPlacement.showCmd.HasFlag(SHOW_WINDOW_CMD.SW_SHOWMAXIMIZED))
|
||||||
|
{
|
||||||
|
LocalSetting.Set(SettingKeys.WindowRect, (CompactRect)window.AppWindow.GetRect());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnOptionsPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
private void OnOptionsPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.PropertyName == nameof(AppOptions.BackdropType))
|
if (e.PropertyName == nameof(AppOptions.BackdropType))
|
||||||
@@ -98,17 +121,12 @@ internal sealed class ExtendedWindow<TWindow> : IRecipient<FlyoutStateChangedMes
|
|||||||
|
|
||||||
private void OnWindowClosed(object sender, WindowEventArgs args)
|
private void OnWindowClosed(object sender, WindowEventArgs args)
|
||||||
{
|
{
|
||||||
if (window.WindowOptions.PersistSize)
|
SaveOrSkipWindowSize();
|
||||||
{
|
|
||||||
Persistence.Save(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
subclass?.Dispose();
|
subclass?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExtendsContentIntoTitleBar()
|
private void ExtendsContentIntoTitleBar()
|
||||||
{
|
{
|
||||||
WindowOptions options = window.WindowOptions;
|
|
||||||
if (options.UseLegacyDragBarImplementation)
|
if (options.UseLegacyDragBarImplementation)
|
||||||
{
|
{
|
||||||
// use normal Window method to extend.
|
// use normal Window method to extend.
|
||||||
@@ -168,31 +186,22 @@ internal sealed class ExtendedWindow<TWindow> : IRecipient<FlyoutStateChangedMes
|
|||||||
private unsafe void UpdateImmersiveDarkMode(FrameworkElement titleBar, object discard)
|
private unsafe void UpdateImmersiveDarkMode(FrameworkElement titleBar, object discard)
|
||||||
{
|
{
|
||||||
BOOL isDarkMode = Control.Theme.ThemeHelper.IsDarkMode(titleBar.ActualTheme);
|
BOOL isDarkMode = Control.Theme.ThemeHelper.IsDarkMode(titleBar.ActualTheme);
|
||||||
DwmSetWindowAttribute(window.WindowOptions.Hwnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, &isDarkMode, unchecked((uint)sizeof(BOOL)));
|
DwmSetWindowAttribute(options.Hwnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, &isDarkMode, unchecked((uint)sizeof(BOOL)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateDragRectangles(bool isFlyoutOpened = false)
|
private void UpdateDragRectangles()
|
||||||
{
|
{
|
||||||
AppWindowTitleBar appTitleBar = window.AppWindow.TitleBar;
|
AppWindowTitleBar appTitleBar = window.AppWindow.TitleBar;
|
||||||
|
|
||||||
if (isFlyoutOpened)
|
double scale = options.GetWindowScale();
|
||||||
{
|
|
||||||
// set to 0
|
|
||||||
appTitleBar.SetDragRectangles(default(RectInt32).ToArray());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WindowOptions options = window.WindowOptions;
|
|
||||||
double scale = options.GetWindowScale();
|
|
||||||
|
|
||||||
// 48 is the navigation button leftInset
|
// 48 is the navigation button leftInset
|
||||||
RectInt32 dragRect = StructMarshal.RectInt32(48, 0, options.TitleBar.ActualSize).Scale(scale);
|
RectInt32 dragRect = StructMarshal.RectInt32(48, 0, options.TitleBar.ActualSize).Scale(scale);
|
||||||
appTitleBar.SetDragRectangles(dragRect.ToArray());
|
appTitleBar.SetDragRectangles(dragRect.ToArray());
|
||||||
|
|
||||||
// workaround for https://github.com/microsoft/WindowsAppSDK/issues/2976
|
// workaround for https://github.com/microsoft/WindowsAppSDK/issues/2976
|
||||||
SizeInt32 size = window.AppWindow.ClientSize;
|
SizeInt32 size = window.AppWindow.ClientSize;
|
||||||
size.Height -= (int)(31 * scale);
|
size.Height -= (int)(31 * scale);
|
||||||
window.AppWindow.ResizeClient(size);
|
window.AppWindow.ResizeClient(size);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
19
src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowExtension.cs
Normal file
19
src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowExtension.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.Windowing;
|
||||||
|
|
||||||
|
internal static class WindowExtension
|
||||||
|
{
|
||||||
|
private static readonly ConditionalWeakTable<Window, WindowController> WindowControllers = new();
|
||||||
|
|
||||||
|
public static void InitializeController<TWindow>(this TWindow window, IServiceProvider serviceProvider)
|
||||||
|
where TWindow : Window, IWindowOptionsSource
|
||||||
|
{
|
||||||
|
WindowController windowController = new(window, window.WindowOptions, serviceProvider);
|
||||||
|
WindowControllers.Add(window, windowController);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,27 +12,23 @@ namespace Snap.Hutao.Core.Windowing;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 窗体子类管理器
|
/// 窗体子类管理器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TWindow">窗体类型</typeparam>
|
|
||||||
[HighQuality]
|
[HighQuality]
|
||||||
internal sealed class WindowSubclass<TWindow> : IDisposable
|
internal sealed class WindowSubclass : IDisposable
|
||||||
where TWindow : Window, IWindowOptionsSource
|
|
||||||
{
|
{
|
||||||
private const int WindowSubclassId = 101;
|
private const int WindowSubclassId = 101;
|
||||||
private const int DragBarSubclassId = 102;
|
private const int DragBarSubclassId = 102;
|
||||||
|
|
||||||
private readonly TWindow window;
|
private readonly Window window;
|
||||||
|
private readonly WindowOptions options;
|
||||||
|
|
||||||
// We have to explicitly hold a reference to SUBCLASSPROC
|
// We have to explicitly hold a reference to SUBCLASSPROC
|
||||||
private SUBCLASSPROC? windowProc;
|
private SUBCLASSPROC? windowProc;
|
||||||
private SUBCLASSPROC? legacyDragBarProc;
|
private SUBCLASSPROC? legacyDragBarProc;
|
||||||
|
|
||||||
/// <summary>
|
public WindowSubclass(Window window, in WindowOptions options)
|
||||||
/// 构造一个新的窗体子类管理器
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="window">窗口</param>
|
|
||||||
public WindowSubclass(TWindow window)
|
|
||||||
{
|
{
|
||||||
this.window = window;
|
this.window = window;
|
||||||
|
this.options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -41,10 +37,9 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
|
|||||||
/// <returns>是否设置成功</returns>
|
/// <returns>是否设置成功</returns>
|
||||||
public bool Initialize()
|
public bool Initialize()
|
||||||
{
|
{
|
||||||
WindowOptions options = window.WindowOptions;
|
|
||||||
|
|
||||||
windowProc = OnSubclassProcedure;
|
windowProc = OnSubclassProcedure;
|
||||||
bool windowHooked = SetWindowSubclass(options.Hwnd, windowProc, WindowSubclassId, 0);
|
bool windowHooked = SetWindowSubclass(options.Hwnd, windowProc, WindowSubclassId, 0);
|
||||||
|
HotKey.Register(options.Hwnd);
|
||||||
|
|
||||||
bool titleBarHooked = true;
|
bool titleBarHooked = true;
|
||||||
|
|
||||||
@@ -71,8 +66,6 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
WindowOptions options = window.WindowOptions;
|
|
||||||
|
|
||||||
RemoveWindowSubclass(options.Hwnd, windowProc, WindowSubclassId);
|
RemoveWindowSubclass(options.Hwnd, windowProc, WindowSubclassId);
|
||||||
windowProc = null;
|
windowProc = null;
|
||||||
|
|
||||||
@@ -81,6 +74,7 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HotKey.Unregister(options.Hwnd);
|
||||||
RemoveWindowSubclass(options.Hwnd, legacyDragBarProc, DragBarSubclassId);
|
RemoveWindowSubclass(options.Hwnd, legacyDragBarProc, DragBarSubclassId);
|
||||||
legacyDragBarProc = null;
|
legacyDragBarProc = null;
|
||||||
}
|
}
|
||||||
@@ -94,7 +88,7 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
|
|||||||
{
|
{
|
||||||
uint dpi = GetDpiForWindow(hwnd);
|
uint dpi = GetDpiForWindow(hwnd);
|
||||||
double scalingFactor = Math.Round(dpi / 96D, 2, MidpointRounding.AwayFromZero);
|
double scalingFactor = Math.Round(dpi / 96D, 2, MidpointRounding.AwayFromZero);
|
||||||
window.ProcessMinMaxInfo((MINMAXINFO*)lParam.Value, scalingFactor);
|
((IWindowOptionsSource)window).ProcessMinMaxInfo((MINMAXINFO*)lParam.Value, scalingFactor);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +97,12 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
|
|||||||
{
|
{
|
||||||
return (LRESULT)(nint)WM_NULL;
|
return (LRESULT)(nint)WM_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case WM_HOTKEY:
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"Hot key pressed, wParam: {wParam.Value}, lParam: {lParam.Value}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ internal sealed partial class GuideWindow : Window, IWindowOptionsSource
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
windowOptions = new(this, DragableGrid, new(MinWidth, MinHeight));
|
windowOptions = new(this, DragableGrid, new(MinWidth, MinHeight));
|
||||||
ExtendedWindow<GuideWindow>.Initialize(this, Ioc.Default);
|
this.InitializeController(serviceProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowOptions IWindowOptionsSource.WindowOptions { get => windowOptions; }
|
WindowOptions IWindowOptionsSource.WindowOptions { get => windowOptions; }
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ internal sealed partial class LaunchGameWindow : Window, IDisposable, IWindowOpt
|
|||||||
|
|
||||||
scope = serviceProvider.CreateScope();
|
scope = serviceProvider.CreateScope();
|
||||||
windowOptions = new(this, DragableGrid, new(MaxWidth, MaxHeight));
|
windowOptions = new(this, DragableGrid, new(MaxWidth, MaxHeight));
|
||||||
ExtendedWindow<LaunchGameWindow>.Initialize(this, scope.ServiceProvider);
|
this.InitializeController(serviceProvider);
|
||||||
RootGrid.DataContext = scope.ServiceProvider.GetRequiredService<LaunchGameViewModel>();
|
RootGrid.DataContext = scope.ServiceProvider.GetRequiredService<LaunchGameViewModel>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ internal sealed partial class MainWindow : Window, IWindowOptionsSource
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
windowOptions = new(this, TitleBarView.DragArea, new(1200, 741), true);
|
windowOptions = new(this, TitleBarView.DragArea, new(1200, 741), true);
|
||||||
ExtendedWindow<MainWindow>.Initialize(this, serviceProvider);
|
this.InitializeController(serviceProvider);
|
||||||
logger = serviceProvider.GetRequiredService<ILogger<MainWindow>>();
|
logger = serviceProvider.GetRequiredService<ILogger<MainWindow>>();
|
||||||
|
|
||||||
closedEventHander = OnClosed;
|
closedEventHander = OnClosed;
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Message;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Flyout开启关闭消息
|
|
||||||
/// </summary>
|
|
||||||
[HighQuality]
|
|
||||||
internal sealed class FlyoutStateChangedMessage
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 构造一个新的Flyout开启关闭消息
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="isOpen">是否为开启状态</param>
|
|
||||||
public FlyoutStateChangedMessage(bool isOpen)
|
|
||||||
{
|
|
||||||
IsOpen = isOpen;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FlyoutStateChangedMessage Open { get; } = new(true);
|
|
||||||
|
|
||||||
public static FlyoutStateChangedMessage Close { get; } = new(false);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否为开启状态
|
|
||||||
/// </summary>
|
|
||||||
public bool IsOpen { get; }
|
|
||||||
}
|
|
||||||
@@ -39,7 +39,10 @@ GetForegroundWindow
|
|||||||
GetWindowPlacement
|
GetWindowPlacement
|
||||||
GetWindowThreadProcessId
|
GetWindowThreadProcessId
|
||||||
ReleaseDC
|
ReleaseDC
|
||||||
|
RegisterHotKey
|
||||||
|
SendInput
|
||||||
SetForegroundWindow
|
SetForegroundWindow
|
||||||
|
UnregisterHotKey
|
||||||
|
|
||||||
// COM
|
// COM
|
||||||
IPersistFile
|
IPersistFile
|
||||||
@@ -53,6 +56,7 @@ IMemoryBufferByteAccess
|
|||||||
// Const value
|
// Const value
|
||||||
INFINITE
|
INFINITE
|
||||||
WM_GETMINMAXINFO
|
WM_GETMINMAXINFO
|
||||||
|
WM_HOTKEY
|
||||||
WM_NCRBUTTONDOWN
|
WM_NCRBUTTONDOWN
|
||||||
WM_NCRBUTTONUP
|
WM_NCRBUTTONUP
|
||||||
WM_NULL
|
WM_NULL
|
||||||
|
|||||||
Reference in New Issue
Block a user