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}" >
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}" />
|
||||
<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="HorizontalContentAlignment" Value="Center"/>
|
||||
</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="CompatCornerRadiusRight">0,6,6,0</CornerRadius>
|
||||
<CornerRadius x:Key="CompatCornerRadiusBottom">0,0,6,6</CornerRadius>
|
||||
<CornerRadius x:Key="SmallCompatCornerRadius">4</CornerRadius>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</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>
|
||||
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.Core.Logging;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32;
|
||||
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 代码
|
||||
/// 窗口管理器
|
||||
/// 主要包含了针对窗体的 P/Inoke 逻辑
|
||||
/// </summary>
|
||||
internal class WindowManager
|
||||
{
|
||||
private const int MinWidth = 800;
|
||||
private const int MinHeight = 600;
|
||||
|
||||
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 UIElement titleBar;
|
||||
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
|
||||
private SUBCLASSPROC? subClassProc;
|
||||
|
||||
@@ -53,17 +53,14 @@ internal class WindowManager
|
||||
int right = LocalSetting.GetValueType<int>(SettingKeys.WindowRight);
|
||||
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)
|
||||
{
|
||||
WINDOWPLACEMENT windowPlacement = new()
|
||||
{
|
||||
length = (uint)Marshal.SizeOf<WINDOWPLACEMENT>(),
|
||||
};
|
||||
WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Default;
|
||||
|
||||
PInvoke.GetWindowPlacement(handle, ref windowPlacement);
|
||||
GetWindowPlacement(handle, ref windowPlacement);
|
||||
|
||||
LocalSetting.Set(SettingKeys.WindowLeft, windowPlacement.rcNormalPosition.left);
|
||||
LocalSetting.Set(SettingKeys.WindowTop, windowPlacement.rcNormalPosition.top);
|
||||
@@ -77,52 +74,42 @@ internal class WindowManager
|
||||
window.SetTitleBar(titleBar);
|
||||
window.Closed += OnWindowClosed;
|
||||
|
||||
PInvoke.SetWindowText(handle, "胡桃");
|
||||
SetWindowText(handle, "胡桃");
|
||||
RECT rect = RetriveWindowRect();
|
||||
if ((rect.right - rect.left) * (rect.bottom - rect.top) > 0)
|
||||
if (rect.Size > 0)
|
||||
{
|
||||
WINDOWPLACEMENT windowPlacement = new()
|
||||
{
|
||||
length = (uint)Marshal.SizeOf<WINDOWPLACEMENT>(),
|
||||
showCmd = SHOW_WINDOW_CMD.SW_SHOWNORMAL,
|
||||
ptMaxPosition = new() { x = -1, y = -1 },
|
||||
rcNormalPosition = rect,
|
||||
};
|
||||
|
||||
PInvoke.SetWindowPlacement(handle, in windowPlacement);
|
||||
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(OnWindowProcedure);
|
||||
_ = PInvoke.SetWindowSubclass(handle, subClassProc, 101, 0);
|
||||
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)
|
||||
{
|
||||
PInvoke.RemoveWindowSubclass(handle, subClassProc, 101);
|
||||
RemoveWindowSubclass(handle, subClassProc, SubclassId);
|
||||
subClassProc = null;
|
||||
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)
|
||||
{
|
||||
case PInvoke.WM_GETMINMAXINFO:
|
||||
case WM_GETMINMAXINFO:
|
||||
{
|
||||
uint dpi = PInvoke.GetDpiForWindow(handle);
|
||||
uint dpi = GetDpiForWindow(handle);
|
||||
float scalingFactor = dpi / 96f;
|
||||
|
||||
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);
|
||||
Win32.Unsafe.SetMinTrackSize(lParam, MinWidth * scalingFactor, MinHeight * scalingFactor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return PInvoke.DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
||||
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
<ContentDialog
|
||||
x:Class="Snap.Hutao.View.Dialog.UserDialog"
|
||||
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:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:settings="using:SettingsUI.Controls"
|
||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||
mc:Ignorable="d"
|
||||
IsPrimaryButtonEnabled="False"
|
||||
Title="设置米游社Cookie"
|
||||
@@ -13,7 +14,10 @@
|
||||
PrimaryButtonText="请输入Cookie"
|
||||
CloseButtonText="取消"
|
||||
Style="{StaticResource DefaultContentDialogStyle}">
|
||||
|
||||
<mxi:Interaction.Behaviors>
|
||||
<shcb:ContentDialogBehavior/>
|
||||
</mxi:Interaction.Behaviors>
|
||||
|
||||
<StackPanel>
|
||||
<TextBox
|
||||
Margin="0,0,0,8"
|
||||
@@ -22,16 +26,16 @@
|
||||
PlaceholderText="在此处输入"
|
||||
VerticalAlignment="Top"/>
|
||||
<settings:Setting
|
||||
Margin="0,8,0,0"
|
||||
Icon=""
|
||||
Header="手动获取"
|
||||
Description="进入我们的文档页面并按指示操作"
|
||||
HorizontalAlignment="Stretch">
|
||||
Margin="0,8,0,0"
|
||||
Icon=""
|
||||
Header="手动获取"
|
||||
Description="进入我们的文档页面并按指示操作"
|
||||
HorizontalAlignment="Stretch">
|
||||
<HyperlinkButton
|
||||
Margin="12,0,0,0"
|
||||
Padding="4"
|
||||
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"/>
|
||||
Margin="12,0,0,0"
|
||||
Padding="4"
|
||||
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"/>
|
||||
</settings:Setting>
|
||||
</StackPanel>
|
||||
</ContentDialog>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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.Threading;
|
||||
|
||||
@@ -15,7 +16,7 @@ public sealed partial class UserDialog : ContentDialog
|
||||
/// 构造一个新的添加用户对话框
|
||||
/// </summary>
|
||||
/// <param name="window">呈现的父窗口</param>
|
||||
public UserDialog(Microsoft.UI.Xaml.Window window)
|
||||
public UserDialog(Window window)
|
||||
{
|
||||
InitializeComponent();
|
||||
XamlRoot = window.Content.XamlRoot;
|
||||
|
||||
@@ -29,6 +29,6 @@ public sealed partial class MainView : UserControl
|
||||
navigationService = Ioc.Default.GetRequiredService<INavigationService>();
|
||||
navigationService.Initialize(NavView, ContentFrame);
|
||||
|
||||
//navigationService.Navigate<AnnouncementPage>(INavigationAwaiter.Default, true);
|
||||
navigationService.Navigate<AnnouncementPage>(INavigationAwaiter.Default, true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user