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.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Composition.SystemBackdrops;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Snap.Hutao.Message;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Service;
|
||||
using Snap.Hutao.Win32;
|
||||
using System.IO;
|
||||
@@ -15,59 +14,48 @@ using Windows.Graphics;
|
||||
using Windows.UI;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Graphics.Dwm;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
using static Windows.Win32.PInvoke;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing;
|
||||
|
||||
/// <summary>
|
||||
/// 扩展窗口
|
||||
/// </summary>
|
||||
/// <typeparam name="TWindow">窗体类型</typeparam>
|
||||
[SuppressMessage("", "CA1001")]
|
||||
internal sealed class ExtendedWindow<TWindow> : IRecipient<FlyoutStateChangedMessage>
|
||||
where TWindow : Window, IWindowOptionsSource
|
||||
internal sealed class WindowController
|
||||
{
|
||||
private readonly TWindow window;
|
||||
private readonly Window window;
|
||||
private readonly WindowOptions options;
|
||||
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.options = options;
|
||||
this.serviceProvider = serviceProvider;
|
||||
|
||||
subclass = new(window);
|
||||
subclass = new(window, options);
|
||||
|
||||
InitializeWindow();
|
||||
InitializeCore();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <param name="window">窗口</param>
|
||||
/// <param name="serviceProvider">服务提供器</param>
|
||||
/// <returns>实例</returns>
|
||||
public static ExtendedWindow<TWindow> Initialize(TWindow window, IServiceProvider serviceProvider)
|
||||
private static void TransformToCenterScreen(ref RectInt32 rect)
|
||||
{
|
||||
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/>
|
||||
public void Receive(FlyoutStateChangedMessage message)
|
||||
{
|
||||
UpdateDragRectangles(message.IsOpen);
|
||||
}
|
||||
|
||||
private void InitializeWindow()
|
||||
private void InitializeCore()
|
||||
{
|
||||
RuntimeOptions hutaoOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
|
||||
WindowOptions options = window.WindowOptions;
|
||||
window.AppWindow.Title = SH.AppNameAndVersion.Format(hutaoOptions.Version);
|
||||
window.AppWindow.SetIcon(Path.Combine(hutaoOptions.InstalledLocation, "Assets/Logo.ico"));
|
||||
ExtendsContentIntoTitleBar();
|
||||
|
||||
Persistence.RecoverOrInit(window);
|
||||
RecoverOrInitWindowSize();
|
||||
UpdateImmersiveDarkMode(options.TitleBar, default!);
|
||||
|
||||
// appWindow.Show(true);
|
||||
@@ -81,12 +69,47 @@ internal sealed class ExtendedWindow<TWindow> : IRecipient<FlyoutStateChangedMes
|
||||
|
||||
subclass.Initialize();
|
||||
|
||||
serviceProvider.GetRequiredService<IMessenger>().Register(this);
|
||||
|
||||
window.Closed += OnWindowClosed;
|
||||
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)
|
||||
{
|
||||
if (e.PropertyName == nameof(AppOptions.BackdropType))
|
||||
@@ -98,17 +121,12 @@ internal sealed class ExtendedWindow<TWindow> : IRecipient<FlyoutStateChangedMes
|
||||
|
||||
private void OnWindowClosed(object sender, WindowEventArgs args)
|
||||
{
|
||||
if (window.WindowOptions.PersistSize)
|
||||
{
|
||||
Persistence.Save(window);
|
||||
}
|
||||
|
||||
SaveOrSkipWindowSize();
|
||||
subclass?.Dispose();
|
||||
}
|
||||
|
||||
private void ExtendsContentIntoTitleBar()
|
||||
{
|
||||
WindowOptions options = window.WindowOptions;
|
||||
if (options.UseLegacyDragBarImplementation)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
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;
|
||||
|
||||
if (isFlyoutOpened)
|
||||
{
|
||||
// set to 0
|
||||
appTitleBar.SetDragRectangles(default(RectInt32).ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowOptions options = window.WindowOptions;
|
||||
double scale = options.GetWindowScale();
|
||||
double scale = options.GetWindowScale();
|
||||
|
||||
// 48 is the navigation button leftInset
|
||||
RectInt32 dragRect = StructMarshal.RectInt32(48, 0, options.TitleBar.ActualSize).Scale(scale);
|
||||
appTitleBar.SetDragRectangles(dragRect.ToArray());
|
||||
// 48 is the navigation button leftInset
|
||||
RectInt32 dragRect = StructMarshal.RectInt32(48, 0, options.TitleBar.ActualSize).Scale(scale);
|
||||
appTitleBar.SetDragRectangles(dragRect.ToArray());
|
||||
|
||||
// workaround for https://github.com/microsoft/WindowsAppSDK/issues/2976
|
||||
SizeInt32 size = window.AppWindow.ClientSize;
|
||||
size.Height -= (int)(31 * scale);
|
||||
window.AppWindow.ResizeClient(size);
|
||||
}
|
||||
// workaround for https://github.com/microsoft/WindowsAppSDK/issues/2976
|
||||
SizeInt32 size = window.AppWindow.ClientSize;
|
||||
size.Height -= (int)(31 * scale);
|
||||
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>
|
||||
/// <typeparam name="TWindow">窗体类型</typeparam>
|
||||
[HighQuality]
|
||||
internal sealed class WindowSubclass<TWindow> : IDisposable
|
||||
where TWindow : Window, IWindowOptionsSource
|
||||
internal sealed class WindowSubclass : IDisposable
|
||||
{
|
||||
private const int WindowSubclassId = 101;
|
||||
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
|
||||
private SUBCLASSPROC? windowProc;
|
||||
private SUBCLASSPROC? legacyDragBarProc;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的窗体子类管理器
|
||||
/// </summary>
|
||||
/// <param name="window">窗口</param>
|
||||
public WindowSubclass(TWindow window)
|
||||
public WindowSubclass(Window window, in WindowOptions options)
|
||||
{
|
||||
this.window = window;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -41,10 +37,9 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
|
||||
/// <returns>是否设置成功</returns>
|
||||
public bool Initialize()
|
||||
{
|
||||
WindowOptions options = window.WindowOptions;
|
||||
|
||||
windowProc = OnSubclassProcedure;
|
||||
bool windowHooked = SetWindowSubclass(options.Hwnd, windowProc, WindowSubclassId, 0);
|
||||
HotKey.Register(options.Hwnd);
|
||||
|
||||
bool titleBarHooked = true;
|
||||
|
||||
@@ -71,8 +66,6 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
WindowOptions options = window.WindowOptions;
|
||||
|
||||
RemoveWindowSubclass(options.Hwnd, windowProc, WindowSubclassId);
|
||||
windowProc = null;
|
||||
|
||||
@@ -81,6 +74,7 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
HotKey.Unregister(options.Hwnd);
|
||||
RemoveWindowSubclass(options.Hwnd, legacyDragBarProc, DragBarSubclassId);
|
||||
legacyDragBarProc = null;
|
||||
}
|
||||
@@ -94,7 +88,7 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
|
||||
{
|
||||
uint dpi = GetDpiForWindow(hwnd);
|
||||
double scalingFactor = Math.Round(dpi / 96D, 2, MidpointRounding.AwayFromZero);
|
||||
window.ProcessMinMaxInfo((MINMAXINFO*)lParam.Value, scalingFactor);
|
||||
((IWindowOptionsSource)window).ProcessMinMaxInfo((MINMAXINFO*)lParam.Value, scalingFactor);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -103,6 +97,12 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -25,7 +25,7 @@ internal sealed partial class GuideWindow : Window, IWindowOptionsSource
|
||||
{
|
||||
InitializeComponent();
|
||||
windowOptions = new(this, DragableGrid, new(MinWidth, MinHeight));
|
||||
ExtendedWindow<GuideWindow>.Initialize(this, Ioc.Default);
|
||||
this.InitializeController(serviceProvider);
|
||||
}
|
||||
|
||||
WindowOptions IWindowOptionsSource.WindowOptions { get => windowOptions; }
|
||||
|
||||
@@ -34,7 +34,7 @@ internal sealed partial class LaunchGameWindow : Window, IDisposable, IWindowOpt
|
||||
|
||||
scope = serviceProvider.CreateScope();
|
||||
windowOptions = new(this, DragableGrid, new(MaxWidth, MaxHeight));
|
||||
ExtendedWindow<LaunchGameWindow>.Initialize(this, scope.ServiceProvider);
|
||||
this.InitializeController(serviceProvider);
|
||||
RootGrid.DataContext = scope.ServiceProvider.GetRequiredService<LaunchGameViewModel>();
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ internal sealed partial class MainWindow : Window, IWindowOptionsSource
|
||||
{
|
||||
InitializeComponent();
|
||||
windowOptions = new(this, TitleBarView.DragArea, new(1200, 741), true);
|
||||
ExtendedWindow<MainWindow>.Initialize(this, serviceProvider);
|
||||
this.InitializeController(serviceProvider);
|
||||
logger = serviceProvider.GetRequiredService<ILogger<MainWindow>>();
|
||||
|
||||
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
|
||||
GetWindowThreadProcessId
|
||||
ReleaseDC
|
||||
RegisterHotKey
|
||||
SendInput
|
||||
SetForegroundWindow
|
||||
UnregisterHotKey
|
||||
|
||||
// COM
|
||||
IPersistFile
|
||||
@@ -53,6 +56,7 @@ IMemoryBufferByteAccess
|
||||
// Const value
|
||||
INFINITE
|
||||
WM_GETMINMAXINFO
|
||||
WM_HOTKEY
|
||||
WM_NCRBUTTONDOWN
|
||||
WM_NCRBUTTONUP
|
||||
WM_NULL
|
||||
|
||||
Reference in New Issue
Block a user