refactor xaml window controller

This commit is contained in:
Lightczx
2024-05-15 17:13:59 +08:00
parent f4593cd325
commit 6edcf97ec9
17 changed files with 322 additions and 245 deletions

View File

@@ -0,0 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
namespace Snap.Hutao.Core.Windowing.Abstraction;
internal interface IXamlWindowContentAsFrameworkElement
{
FrameworkElement ContentAccess { get; }
}

View File

@@ -0,0 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
namespace Snap.Hutao.Core.Windowing.Abstraction;
internal interface IXamlWindowExtendContentIntoTitleBar
{
FrameworkElement TitleBarAccess { get; }
}

View File

@@ -0,0 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Windows.Graphics;
namespace Snap.Hutao.Core.Windowing.Abstraction;
internal interface IXamlWindowHasInitSize
{
SizeInt32 InitSize { get; }
}

View File

@@ -1,7 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Core.Windowing;
namespace Snap.Hutao.Core.Windowing.Abstraction;
/// <summary>
/// 为扩展窗体提供必要的选项

View File

@@ -0,0 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Core.Windowing.Abstraction;
internal interface IXamlWindowRectPersisted : IXamlWindowHasInitSize
{
string PersistRectKey { get; }
}

View File

@@ -0,0 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
namespace Snap.Hutao.Core.Windowing.Abstraction;
internal interface IXamlWindowSubclassMinMaxInfoHandler
{
unsafe void HandleMinMaxInfo(ref MINMAXINFO info, double scalingFactor);
}

View File

@@ -1,16 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
namespace Snap.Hutao.Core.Windowing;
internal interface IMinMaxInfoHandler
{
/// <summary>
/// 处理最大最小信息
/// </summary>
/// <param name="info">信息</param>
/// <param name="scalingFactor">缩放比</param>
unsafe void HandleMinMaxInfo(ref MINMAXINFO info, double scalingFactor);
}

View File

@@ -23,7 +23,7 @@ internal sealed class NotifyIconXamlHostWindow : Window, IDisposable, IWindowNee
{
Content = new Border();
this.SetLayeredWindow();
this.SetLayered();
AppWindow.Title = "SnapHutaoNotifyIconXamlHost";
AppWindow.IsShownInSwitchers = false;
@@ -36,8 +36,7 @@ internal sealed class NotifyIconXamlHostWindow : Window, IDisposable, IWindowNee
presenter.SetBorderAndTitleBar(false, false);
}
XamlWindowOptions options = new(this, default!, default);
subclass = new(this, options);
subclass = new(this);
subclass.Initialize();
Activate();

View File

