mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
add simplified win32 implementation
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
<Style x:Key="SettingButtonStyle" TargetType="Button" BasedOn="{StaticResource DefaultButtonStyle}" >
|
<Style x:Key="SettingButtonStyle" TargetType="Button" BasedOn="{StaticResource DefaultButtonStyle}" >
|
||||||
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}" />
|
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}" />
|
||||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
|
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
|
||||||
<Setter Property="Padding" Value="16,4,16,4" />
|
<Setter Property="Padding" Value="16,5,16,6" />
|
||||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
|||||||
10
src/Snap.Hutao/Snap.Hutao.Win32/Foundation/POINT.cs
Normal file
10
src/Snap.Hutao/Snap.Hutao.Win32/Foundation/POINT.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Windows.Win32.Foundation;
|
||||||
|
|
||||||
|
public partial struct POINT
|
||||||
|
{
|
||||||
|
public POINT(int x,int y)
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/Snap.Hutao/Snap.Hutao.Win32/Foundation/RECT.cs
Normal file
19
src/Snap.Hutao/Snap.Hutao.Win32/Foundation/RECT.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
namespace Windows.Win32.Foundation;
|
||||||
|
public partial struct RECT
|
||||||
|
{
|
||||||
|
public RECT(int left, int top, int right, int bottom)
|
||||||
|
{
|
||||||
|
this.left = left;
|
||||||
|
this.top = top;
|
||||||
|
this.right = right;
|
||||||
|
this.bottom = bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Size
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (right - left) * (bottom - top);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Windows.Win32.Foundation;
|
||||||
|
|
||||||
|
namespace Windows.Win32.UI.WindowsAndMessaging;
|
||||||
|
public partial struct WINDOWPLACEMENT
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the default (empty) value.
|
||||||
|
/// </summary>
|
||||||
|
public static WINDOWPLACEMENT Default
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new WINDOWPLACEMENT()
|
||||||
|
{
|
||||||
|
length = (uint)Marshal.SizeOf<WINDOWPLACEMENT>(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的<see cref="WINDOWPLACEMENT"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptMaxPosition">最大点</param>
|
||||||
|
/// <param name="rcNormalPosition">正常位置</param>
|
||||||
|
/// <param name="showCmd">显示命令</param>
|
||||||
|
/// <returns>窗体位置</returns>
|
||||||
|
public static WINDOWPLACEMENT Create(POINT ptMaxPosition, RECT rcNormalPosition, SHOW_WINDOW_CMD showCmd)
|
||||||
|
{
|
||||||
|
WINDOWPLACEMENT result = Default;
|
||||||
|
|
||||||
|
result.ptMaxPosition = ptMaxPosition;
|
||||||
|
result.rcNormalPosition = rcNormalPosition;
|
||||||
|
result.showCmd = showCmd;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/Snap.Hutao/Snap.Hutao.Win32/Unsafe.cs
Normal file
18
src/Snap.Hutao/Snap.Hutao.Win32/Unsafe.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using Windows.Win32.UI.WindowsAndMessaging;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Win32;
|
||||||
|
public class Unsafe
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 使用指针操作简化封送
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lPARAM"></param>
|
||||||
|
/// <param name="minWidth"></param>
|
||||||
|
/// <param name="minHeight"></param>
|
||||||
|
public static unsafe void SetMinTrackSize(nint lPARAM, float minWidth, float minHeight)
|
||||||
|
{
|
||||||
|
MINMAXINFO* rect2 = (MINMAXINFO*)lPARAM;
|
||||||
|
rect2->ptMinTrackSize.x = (int)Math.Max(minWidth, rect2->ptMinTrackSize.x);
|
||||||
|
rect2->ptMinTrackSize.y = (int)Math.Max(minHeight, rect2->ptMinTrackSize.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,6 @@
|
|||||||
<CornerRadius x:Key="CompatCornerRadiusTop">6,6,0,0</CornerRadius>
|
<CornerRadius x:Key="CompatCornerRadiusTop">6,6,0,0</CornerRadius>
|
||||||
<CornerRadius x:Key="CompatCornerRadiusRight">0,6,6,0</CornerRadius>
|
<CornerRadius x:Key="CompatCornerRadiusRight">0,6,6,0</CornerRadius>
|
||||||
<CornerRadius x:Key="CompatCornerRadiusBottom">0,0,6,6</CornerRadius>
|
<CornerRadius x:Key="CompatCornerRadiusBottom">0,0,6,6</CornerRadius>
|
||||||
<CornerRadius x:Key="SmallCompatCornerRadius">4</CornerRadius>
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
</Application>
|
</Application>
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using CommunityToolkit.WinUI.UI.Behaviors;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
using Microsoft.UI.Xaml.Shapes;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Control.Behavior;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Make ContentDialog's SmokeLayerBackground dsiplay over custom titleBar
|
||||||
|
/// </summary>
|
||||||
|
public class ContentDialogBehavior : BehaviorBase<FrameworkElement>
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void OnAssociatedObjectLoaded()
|
||||||
|
{
|
||||||
|
DependencyObject parent = VisualTreeHelper.GetParent(AssociatedObject);
|
||||||
|
DependencyObject child = VisualTreeHelper.GetChild(parent, 2);
|
||||||
|
Rectangle smokeLayerBackground = (Rectangle)child;
|
||||||
|
|
||||||
|
smokeLayerBackground.Margin = new Thickness(0);
|
||||||
|
smokeLayerBackground.RegisterPropertyChangedCallback(FrameworkElement.MarginProperty, OnMarginChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnMarginChanged(DependencyObject sender, DependencyProperty property)
|
||||||
|
{
|
||||||
|
if (property == FrameworkElement.MarginProperty)
|
||||||
|
{
|
||||||
|
sender.ClearValue(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -78,4 +78,9 @@ internal static class EventIds
|
|||||||
/// 背景状态
|
/// 背景状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly EventId BackdropState = 200001;
|
public static readonly EventId BackdropState = 200001;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 子类控制
|
||||||
|
/// </summary>
|
||||||
|
public static readonly EventId SubClassing = 200002;
|
||||||
}
|
}
|
||||||
@@ -5,30 +5,30 @@ using Microsoft.UI.Xaml;
|
|||||||
using Snap.Hutao.Control.HostBackdrop;
|
using Snap.Hutao.Control.HostBackdrop;
|
||||||
using Snap.Hutao.Core.Logging;
|
using Snap.Hutao.Core.Logging;
|
||||||
using Snap.Hutao.Core.Setting;
|
using Snap.Hutao.Core.Setting;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Windows.Win32;
|
|
||||||
using Windows.Win32.Foundation;
|
using Windows.Win32.Foundation;
|
||||||
using Windows.Win32.UI.Shell;
|
using Windows.Win32.UI.Shell;
|
||||||
using Windows.Win32.UI.WindowsAndMessaging;
|
using Windows.Win32.UI.WindowsAndMessaging;
|
||||||
using WinRT.Interop;
|
using WinRT.Interop;
|
||||||
|
|
||||||
|
using static Windows.Win32.PInvoke;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core;
|
namespace Snap.Hutao.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 窗口状态管理器
|
/// 窗口管理器
|
||||||
/// 主要包含了各类 P/Inoke 代码
|
/// 主要包含了针对窗体的 P/Inoke 逻辑
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class WindowManager
|
internal class WindowManager
|
||||||
{
|
{
|
||||||
private const int MinWidth = 800;
|
private const int MinWidth = 848;
|
||||||
private const int MinHeight = 600;
|
private const int MinHeight = 524;
|
||||||
|
private const int SubclassId = 101;
|
||||||
private readonly HWND handle;
|
private readonly HWND handle;
|
||||||
private readonly Window window;
|
private readonly Window window;
|
||||||
private readonly UIElement titleBar;
|
private readonly UIElement titleBar;
|
||||||
private readonly ILogger<WindowManager> logger;
|
private readonly ILogger<WindowManager> logger;
|
||||||
|
|
||||||
// We have to explictly hold a reference to the SUBCLASSPROC
|
// We have to explictly hold a reference to the SUBCLASSPROC,
|
||||||
// otherwise will casuse System.ExecutionEngineException
|
// otherwise will casuse System.ExecutionEngineException
|
||||||
private SUBCLASSPROC? subClassProc;
|
private SUBCLASSPROC? subClassProc;
|
||||||
|
|
||||||
@@ -53,17 +53,14 @@ internal class WindowManager
|
|||||||
int right = LocalSetting.GetValueType<int>(SettingKeys.WindowRight);
|
int right = LocalSetting.GetValueType<int>(SettingKeys.WindowRight);
|
||||||
int bottom = LocalSetting.GetValueType<int>(SettingKeys.WindowBottom);
|
int bottom = LocalSetting.GetValueType<int>(SettingKeys.WindowBottom);
|
||||||
|
|
||||||
return new() { left = left, top = top, right = right, bottom = bottom };
|
return new(left, top, right, bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SaveWindowRect(HWND handle)
|
private static void SaveWindowRect(HWND handle)
|
||||||
{
|
{
|
||||||
WINDOWPLACEMENT windowPlacement = new()
|
WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Default;
|
||||||
{
|
|
||||||
length = (uint)Marshal.SizeOf<WINDOWPLACEMENT>(),
|
|
||||||
};
|
|
||||||
|
|
||||||
PInvoke.GetWindowPlacement(handle, ref windowPlacement);
|
GetWindowPlacement(handle, ref windowPlacement);
|
||||||
|
|
||||||
LocalSetting.Set(SettingKeys.WindowLeft, windowPlacement.rcNormalPosition.left);
|
LocalSetting.Set(SettingKeys.WindowLeft, windowPlacement.rcNormalPosition.left);
|
||||||
LocalSetting.Set(SettingKeys.WindowTop, windowPlacement.rcNormalPosition.top);
|
LocalSetting.Set(SettingKeys.WindowTop, windowPlacement.rcNormalPosition.top);
|
||||||
@@ -77,52 +74,42 @@ internal class WindowManager
|
|||||||
window.SetTitleBar(titleBar);
|
window.SetTitleBar(titleBar);
|
||||||
window.Closed += OnWindowClosed;
|
window.Closed += OnWindowClosed;
|
||||||
|
|
||||||
PInvoke.SetWindowText(handle, "胡桃");
|
SetWindowText(handle, "胡桃");
|
||||||
RECT rect = RetriveWindowRect();
|
RECT rect = RetriveWindowRect();
|
||||||
if ((rect.right - rect.left) * (rect.bottom - rect.top) > 0)
|
if (rect.Size > 0)
|
||||||
{
|
{
|
||||||
WINDOWPLACEMENT windowPlacement = new()
|
WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Create(new(-1, -1), rect, SHOW_WINDOW_CMD.SW_SHOWNORMAL);
|
||||||
{
|
SetWindowPlacement(handle, in windowPlacement);
|
||||||
length = (uint)Marshal.SizeOf<WINDOWPLACEMENT>(),
|
|
||||||
showCmd = SHOW_WINDOW_CMD.SW_SHOWNORMAL,
|
|
||||||
ptMaxPosition = new() { x = -1, y = -1 },
|
|
||||||
rcNormalPosition = rect,
|
|
||||||
};
|
|
||||||
|
|
||||||
PInvoke.SetWindowPlacement(handle, in windowPlacement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool micaApplied = new SystemBackdrop(window).TrySetBackdrop();
|
bool micaApplied = new SystemBackdrop(window).TrySetBackdrop();
|
||||||
logger.LogInformation(EventIds.BackdropState, "Apply {name} : {result}", nameof(SystemBackdrop), micaApplied ? "succeed" : "failed");
|
logger.LogInformation(EventIds.BackdropState, "Apply {name} : {result}", nameof(SystemBackdrop), micaApplied ? "succeed" : "failed");
|
||||||
|
|
||||||
subClassProc = new(OnWindowProcedure);
|
subClassProc = new(OnSubclassProcedure);
|
||||||
_ = PInvoke.SetWindowSubclass(handle, subClassProc, 101, 0);
|
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)
|
private void OnWindowClosed(object sender, WindowEventArgs args)
|
||||||
{
|
{
|
||||||
PInvoke.RemoveWindowSubclass(handle, subClassProc, 101);
|
RemoveWindowSubclass(handle, subClassProc, SubclassId);
|
||||||
subClassProc = null;
|
subClassProc = null;
|
||||||
SaveWindowRect(handle);
|
SaveWindowRect(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LRESULT OnWindowProcedure(HWND hwnd, uint uMsg, WPARAM wParam, LPARAM lParam, nuint uIdSubclass, nuint dwRefData)
|
private LRESULT OnSubclassProcedure(HWND hwnd, uint uMsg, WPARAM wParam, LPARAM lParam, nuint uIdSubclass, nuint dwRefData)
|
||||||
{
|
{
|
||||||
switch (uMsg)
|
switch (uMsg)
|
||||||
{
|
{
|
||||||
case PInvoke.WM_GETMINMAXINFO:
|
case WM_GETMINMAXINFO:
|
||||||
{
|
{
|
||||||
uint dpi = PInvoke.GetDpiForWindow(handle);
|
uint dpi = GetDpiForWindow(handle);
|
||||||
float scalingFactor = dpi / 96f;
|
float scalingFactor = dpi / 96f;
|
||||||
|
Win32.Unsafe.SetMinTrackSize(lParam, MinWidth * scalingFactor, MinHeight * scalingFactor);
|
||||||
MINMAXINFO minMaxInfo = Marshal.PtrToStructure<MINMAXINFO>(lParam);
|
|
||||||
minMaxInfo.ptMinTrackSize.x = (int)Math.Max(MinWidth * scalingFactor, minMaxInfo.ptMinTrackSize.x);
|
|
||||||
minMaxInfo.ptMinTrackSize.y = (int)Math.Max(MinHeight * scalingFactor, minMaxInfo.ptMinTrackSize.y);
|
|
||||||
Marshal.StructureToPtr(minMaxInfo, lParam, true);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PInvoke.DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
<ContentDialog
|
<ContentDialog
|
||||||
x:Class="Snap.Hutao.View.Dialog.UserDialog"
|
x:Class="Snap.Hutao.View.Dialog.UserDialog"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:local="using:Snap.Hutao.View.Dialog"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
||||||
xmlns:settings="using:SettingsUI.Controls"
|
xmlns:settings="using:SettingsUI.Controls"
|
||||||
|
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
IsPrimaryButtonEnabled="False"
|
IsPrimaryButtonEnabled="False"
|
||||||
Title="设置米游社Cookie"
|
Title="设置米游社Cookie"
|
||||||
@@ -13,7 +14,10 @@
|
|||||||
PrimaryButtonText="请输入Cookie"
|
PrimaryButtonText="请输入Cookie"
|
||||||
CloseButtonText="取消"
|
CloseButtonText="取消"
|
||||||
Style="{StaticResource DefaultContentDialogStyle}">
|
Style="{StaticResource DefaultContentDialogStyle}">
|
||||||
|
<mxi:Interaction.Behaviors>
|
||||||
|
<shcb:ContentDialogBehavior/>
|
||||||
|
</mxi:Interaction.Behaviors>
|
||||||
|
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBox
|
<TextBox
|
||||||
Margin="0,0,0,8"
|
Margin="0,0,0,8"
|
||||||
@@ -22,16 +26,16 @@
|
|||||||
PlaceholderText="在此处输入"
|
PlaceholderText="在此处输入"
|
||||||
VerticalAlignment="Top"/>
|
VerticalAlignment="Top"/>
|
||||||
<settings:Setting
|
<settings:Setting
|
||||||
Margin="0,8,0,0"
|
Margin="0,8,0,0"
|
||||||
Icon=""
|
Icon=""
|
||||||
Header="手动获取"
|
Header="手动获取"
|
||||||
Description="进入我们的文档页面并按指示操作"
|
Description="进入我们的文档页面并按指示操作"
|
||||||
HorizontalAlignment="Stretch">
|
HorizontalAlignment="Stretch">
|
||||||
<HyperlinkButton
|
<HyperlinkButton
|
||||||
Margin="12,0,0,0"
|
Margin="12,0,0,0"
|
||||||
Padding="4"
|
Padding="4"
|
||||||
Content="立即前往"
|
Content="立即前往"
|
||||||
NavigateUri="https://www.snapgenshin.com/documents/features/mhy-account-switch.html#%E5%A6%82%E4%BD%95%E8%8E%B7%E5%8F%96-cookie"/>
|
NavigateUri="https://www.snapgenshin.com/documents/features/mhy-account-switch.html#%E5%A6%82%E4%BD%95%E8%8E%B7%E5%8F%96-cookie"/>
|
||||||
</settings:Setting>
|
</settings:Setting>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ContentDialog>
|
</ContentDialog>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// 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 Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Snap.Hutao.Core.Threading;
|
using Snap.Hutao.Core.Threading;
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ public sealed partial class UserDialog : ContentDialog
|
|||||||
/// 构造一个新的添加用户对话框
|
/// 构造一个新的添加用户对话框
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="window">呈现的父窗口</param>
|
/// <param name="window">呈现的父窗口</param>
|
||||||
public UserDialog(Microsoft.UI.Xaml.Window window)
|
public UserDialog(Window window)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
XamlRoot = window.Content.XamlRoot;
|
XamlRoot = window.Content.XamlRoot;
|
||||||
|
|||||||
@@ -29,6 +29,6 @@ public sealed partial class MainView : UserControl
|
|||||||
navigationService = Ioc.Default.GetRequiredService<INavigationService>();
|
navigationService = Ioc.Default.GetRequiredService<INavigationService>();
|
||||||
navigationService.Initialize(NavView, ContentFrame);
|
navigationService.Initialize(NavView, ContentFrame);
|
||||||
|
|
||||||
//navigationService.Navigate<AnnouncementPage>(INavigationAwaiter.Default, true);
|
navigationService.Navigate<AnnouncementPage>(INavigationAwaiter.Default, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user