@@ -1,8 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Hosting;
using Snap.Hutao.Core.Windowing.Abstraction;
using Snap.Hutao.Core.Windowing.Backdrop;
using Snap.Hutao.Win32.Foundation;
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
@@ -18,9 +20,9 @@ internal static class WindowExtension
private static readonly ConditionalWeakTable<Window, XamlWindowController> WindowControllers = [];
public static void InitializeController<TWindow>(this TWindow window, IServiceProvider serviceProvider)
where TWindow : Window, IXamlWindowOptionsSource
where TWindow : Window
{
XamlWindowController windowController = new(window, window.WindowOptions, serviceProvider);
XamlWindowController windowController = new(window, serviceProvider);
WindowControllers.Add(window, windowController);
}
@@ -30,25 +32,6 @@ internal static class WindowExtension
return WindowControllers.TryGetValue(window, out _);
}
public static void SetLayeredWindow(this Window window)
{
HWND hwnd = (HWND)WindowNative.GetWindowHandle(window);
nint style = GetWindowLongPtrW(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
style |= (nint)WINDOW_EX_STYLE.WS_EX_LAYERED;
SetWindowLongPtrW(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, style);
SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_COLORKEY | LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_ALPHA);
}
public static void Show(this Window window)
{
ShowWindow(GetWindowHandle(window), SHOW_WINDOW_CMD.SW_NORMAL);
}
public static void Hide(this Window window)
{
ShowWindow(GetWindowHandle(window), SHOW_WINDOW_CMD.SW_HIDE);
}
public static DesktopWindowXamlSource? GetDesktopWindowXamlSource(this Window window)
{
if (window.SystemBackdrop is SystemBackdropDesktopWindowXamlSourceAccess access)
@@ -59,10 +42,64 @@ internal static class WindowExtension
return default;
}
public static InputNonClientPointerSource GetInputNonClientPointerSource(this Window window)
{
return InputNonClientPointerSource.GetForWindowId(window.AppWindow.Id);
}
public static HWND GetWindowHandle(this Window? window)
{
return window is IXamlWindowOptionsSource optionsSource
? optionsSource.WindowOptions.Hwnd
: WindowNative.GetWindowHandle(window);
return WindowNative.GetWindowHandle(window);
}
public static void Show(this Window window)
{
ShowWindow(GetWindowHandle(window), SHOW_WINDOW_CMD.SW_NORMAL);
}
public static void Hide(this Window window)
{
ShowWindow(GetWindowHandle(window), SHOW_WINDOW_CMD.SW_HIDE);
}
public static void SetLayered(this Window window)
{
HWND hwnd = (HWND)WindowNative.GetWindowHandle(window);
nint style = GetWindowLongPtrW(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
style |= (nint)WINDOW_EX_STYLE.WS_EX_LAYERED;
SetWindowLongPtrW(hwnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, style);
SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_COLORKEY | LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_ALPHA);
}
public static unsafe void BringToForeground(this Window window)
{
HWND fgHwnd = GetForegroundWindow();
HWND hwnd = window.GetWindowHandle();
uint threadIdHwnd = GetWindowThreadProcessId(hwnd, default);
uint threadIdFgHwnd = GetWindowThreadProcessId(fgHwnd, default);
if (threadIdHwnd != threadIdFgHwnd)
{
AttachThreadInput(threadIdHwnd, threadIdFgHwnd, true);
SetForegroundWindow(hwnd);
AttachThreadInput(threadIdHwnd, threadIdFgHwnd, false);
}
else
{
SetForegroundWindow(hwnd);
}
}
public static double GetRasterizationScale(this Window window)
{
// TODO: test this
if (window.Content is not null)
{
return window.Content.RasterizationScale;
}
uint dpi = GetDpiForWindow(window.GetWindowHandle());
return Math.Round(dpi / 96D, 2, MidpointRounding.AwayFromZero);
}
}

View File

@@ -3,11 +3,13 @@
using Microsoft.UI;
using Microsoft.UI.Composition.SystemBackdrops;
using Microsoft.UI.Content;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Snap.Hutao.Core.LifeCycle;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Core.Windowing.Abstraction;
using Snap.Hutao.Service;
using Snap.Hutao.Win32;
using Snap.Hutao.Win32.Foundation;
@@ -22,38 +24,27 @@ using static Snap.Hutao.Win32.User32;
namespace Snap.Hutao.Core.Windowing;
[SuppressMessage("", "CA1001")]
[SuppressMessage("", "SA1124")]
[SuppressMessage("", "SA1204")]
internal sealed class XamlWindowController
{
private readonly Window window;
private readonly XamlWindowOptions options;
private readonly IServiceProvider serviceProvider;
private readonly XamlWindowSubclass subclass;
private readonly XamlWindowNonRudeHWND windowNonRudeHWND;
public XamlWindowController(Window window, in XamlWindowOptions options, IServiceProvider serviceProvider)
public XamlWindowController(Window window, IServiceProvider serviceProvider)
{
this.window = window;
this.options = options;
this.serviceProvider = serviceProvider;
subclass = new(window, options);
windowNonRudeHWND = new(options.Hwnd);
// Subclassing and NonRudeHWND are standard infrastructure.
subclass = new(window);
windowNonRudeHWND = new(window.GetWindowHandle());
InitializeCore();
}
private static void TransformToCenterScreen(ref RectInt32 rect)
{
DisplayArea displayArea = DisplayArea.GetFromRect(rect, DisplayAreaFallback.Nearest);
RectInt32 workAreaRect = displayArea.WorkArea;
rect.Width = Math.Min(workAreaRect.Width, rect.Width);
rect.Height = Math.Min(workAreaRect.Height, rect.Height);
rect.X = workAreaRect.X + ((workAreaRect.Width - rect.Width) / 2);
rect.Y = workAreaRect.Y + ((workAreaRect.Height - rect.Height) / 2);
}
private void InitializeCore()
{
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
@@ -61,76 +52,46 @@ internal sealed class XamlWindowController
window.AppWindow.Title = SH.FormatAppNameAndVersion(runtimeOptions.Version);
window.AppWindow.SetIcon(Path.Combine(runtimeOptions.InstalledLocation, "Assets/Logo.ico"));
ExtendsContentIntoTitleBar();
RecoverOrInitWindowSize();
UpdateElementTheme(appOptions.ElementTheme);
UpdateImmersiveDarkMode(options.TitleBar, default!);
// ExtendContentIntoTitleBar
if (window is IXamlWindowExtendContentIntoTitleBar xamlWindow)
{
ExtendsContentIntoTitleBar(window, xamlWindow);
}
// Size stuff
if (window is IXamlWindowHasInitSize xamlWindow2)
{
RecoverOrInitWindowSize(xamlWindow2);
}
// Element Theme & Immersive Dark
UpdateElementTheme(window, appOptions.ElementTheme);
if (window is IXamlWindowContentAsFrameworkElement xamlWindow3)
{
UpdateImmersiveDarkMode(xamlWindow3.ContentAccess, default!);
xamlWindow3.ContentAccess.ActualThemeChanged += UpdateImmersiveDarkMode;
}
// appWindow.Show(true);
// appWindow.Show can't bring window to top.
window.Activate();
options.BringToForeground();
window.BringToForeground();
// SystemBackdrop
UpdateSystemBackdrop(appOptions.BackdropType);
if (window.GetDesktopWindowXamlSource() is { } desktopWindowXamlSource)
{
DesktopChildSiteBridge desktopChildSiteBridge = desktopWindowXamlSource.SiteBridge;
desktopChildSiteBridge.ResizePolicy = ContentSizePolicy.ResizeContentToParentWindow;
}
appOptions.PropertyChanged += OnOptionsPropertyChanged;
subclass.Initialize();
window.Closed += OnWindowClosed;
options.TitleBar.ActualThemeChanged += UpdateImmersiveDarkMode;
}
private void RecoverOrInitWindowSize()
{
// Set first launch size
double scale = options.GetRasterizationScale();
SizeInt32 scaledSize = options.InitSize.Scale(scale);
RectInt32 rect = StructMarshal.RectInt32(scaledSize);
if (!string.IsNullOrEmpty(options.PersistRectKey))
{
RectInt32 persistedRect = (CompactRect)LocalSetting.Get(options.PersistRectKey, (CompactRect)rect);
if (persistedRect.Size() >= options.InitSize.Size())
{
rect = persistedRect.Scale(scale);
}
}
TransformToCenterScreen(ref rect);
window.AppWindow.MoveAndResize(rect);
}
private void SaveOrSkipWindowSize()
{
if (string.IsNullOrEmpty(options.PersistRectKey))
{
return;
}
WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Create();
GetWindowPlacement(options.Hwnd, ref windowPlacement);
// prevent save value when we are maximized.
if (!windowPlacement.ShowCmd.HasFlag(SHOW_WINDOW_CMD.SW_SHOWMAXIMIZED))
{
double scale = 1.0 / options.GetRasterizationScale();
LocalSetting.Set(options.PersistRectKey, (CompactRect)window.AppWindow.GetRect().Scale(scale));
}
}
private void OnOptionsPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (sender is not AppOptions options)
{
return;
}
_ = e.PropertyName switch
{
nameof(AppOptions.BackdropType) => UpdateSystemBackdrop(options.BackdropType),
nameof(AppOptions.ElementTheme) => UpdateElementTheme(options.ElementTheme),
_ => false,
};
}
private void OnWindowClosed(object sender, WindowEventArgs args)
@@ -150,22 +111,31 @@ internal sealed class XamlWindowController
}
else
{
SaveOrSkipWindowSize();
if (window is IXamlWindowRectPersisted rectPersisted)
{
SaveOrSkipWindowSize(rectPersisted);
}
subclass?.Dispose();
windowNonRudeHWND?.Dispose();
}
}
private void ExtendsContentIntoTitleBar()
{
AppWindowTitleBar appTitleBar = window.AppWindow.TitleBar;
appTitleBar.IconShowOptions = IconShowOptions.HideIconAndSystemMenu;
appTitleBar.ExtendsContentIntoTitleBar = true;
#region SystemBackdrop & ElementTheme
UpdateTitleButtonColor();
UpdateDragRectangles();
options.TitleBar.ActualThemeChanged += (_, _) => UpdateTitleButtonColor();
options.TitleBar.SizeChanged += (_, _) => UpdateDragRectangles();
private void OnOptionsPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (sender is not AppOptions options)
{
return;
}
_ = e.PropertyName switch
{
nameof(AppOptions.BackdropType) => UpdateSystemBackdrop(options.BackdropType),
nameof(AppOptions.ElementTheme) => UpdateElementTheme(window, options.ElementTheme),
_ => false,
};
}
private bool UpdateSystemBackdrop(BackdropType backdropType)
@@ -184,21 +154,108 @@ internal sealed class XamlWindowController
return true;
}
private bool UpdateElementTheme(ElementTheme theme)
private static bool UpdateElementTheme(Window window, ElementTheme theme)
{
((FrameworkElement)window.Content).RequestedTheme = theme;
if (window is IXamlWindowContentAsFrameworkElement xamlWindow)
{
xamlWindow.ContentAccess.RequestedTheme = theme;
return true;
}
return true;
if (window.Content is FrameworkElement frameworkElement)
{
frameworkElement.RequestedTheme = theme;
return true;
}
return false;
}
#endregion
#region IXamlWindowContentAsFrameworkElement
private unsafe void UpdateImmersiveDarkMode(FrameworkElement titleBar, object discard)
{
BOOL isDarkMode = Control.Theme.ThemeHelper.IsDarkMode(titleBar.ActualTheme);
DwmSetWindowAttribute(window.GetWindowHandle(), DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, ref isDarkMode);
}
#endregion
#region IXamlWindowHasInitSize & IXamlWindowRectPersisted
private static void TransformToCenterScreen(ref RectInt32 rect)
{
DisplayArea displayArea = DisplayArea.GetFromRect(rect, DisplayAreaFallback.Nearest);
RectInt32 workAreaRect = displayArea.WorkArea;
rect.Width = Math.Min(workAreaRect.Width, rect.Width);
rect.Height = Math.Min(workAreaRect.Height, rect.Height);
rect.X = workAreaRect.X + ((workAreaRect.Width - rect.Width) / 2);
rect.Y = workAreaRect.Y + ((workAreaRect.Height - rect.Height) / 2);
}
private void RecoverOrInitWindowSize(IXamlWindowHasInitSize xamlWindow)
{
double scale = window.GetRasterizationScale();
SizeInt32 scaledSize = xamlWindow.InitSize.Scale(scale);
RectInt32 rect = StructMarshal.RectInt32(scaledSize);
if (window is IXamlWindowRectPersisted rectPersisted)
{
RectInt32 persistedRect = (CompactRect)LocalSetting.Get(rectPersisted.PersistRectKey, (CompactRect)rect);
if (persistedRect.Size() >= xamlWindow.InitSize.Size())
{
rect = persistedRect.Scale(scale);
}
}
TransformToCenterScreen(ref rect);
window.AppWindow.MoveAndResize(rect);
}
private void SaveOrSkipWindowSize(IXamlWindowRectPersisted rectPersisted)
{
WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Create();
GetWindowPlacement(window.GetWindowHandle(), ref windowPlacement);
// prevent save value when we are maximized.
if (!windowPlacement.ShowCmd.HasFlag(SHOW_WINDOW_CMD.SW_SHOWMAXIMIZED))
{
double scale = 1.0 / window.GetRasterizationScale();
LocalSetting.Set(rectPersisted.PersistRectKey, (CompactRect)window.AppWindow.GetRect().Scale(scale));
}
}
#endregion
#region IXamlWindowExtendContentIntoTitleBar
private void ExtendsContentIntoTitleBar(Window window, IXamlWindowExtendContentIntoTitleBar xamlWindow)
{
AppWindowTitleBar appTitleBar = window.AppWindow.TitleBar;
appTitleBar.IconShowOptions = IconShowOptions.HideIconAndSystemMenu;
appTitleBar.ExtendsContentIntoTitleBar = true;
UpdateTitleButtonColor();
xamlWindow.TitleBarAccess.ActualThemeChanged += (_, _) => UpdateTitleButtonColor();
UpdateDragRectangles();
xamlWindow.TitleBarAccess.SizeChanged += (_, _) => UpdateDragRectangles();
}
private void UpdateTitleButtonColor()
{
if (window is not IXamlWindowExtendContentIntoTitleBar xamlWindow)
{
return;
}
AppWindowTitleBar appTitleBar = window.AppWindow.TitleBar;
appTitleBar.ButtonBackgroundColor = Colors.Transparent;
appTitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
bool isDarkMode = Control.Theme.ThemeHelper.IsDarkMode(options.TitleBar.ActualTheme);
bool isDarkMode = Control.Theme.ThemeHelper.IsDarkMode(xamlWindow.TitleBarAccess.ActualTheme);
Color systemBaseLowColor = Control.Theme.SystemColors.BaseLowColor(isDarkMode);
appTitleBar.ButtonHoverBackgroundColor = systemBaseLowColor;
@@ -217,20 +274,20 @@ internal sealed class XamlWindowController
appTitleBar.ButtonPressedForegroundColor = systemBaseHighColor;
}
private unsafe void UpdateImmersiveDarkMode(FrameworkElement titleBar, object discard)
{
BOOL isDarkMode = Control.Theme.ThemeHelper.IsDarkMode(titleBar.ActualTheme);
DwmSetWindowAttribute(options.Hwnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, ref isDarkMode);
}
private void UpdateDragRectangles()
{
if (window is not IXamlWindowExtendContentIntoTitleBar xamlWindow)
{
return;
}
AppWindowTitleBar appTitleBar = window.AppWindow.TitleBar;
double scale = options.GetRasterizationScale();
double scale = window.GetRasterizationScale();
// 48 is the navigation button leftInset
RectInt32 dragRect = StructMarshal.RectInt32(48, 0, options.TitleBar.ActualSize).Scale(scale);
RectInt32 dragRect = StructMarshal.RectInt32(48, 0, xamlWindow.TitleBarAccess.ActualSize).Scale(scale);
appTitleBar.SetDragRectangles([dragRect]);
}
#endregion
}

View File

@@ -1,87 +1,22 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
using Snap.Hutao.Win32.Foundation;
using Windows.Graphics;
using WinRT.Interop;
using static Snap.Hutao.Win32.User32;
namespace Snap.Hutao.Core.Windowing;
/// <summary>
/// Window 选项
/// </summary>
internal readonly struct XamlWindowOptions
internal sealed class XamlWindowOptions
{
/// <summary>
/// 窗体句柄
/// </summary>
public readonly HWND Hwnd;
/// <summary>
/// 非客户端区域指针源
/// </summary>
public readonly InputNonClientPointerSource InputNonClientPointerSource;
/// <summary>
/// 标题栏元素
/// </summary>
public readonly FrameworkElement TitleBar;
/// <summary>
/// 初始大小
/// </summary>
public readonly SizeInt32 InitSize;
/// <summary>
/// 是否持久化尺寸
/// </summary>
[Obsolete]
public readonly bool PersistSize;
public readonly string? PersistRectKey;
public XamlWindowOptions(Window window, FrameworkElement titleBar, SizeInt32 initSize, string? persistSize = default)
{
Hwnd = WindowNative.GetWindowHandle(window);
InputNonClientPointerSource = InputNonClientPointerSource.GetForWindowId(window.AppWindow.Id);
TitleBar = titleBar;
InitSize = initSize;
PersistRectKey = persistSize;
}
/// <summary>
/// 获取窗体当前的DPI缩放比
/// </summary>
/// <returns>缩放比</returns>
public double GetRasterizationScale()
{
uint dpi = GetDpiForWindow(Hwnd);
return Math.Round(dpi / 96D, 2, MidpointRounding.AwayFromZero);
}
public SizeInt32 InitSize { get; }
/// <summary>
/// 将窗口设为前台窗口
/// </summary>
/// <param name="hwnd">窗口句柄</param>
public unsafe void BringToForeground()
{
HWND fgHwnd = GetForegroundWindow();
uint threadIdHwnd = GetWindowThreadProcessId(Hwnd, default);
uint threadIdFgHwnd = GetWindowThreadProcessId(fgHwnd, default);
if (threadIdHwnd != threadIdFgHwnd)
{
AttachThreadInput(threadIdHwnd, threadIdFgHwnd, true);
SetForegroundWindow(Hwnd);
AttachThreadInput(threadIdHwnd, threadIdFgHwnd, false);
}
else
{
SetForegroundWindow(Hwnd);
}
}
public string? PersistRectKey { get; }
}

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Snap.Hutao.Core.Windowing.Abstraction;
using Snap.Hutao.Core.Windowing.Backdrop;
using Snap.Hutao.Core.Windowing.NotifyIcon;
using Snap.Hutao.Win32;
@@ -22,28 +23,28 @@ internal sealed class XamlWindowSubclass : IDisposable
private const int WindowSubclassId = 101;
private readonly Window window;
private readonly XamlWindowOptions options;
private readonly HWND hwnd;
// We have to explicitly hold a reference to SUBCLASSPROC
private SUBCLASSPROC windowProc = default!;
private UnmanagedAccess<XamlWindowSubclass> unmanagedAccess = default!;
public XamlWindowSubclass(Window window, in XamlWindowOptions options)
public XamlWindowSubclass(Window window)
{
this.window = window;
this.options = options;
hwnd = window.GetWindowHandle();
}
public unsafe bool Initialize()
{
windowProc = SUBCLASSPROC.Create(&OnSubclassProcedure);
unmanagedAccess = UnmanagedAccess.Create(this);
return SetWindowSubclass(options.Hwnd, windowProc, WindowSubclassId, unmanagedAccess);
return SetWindowSubclass(hwnd, windowProc, WindowSubclassId, unmanagedAccess);
}
public void Dispose()
{
RemoveWindowSubclass(options.Hwnd, windowProc, WindowSubclassId);
RemoveWindowSubclass(hwnd, windowProc, WindowSubclassId);
windowProc = default!;
unmanagedAccess.Dispose();
}
@@ -59,9 +60,9 @@ internal sealed class XamlWindowSubclass : IDisposable
{
case WM_GETMINMAXINFO:
{
if (state.window is IMinMaxInfoHandler handler)
if (state.window is IXamlWindowSubclassMinMaxInfoHandler handler)
{
handler.HandleMinMaxInfo(ref *(MINMAXINFO*)lParam, state.options.GetRasterizationScale());
handler.HandleMinMaxInfo(ref *(MINMAXINFO*)lParam, state.window.GetRasterizationScale());
}
break;

View File

@@ -4,7 +4,9 @@
using Microsoft.UI.Xaml;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Core.Windowing;
using Snap.Hutao.Core.Windowing.Abstraction;
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
using Windows.Graphics;
namespace Snap.Hutao;
@@ -12,7 +14,10 @@ namespace Snap.Hutao;
/// 指引窗口
/// </summary>
[Injection(InjectAs.Singleton)]
internal sealed partial class GuideWindow : Window, IXamlWindowOptionsSource, IMinMaxInfoHandler
internal sealed partial class GuideWindow : Window,
IXamlWindowExtendContentIntoTitleBar,
IXamlWindowRectPersisted,
IXamlWindowSubclassMinMaxInfoHandler
{
private const int MinWidth = 1000;
private const int MinHeight = 650;
@@ -20,16 +25,17 @@ internal sealed partial class GuideWindow : Window, IXamlWindowOptionsSource, IM
private const int MaxWidth = 1200;
private const int MaxHeight = 800;
private readonly XamlWindowOptions windowOptions;
public GuideWindow(IServiceProvider serviceProvider)
{
InitializeComponent();
windowOptions = new(this, DragableGrid, new(MinWidth, MinHeight), SettingKeys.GuideWindowRect);
this.InitializeController(serviceProvider);
}
XamlWindowOptions IXamlWindowOptionsSource.WindowOptions { get => windowOptions; }
public FrameworkElement TitleBarAccess { get => DragableGrid; }
public string PersistRectKey { get => SettingKeys.GuideWindowRect; }
public SizeInt32 InitSize { get; } = new(MinWidth, MinHeight);
public unsafe void HandleMinMaxInfo(ref MINMAXINFO info, double scalingFactor)
{

View File

@@ -3,10 +3,11 @@
using Microsoft.UI.Xaml;
using Snap.Hutao.Control.Extension;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Core.Windowing;
using Snap.Hutao.Core.Windowing.Abstraction;
using Snap.Hutao.ViewModel.Game;
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
using Windows.Graphics;
namespace Snap.Hutao;
@@ -15,7 +16,11 @@ namespace Snap.Hutao;
/// </summary>
[HighQuality]
[Injection(InjectAs.Singleton)]
internal sealed partial class LaunchGameWindow : Window, IDisposable, IXamlWindowOptionsSource, IMinMaxInfoHandler
internal sealed partial class LaunchGameWindow : Window,
IDisposable,
IXamlWindowExtendContentIntoTitleBar,
IXamlWindowHasInitSize,
IXamlWindowSubclassMinMaxInfoHandler
{
private const int MinWidth = 240;
private const int MinHeight = 240;
@@ -23,7 +28,6 @@ internal sealed partial class LaunchGameWindow : Window, IDisposable, IXamlWindo
private const int MaxWidth = 320;
private const int MaxHeight = 320;
private readonly XamlWindowOptions windowOptions;
private readonly IServiceScope scope;
/// <summary>
@@ -35,13 +39,14 @@ internal sealed partial class LaunchGameWindow : Window, IDisposable, IXamlWindo
InitializeComponent();
scope = serviceProvider.CreateScope();
windowOptions = new(this, DragableGrid, new(MaxWidth, MaxHeight));
this.InitializeController(serviceProvider);
RootGrid.InitializeDataContext<LaunchGameViewModel>(scope.ServiceProvider);
}
/// <inheritdoc/>
public XamlWindowOptions WindowOptions { get => windowOptions; }
public FrameworkElement TitleBarAccess { get => DragableGrid; }
public SizeInt32 InitSize { get; } = new(MaxWidth, MaxHeight);
/// <inheritdoc/>
public void Dispose()

View File

@@ -5,7 +5,9 @@ using Microsoft.UI.Content;
using Microsoft.UI.Xaml;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Core.Windowing;
using Snap.Hutao.Core.Windowing.Abstraction;
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
using Windows.Graphics;
namespace Snap.Hutao;
@@ -14,13 +16,14 @@ namespace Snap.Hutao;
/// </summary>
[HighQuality]
[Injection(InjectAs.Singleton)]
internal sealed partial class MainWindow : Window, IXamlWindowOptionsSource, IMinMaxInfoHandler
internal sealed partial class MainWindow : Window,
IXamlWindowExtendContentIntoTitleBar,
IXamlWindowRectPersisted,
IXamlWindowSubclassMinMaxInfoHandler
{
private const int MinWidth = 1000;
private const int MinHeight = 600;
private readonly XamlWindowOptions windowOptions;
/// <summary>
/// 构造一个新的主窗体
/// </summary>
@@ -28,18 +31,14 @@ internal sealed partial class MainWindow : Window, IXamlWindowOptionsSource, IMi
public MainWindow(IServiceProvider serviceProvider)
{
InitializeComponent();
windowOptions = new(this, TitleBarView.DragArea, new(1200, 741), SettingKeys.WindowRect);
this.InitializeController(serviceProvider);
if (this.GetDesktopWindowXamlSource() is { } desktopWindowXamlSource)
{
DesktopChildSiteBridge desktopChildSiteBridge = desktopWindowXamlSource.SiteBridge;
desktopChildSiteBridge.ResizePolicy = ContentSizePolicy.ResizeContentToParentWindow;
}
}
/// <inheritdoc/>
public XamlWindowOptions WindowOptions { get => windowOptions; }
public FrameworkElement TitleBarAccess { get => TitleBarView.DragArea; }
public string PersistRectKey { get => SettingKeys.WindowRect; }
public SizeInt32 InitSize { get; } = new(1200, 741);
/// <inheritdoc/>
public unsafe void HandleMinMaxInfo(ref MINMAXINFO pInfo, double scalingFactor)

View File

@@ -49,7 +49,7 @@ internal sealed partial class NotifyIconViewModel : ObservableObject
{
// MainWindow is activated, bring to foreground
mainWindow.Show();
mainWindow.WindowOptions.BringToForeground();
mainWindow.BringToForeground();
return;
}
@@ -61,14 +61,14 @@ internal sealed partial class NotifyIconViewModel : ObservableObject
// TODO: Can actually be no any window is initialized
mainWindow.Show();
mainWindow.WindowOptions.BringToForeground();
mainWindow.BringToForeground();
break;
}
case Window otherWindow:
{
otherWindow.Show();
(otherWindow as IXamlWindowOptionsSource)?.WindowOptions.BringToForeground();
otherWindow.BringToForeground();
return;
}
}

View File

@@ -7,6 +7,7 @@ using Snap.Hutao.Core.Caching;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.LifeCycle;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Core.Windowing;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.ViewModel.Guide;
using Snap.Hutao.Web.Hutao.HutaoAsAService;
@@ -125,7 +126,7 @@ internal sealed partial class TestViewModel : Abstraction.ViewModel
{
if (serviceProvider.GetRequiredService<ICurrentXamlWindowReference>().Window is MainWindow mainWindow)
{
double scale = mainWindow.WindowOptions.GetRasterizationScale();
double scale = mainWindow.GetRasterizationScale();
mainWindow.AppWindow.Resize(new Windows.Graphics.SizeInt32(1372, 772).Scale(scale));
}
}