mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
use Microsoft.Windows.CsWin32 to replace old p/invoke code
This commit is contained in:
5
src/Snap.Hutao/Snap.Hutao.Win32/NativeMethods.json
Normal file
5
src/Snap.Hutao/Snap.Hutao.Win32/NativeMethods.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://aka.ms/CsWin32.schema.json",
|
||||
"allowMarshaling": true,
|
||||
"public": true
|
||||
}
|
||||
16
src/Snap.Hutao/Snap.Hutao.Win32/NativeMethods.txt
Normal file
16
src/Snap.Hutao/Snap.Hutao.Win32/NativeMethods.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
// ComCtl32
|
||||
SetWindowSubclass
|
||||
RemoveWindowSubclass
|
||||
DefSubclassProc
|
||||
|
||||
// User32
|
||||
SetWindowText
|
||||
GetDpiForWindow
|
||||
GetWindowPlacement
|
||||
SetWindowPlacement
|
||||
|
||||
// Type definition
|
||||
MINMAXINFO
|
||||
|
||||
// Const value
|
||||
WM_GETMINMAXINFO
|
||||
15
src/Snap.Hutao/Snap.Hutao.Win32/Snap.Hutao.Win32.csproj
Normal file
15
src/Snap.Hutao/Snap.Hutao.Win32/Snap.Hutao.Win32.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.10-beta">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -14,6 +14,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SettingsUI", "SettingsUI\Se
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.SourceGeneration", "Snap.Hutao.SourceGeneration\Snap.Hutao.SourceGeneration.csproj", "{8B96721E-5604-47D2-9B72-06FEBAD0CE00}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Snap.Hutao.Win32", "Snap.Hutao.Win32\Snap.Hutao.Win32.csproj", "{29209B14-A6E1-442E-9287-2C65B03C96CD}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -82,6 +84,22 @@ Global
|
||||
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Release|x64.Build.0 = Release|x64
|
||||
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Release|x86.Build.0 = Release|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|arm64.ActiveCfg = Debug|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|arm64.Build.0 = Debug|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|arm64.ActiveCfg = Release|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|arm64.Build.0 = Release|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|x64.Build.0 = Release|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -28,7 +28,6 @@ public partial class App : Application
|
||||
/// </summary>
|
||||
public App()
|
||||
{
|
||||
AppInstance.GetCurrent().Activated += OnActivated;
|
||||
// load app resource
|
||||
InitializeComponent();
|
||||
InitializeDependencyInjection();
|
||||
@@ -79,14 +78,10 @@ public partial class App : Application
|
||||
AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
|
||||
AppInstance firstInstance = AppInstance.FindOrRegisterForKey("main");
|
||||
|
||||
if (!firstInstance.IsCurrent)
|
||||
{
|
||||
// Redirect the activation (and args) to the "main" instance, and exit.
|
||||
await firstInstance.RedirectActivationToAsync(activatedEventArgs);
|
||||
Process.GetCurrentProcess().Kill();
|
||||
}
|
||||
else
|
||||
if (firstInstance.IsCurrent)
|
||||
{
|
||||
firstInstance.Activated += OnActivated;
|
||||
|
||||
Window = Ioc.Default.GetRequiredService<MainWindow>();
|
||||
Window.Activate();
|
||||
|
||||
@@ -94,10 +89,16 @@ public partial class App : Application
|
||||
|
||||
Ioc.Default
|
||||
.GetRequiredService<IMetadataService>()
|
||||
.As<IMetadataInitializer>()?
|
||||
.ImplictAs<IMetadataInitializer>()?
|
||||
.InitializeInternalAsync()
|
||||
.SafeForget(logger);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Redirect the activation (and args) to the "main" instance, and exit.
|
||||
await firstInstance.RedirectActivationToAsync(activatedEventArgs);
|
||||
Process.GetCurrentProcess().Kill();
|
||||
}
|
||||
}
|
||||
|
||||
private static void InitializeDependencyInjection()
|
||||
|
||||
@@ -30,12 +30,6 @@ public class SystemBackdrop
|
||||
this.window = window;
|
||||
}
|
||||
|
||||
private enum BackDropType
|
||||
{
|
||||
None,
|
||||
Mica,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试设置背景
|
||||
/// </summary>
|
||||
@@ -63,8 +57,7 @@ public class SystemBackdrop
|
||||
|
||||
backdropController = new MicaController();
|
||||
|
||||
ICompositionSupportsSystemBackdrop target = window.As<ICompositionSupportsSystemBackdrop>();
|
||||
backdropController.AddSystemBackdropTarget(target);
|
||||
backdropController.AddSystemBackdropTarget(window.As<ICompositionSupportsSystemBackdrop>());
|
||||
backdropController.SetSystemBackdropConfiguration(configurationSource);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -64,18 +64,11 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
||||
/// <param name="storageFile">文件</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>加载的图像表面</returns>
|
||||
protected virtual async Task<LoadedImageSurface?> LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
|
||||
protected virtual async Task<LoadedImageSurface> LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
|
||||
{
|
||||
try
|
||||
using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token))
|
||||
{
|
||||
using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token))
|
||||
{
|
||||
return LoadedImageSurface.StartLoadFromStream(imageStream);
|
||||
}
|
||||
}
|
||||
catch (COMException ex) when (ex.Is(COMError.WINCODEC_ERR_COMPONENTNOTFOUND))
|
||||
{
|
||||
return null;
|
||||
return LoadedImageSurface.StartLoadFromStream(imageStream);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +123,19 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
||||
|
||||
Compositor compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
|
||||
|
||||
if (await LoadImageSurfaceAsync(storageFile, token) is LoadedImageSurface imageSurface)
|
||||
LoadedImageSurface? imageSurface = null;
|
||||
|
||||
try
|
||||
{
|
||||
imageSurface = await LoadImageSurfaceAsync(storageFile, token);
|
||||
}
|
||||
catch (COMException ex) when (ex.Is(COMError.WINCODEC_ERR_COMPONENTNOTFOUND))
|
||||
{
|
||||
// Image is broken, remove it
|
||||
await imageCache.RemoveAsync(uri.Enumerate());
|
||||
}
|
||||
|
||||
if (imageSurface != null)
|
||||
{
|
||||
spriteVisual = CompositeSpriteVisual(compositor, imageSurface);
|
||||
OnUpdateVisual(spriteVisual);
|
||||
@@ -139,11 +144,6 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
||||
|
||||
await AnimationBuilder.Create().Opacity(1d).StartAsync(this, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Image is broken, remove it
|
||||
await imageCache.RemoveAsync(uri.Enumerate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ public class Gradient : CompositionImage
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task<LoadedImageSurface?> LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
|
||||
protected override async Task<LoadedImageSurface> LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
|
||||
{
|
||||
using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token))
|
||||
{
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Core.Win32;
|
||||
|
||||
[SuppressMessage("", "SA1600")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct POINT
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
|
||||
public POINT(int x, int y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
public static implicit operator System.Drawing.Point(POINT p)
|
||||
{
|
||||
return new System.Drawing.Point(p.X, p.Y);
|
||||
}
|
||||
|
||||
public static implicit operator POINT(System.Drawing.Point p)
|
||||
{
|
||||
return new POINT(p.X, p.Y);
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Core.Win32;
|
||||
|
||||
[SuppressMessage("", "SA1132")]
|
||||
[SuppressMessage("", "SA1600")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RECT
|
||||
{
|
||||
public int Left, Top, Right, Bottom;
|
||||
|
||||
public RECT(int left, int top, int right, int bottom)
|
||||
{
|
||||
Left = left;
|
||||
Top = top;
|
||||
Right = right;
|
||||
Bottom = bottom;
|
||||
}
|
||||
|
||||
public RECT(System.Drawing.Rectangle r)
|
||||
: this(r.Left, r.Top, r.Right, r.Bottom)
|
||||
{
|
||||
}
|
||||
|
||||
public int X
|
||||
{
|
||||
get => Left;
|
||||
set
|
||||
{
|
||||
Right -= Left - value;
|
||||
Left = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int Y
|
||||
{
|
||||
get => Top;
|
||||
set
|
||||
{
|
||||
Bottom -= Top - value;
|
||||
Top = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int Height
|
||||
{
|
||||
get => Bottom - Top;
|
||||
set => Bottom = value + Top;
|
||||
}
|
||||
|
||||
public int Width
|
||||
{
|
||||
get => Right - Left;
|
||||
set => Right = value + Left;
|
||||
}
|
||||
|
||||
public long Area
|
||||
{
|
||||
get => Math.BigMul(Width, Height);
|
||||
}
|
||||
|
||||
public System.Drawing.Point Location
|
||||
{
|
||||
get => new(Left, Top);
|
||||
set
|
||||
{
|
||||
X = value.X;
|
||||
Y = value.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public System.Drawing.Size Size
|
||||
{
|
||||
get => new(Width, Height);
|
||||
set
|
||||
{
|
||||
Width = value.Width;
|
||||
Height = value.Height;
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator System.Drawing.Rectangle(RECT r)
|
||||
{
|
||||
return new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height);
|
||||
}
|
||||
|
||||
public static implicit operator RECT(System.Drawing.Rectangle r)
|
||||
{
|
||||
return new RECT(r);
|
||||
}
|
||||
|
||||
public static bool operator ==(RECT r1, RECT r2)
|
||||
{
|
||||
return r1.Equals(r2);
|
||||
}
|
||||
|
||||
public static bool operator !=(RECT r1, RECT r2)
|
||||
{
|
||||
return !r1.Equals(r2);
|
||||
}
|
||||
|
||||
public bool Equals(RECT r)
|
||||
{
|
||||
return r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is RECT rect)
|
||||
{
|
||||
return Equals(rect);
|
||||
}
|
||||
else if (obj is System.Drawing.Rectangle rectangle)
|
||||
{
|
||||
return Equals(new RECT(rectangle));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ((System.Drawing.Rectangle)this).GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, "{{Left={0},Top={1},Right={2},Bottom={3}}}", Left, Top, Right, Bottom);
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Win32;
|
||||
|
||||
[SuppressMessage("", "SA1600")]
|
||||
public enum ShowWindowCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Hides the window and activates another window.
|
||||
/// </summary>
|
||||
Hide = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Activates and displays a window. If the window is minimized or
|
||||
/// maximized, the system restores it to its original size and position.
|
||||
/// An application should specify this flag when displaying the window
|
||||
/// for the first time.
|
||||
/// </summary>
|
||||
Normal = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Activates the window and displays it as a minimized window.
|
||||
/// </summary>
|
||||
ShowMinimized = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Maximizes the specified window.
|
||||
/// </summary>
|
||||
Maximize = 3, // is this the right value?
|
||||
|
||||
/// <summary>
|
||||
/// Activates the window and displays it as a maximized window.
|
||||
/// </summary>
|
||||
ShowMaximized = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Displays a window in its most recent size and position. This value
|
||||
/// is similar to <see cref="Win32.ShowWindowCommand.Normal"/>, except
|
||||
/// the window is not activated.
|
||||
/// </summary>
|
||||
ShowNoActivate = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Activates the window and displays it in its current size and position.
|
||||
/// </summary>
|
||||
Show = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Minimizes the specified window and activates the next top-level
|
||||
/// window in the Z order.
|
||||
/// </summary>
|
||||
Minimize = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Displays the window as a minimized window. This value is similar to
|
||||
/// <see cref="Win32.ShowWindowCommand.ShowMinimized"/>, except the
|
||||
/// window is not activated.
|
||||
/// </summary>
|
||||
ShowMinNoActive = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Displays the window in its current size and position. This value is
|
||||
/// similar to <see cref="Win32.ShowWindowCommand.Show"/>, except the
|
||||
/// window is not activated.
|
||||
/// </summary>
|
||||
ShowNA = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Activates and displays the window. If the window is minimized or
|
||||
/// maximized, the system restores it to its original size and position.
|
||||
/// An application should specify this flag when restoring a minimized window.
|
||||
/// </summary>
|
||||
Restore = 9,
|
||||
|
||||
/// <summary>
|
||||
/// Sets the show state based on the SW_* value specified in the
|
||||
/// STARTUPINFO structure passed to the CreateProcess function by the
|
||||
/// program that started the application.
|
||||
/// </summary>
|
||||
ShowDefault = 10,
|
||||
|
||||
/// <summary>
|
||||
/// <b>Windows 2000/XP:</b> Minimizes a window, even if the thread
|
||||
/// that owns the window is not responding. This flag should only be
|
||||
/// used when minimizing windows from a different thread.
|
||||
/// </summary>
|
||||
ForceMinimize = 11,
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Core.Win32;
|
||||
|
||||
/// <summary>
|
||||
/// 包含 user32.dll 平台调用的代码
|
||||
/// </summary>
|
||||
internal static class User32
|
||||
{
|
||||
[SuppressMessage("", "SA1600")]
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the show state and the restored, minimized, and maximized positions of the specified window.
|
||||
/// </summary>
|
||||
/// <param name="hWnd">
|
||||
/// A handle to the window.
|
||||
/// </param>
|
||||
/// <param name="lpwndpl">
|
||||
/// A pointer to a WINDOWPLACEMENT structure that specifies the new show state and window positions.
|
||||
/// <para>
|
||||
/// Before calling SetWindowPlacement, set the length member of the WINDOWPLACEMENT structure to sizeof(WINDOWPLACEMENT). SetWindowPlacement fails if the length member is not set correctly.
|
||||
/// </para>
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If the function succeeds, the return value is nonzero.
|
||||
/// <para>
|
||||
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.
|
||||
/// </para>
|
||||
/// </returns>
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
|
||||
|
||||
/// <summary>
|
||||
/// Changes the text of the specified window's title bar (if it has one). If the specified window is a control, the
|
||||
/// text of the control is changed. However, SetWindowText cannot change the text of a control in another application.
|
||||
/// <para>
|
||||
/// Go to <see href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms633546%28v=vs.85%29.aspx"/> for more
|
||||
/// information
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="hwnd">C++ ( hWnd [in]. Type: HWND )<br />A handle to the window or control whose text is to be changed.</param>
|
||||
/// <param name="lpString">C++ ( lpString [in, optional]. Type: LPCTSTR )<br />The new title or control text.</param>
|
||||
/// <returns>
|
||||
/// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.<br />
|
||||
/// To get extended error information, call GetLastError.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// If the target window is owned by the current process, <see cref="SetWindowText" /> causes a WM_SETTEXT message to
|
||||
/// be sent to the specified window or control. If the control is a list box control created with the WS_CAPTION style,
|
||||
/// however, <see cref="SetWindowText" /> sets the text for the control, not for the list box entries.<br />To set the
|
||||
/// text of a control in another process, send the WM_SETTEXT message directly instead of calling
|
||||
/// <see cref="SetWindowText" />. The <see cref="SetWindowText" /> function does not expand tab characters (ASCII code
|
||||
/// 0x09). Tab characters are displayed as vertical bar(|) characters.<br />For an example go to
|
||||
/// <see href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms644928%28v=vs.85%29.aspx#sending">Sending a Message. </see>
|
||||
/// </remarks>
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
public static extern bool SetWindowText(IntPtr hwnd, string lpString);
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Core.Win32;
|
||||
|
||||
/// <summary>
|
||||
/// Contains information about the placement of a window on the screen.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct WINDOWPLACEMENT
|
||||
{
|
||||
/// <summary>
|
||||
/// The length of the structure, in bytes. Before calling the GetWindowPlacement or SetWindowPlacement functions, set this member to sizeof(WINDOWPLACEMENT).
|
||||
/// <para>
|
||||
/// GetWindowPlacement and SetWindowPlacement fail if this member is not set correctly.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public int Length;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies flags that control the position of the minimized window and the method by which the window is restored.
|
||||
/// </summary>
|
||||
public int Flags;
|
||||
|
||||
/// <summary>
|
||||
/// The current show state of the window.
|
||||
/// </summary>
|
||||
public ShowWindowCommand ShowCmd;
|
||||
|
||||
/// <summary>
|
||||
/// The coordinates of the window's upper-left corner when the window is minimized.
|
||||
/// </summary>
|
||||
public POINT MinPosition;
|
||||
|
||||
/// <summary>
|
||||
/// The coordinates of the window's upper-left corner when the window is maximized.
|
||||
/// </summary>
|
||||
public POINT MaxPosition;
|
||||
|
||||
/// <summary>
|
||||
/// The window's coordinates when the window is in the restored position.
|
||||
/// </summary>
|
||||
public RECT NormalPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default (empty) value.
|
||||
/// </summary>
|
||||
public static WINDOWPLACEMENT Default
|
||||
{
|
||||
get
|
||||
{
|
||||
WINDOWPLACEMENT result = default(WINDOWPLACEMENT);
|
||||
result.Length = Marshal.SizeOf(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的<see cref="WINDOWPLACEMENT"/>
|
||||
/// </summary>
|
||||
/// <param name="max">最大点</param>
|
||||
/// <param name="normal">正常位置</param>
|
||||
/// <param name="command">显示命令</param>
|
||||
/// <returns>窗体位置</returns>
|
||||
public static WINDOWPLACEMENT Create(POINT max, RECT normal, ShowWindowCommand command)
|
||||
{
|
||||
WINDOWPLACEMENT result = Default;
|
||||
|
||||
result.MaxPosition = max;
|
||||
result.NormalPosition = normal;
|
||||
result.ShowCmd = command;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -5,21 +5,33 @@ using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Control.HostBackdrop;
|
||||
using Snap.Hutao.Core.Logging;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Core.Win32;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.Shell;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
using WinRT.Interop;
|
||||
|
||||
namespace Snap.Hutao.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 窗口状态管理器
|
||||
/// 主要包含了各类 P/Inoke 代码
|
||||
/// </summary>
|
||||
internal class WindowManager
|
||||
{
|
||||
private readonly IntPtr handle;
|
||||
private const int MinWidth = 800;
|
||||
private const int MinHeight = 600;
|
||||
|
||||
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
|
||||
// otherwise will casuse System.ExecutionEngineException
|
||||
private SUBCLASSPROC? subClassProc;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的窗口状态管理器
|
||||
/// </summary>
|
||||
@@ -30,9 +42,7 @@ internal class WindowManager
|
||||
this.window = window;
|
||||
this.titleBar = titleBar;
|
||||
logger = Ioc.Default.GetRequiredService<ILogger<WindowManager>>();
|
||||
|
||||
handle = WindowNative.GetWindowHandle(window);
|
||||
|
||||
handle = (HWND)WindowNative.GetWindowHandle(window);
|
||||
InitializeWindow();
|
||||
}
|
||||
|
||||
@@ -43,18 +53,22 @@ internal class WindowManager
|
||||
int right = LocalSetting.GetValueType<int>(SettingKeys.WindowRight);
|
||||
int bottom = LocalSetting.GetValueType<int>(SettingKeys.WindowBottom);
|
||||
|
||||
return new RECT(left, top, right, bottom);
|
||||
return new() { left = left, top = top, right = right, bottom = bottom };
|
||||
}
|
||||
|
||||
private static void SaveWindowRect(IntPtr handle)
|
||||
private static void SaveWindowRect(HWND handle)
|
||||
{
|
||||
WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Default;
|
||||
User32.GetWindowPlacement(handle, ref windowPlacement);
|
||||
WINDOWPLACEMENT windowPlacement = new()
|
||||
{
|
||||
length = (uint)Marshal.SizeOf<WINDOWPLACEMENT>(),
|
||||
};
|
||||
|
||||
LocalSetting.Set(SettingKeys.WindowLeft, windowPlacement.NormalPosition.Left);
|
||||
LocalSetting.Set(SettingKeys.WindowTop, windowPlacement.NormalPosition.Top);
|
||||
LocalSetting.Set(SettingKeys.WindowRight, windowPlacement.NormalPosition.Right);
|
||||
LocalSetting.Set(SettingKeys.WindowBottom, windowPlacement.NormalPosition.Bottom);
|
||||
PInvoke.GetWindowPlacement(handle, ref windowPlacement);
|
||||
|
||||
LocalSetting.Set(SettingKeys.WindowLeft, windowPlacement.rcNormalPosition.left);
|
||||
LocalSetting.Set(SettingKeys.WindowTop, windowPlacement.rcNormalPosition.top);
|
||||
LocalSetting.Set(SettingKeys.WindowRight, windowPlacement.rcNormalPosition.right);
|
||||
LocalSetting.Set(SettingKeys.WindowBottom, windowPlacement.rcNormalPosition.bottom);
|
||||
}
|
||||
|
||||
private void InitializeWindow()
|
||||
@@ -63,20 +77,52 @@ internal class WindowManager
|
||||
window.SetTitleBar(titleBar);
|
||||
window.Closed += OnWindowClosed;
|
||||
|
||||
User32.SetWindowText(handle, "胡桃");
|
||||
PInvoke.SetWindowText(handle, "胡桃");
|
||||
RECT rect = RetriveWindowRect();
|
||||
if (rect.Area > 0)
|
||||
if ((rect.right - rect.left) * (rect.bottom - rect.top) > 0)
|
||||
{
|
||||
WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Create(new POINT(-1, -1), rect, ShowWindowCommand.Normal);
|
||||
User32.SetWindowPlacement(handle, ref windowPlacement);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private void OnWindowClosed(object sender, WindowEventArgs args)
|
||||
{
|
||||
PInvoke.RemoveWindowSubclass(handle, subClassProc, 101);
|
||||
subClassProc = null;
|
||||
SaveWindowRect(handle);
|
||||
}
|
||||
|
||||
private LRESULT OnWindowProcedure(HWND hwnd, uint uMsg, WPARAM wParam, LPARAM lParam, nuint uIdSubclass, nuint dwRefData)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case PInvoke.WM_GETMINMAXINFO:
|
||||
{
|
||||
uint dpi = PInvoke.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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return PInvoke.DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ public static class ObjectExtensions
|
||||
/// <typeparam name="T">目标转换类型</typeparam>
|
||||
/// <param name="obj">对象</param>
|
||||
/// <returns>转换类型后的对象</returns>
|
||||
public static T? As<T>(this object? obj)
|
||||
public static T? ImplictAs<T>(this object? obj)
|
||||
where T : class
|
||||
{
|
||||
return obj as T;
|
||||
|
||||
@@ -28,15 +28,4 @@ public class User
|
||||
/// 用户的Cookie
|
||||
/// </summary>
|
||||
public string? Cookie { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设置用户的选中状态
|
||||
/// 同时更新用户选择的角色信息
|
||||
/// </summary>
|
||||
/// <param name="user">用户</param>
|
||||
/// <param name="isSelected">是否选中</param>
|
||||
public static void SetSelectionState(User user, bool isSelected)
|
||||
{
|
||||
user!.IsSelected = isSelected;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity
|
||||
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
|
||||
Publisher="CN=DGP Studio"
|
||||
Version="1.0.19.0" />
|
||||
Version="1.0.20.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>胡桃</DisplayName>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Context.Database;
|
||||
using Snap.Hutao.Model.Binding;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Web.Hoyolab.Bbs.User;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
@@ -22,8 +23,8 @@ internal class UserService : IUserService
|
||||
private readonly UserClient userClient;
|
||||
private readonly UserGameRoleClient userGameRoleClient;
|
||||
|
||||
private Model.Binding.User? currentUser;
|
||||
private ObservableCollection<Model.Binding.User>? userCollection = null;
|
||||
private User? currentUser;
|
||||
private ObservableCollection<User>? userCollection = null;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的用户服务
|
||||
@@ -39,7 +40,7 @@ internal class UserService : IUserService
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Model.Binding.User? CurrentUser
|
||||
public User? CurrentUser
|
||||
{
|
||||
get => currentUser;
|
||||
set
|
||||
@@ -68,12 +69,12 @@ internal class UserService : IUserService
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<UserAddResult> TryAddUserAsync(Model.Binding.User newUser, string uid)
|
||||
public async Task<UserAddResult> TryAddUserAsync(User newUser, string uid)
|
||||
{
|
||||
Must.NotNull(userCollection!);
|
||||
|
||||
// 查找是否有相同的uid
|
||||
if (userCollection.SingleOrDefault(u => u.UserInfo!.Uid == uid) is Model.Binding.User userWithSameUid)
|
||||
if (userCollection.SingleOrDefault(u => u.UserInfo!.Uid == uid) is User userWithSameUid)
|
||||
{
|
||||
// Prevent users from adding a completely same cookie.
|
||||
if (userWithSameUid.Cookie == newUser.Cookie)
|
||||
@@ -94,36 +95,39 @@ internal class UserService : IUserService
|
||||
{
|
||||
Verify.Operation(newUser.IsInitialized, "该用户尚未初始化");
|
||||
|
||||
// Sync cache
|
||||
userCollection.Add(newUser);
|
||||
|
||||
// Sync database
|
||||
appDbContext.Users.Add(newUser.Entity);
|
||||
await appDbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
|
||||
// Sync cache
|
||||
userCollection.Add(newUser);
|
||||
|
||||
return UserAddResult.Added;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task RemoveUserAsync(Model.Binding.User user)
|
||||
public Task RemoveUserAsync(User user)
|
||||
{
|
||||
// Sync cache
|
||||
userCollection!.Remove(user);
|
||||
|
||||
// Sync database
|
||||
appDbContext.Users.Remove(user.Entity);
|
||||
return appDbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<ObservableCollection<Model.Binding.User>> GetUserCollectionAsync()
|
||||
public async Task<ObservableCollection<User>> GetUserCollectionAsync()
|
||||
{
|
||||
if (userCollection == null)
|
||||
{
|
||||
List<Model.Binding.User> users = new();
|
||||
List<User> users = new();
|
||||
|
||||
foreach (Model.Entity.User user in appDbContext.Users)
|
||||
foreach (Model.Entity.User entity in appDbContext.Users)
|
||||
{
|
||||
Model.Binding.User? initialized = await Model.Binding.User
|
||||
.CreateAsync(user, userClient, userGameRoleClient)
|
||||
User? initialized = await User
|
||||
.CreateAsync(entity, userClient, userGameRoleClient)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (initialized != null)
|
||||
@@ -133,23 +137,22 @@ internal class UserService : IUserService
|
||||
else
|
||||
{
|
||||
// User is unable to be initialized, remove it.
|
||||
appDbContext.Users.Remove(user);
|
||||
appDbContext.Users.Remove(entity);
|
||||
await appDbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
CurrentUser = users.SingleOrDefault(user => user.IsSelected);
|
||||
|
||||
userCollection = new(users);
|
||||
CurrentUser = users.SingleOrDefault(user => user.IsSelected);
|
||||
}
|
||||
|
||||
return userCollection;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<Model.Binding.User?> CreateUserAsync(string cookie)
|
||||
public Task<User?> CreateUserAsync(string cookie)
|
||||
{
|
||||
return Model.Binding.User.CreateAsync(new() { Cookie = cookie }, userClient, userGameRoleClient);
|
||||
return User.CreateAsync(new() { Cookie = cookie }, userClient, userGameRoleClient);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SettingsUI\SettingsUI.csproj" />
|
||||
<ProjectReference Include="..\Snap.Hutao.SourceGeneration\Snap.Hutao.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
<ProjectReference Include="..\Snap.Hutao.Win32\Snap.Hutao.Win32.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ using Snap.Hutao.ViewModel;
|
||||
namespace Snap.Hutao.View.Page;
|
||||
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// 设置页面
|
||||
/// </summary>
|
||||
public sealed partial class SettingPage : Microsoft.UI.Xaml.Controls.Page
|
||||
{
|
||||
|
||||
@@ -8,7 +8,6 @@ using Snap.Hutao.Factory.Abstraction;
|
||||
using Snap.Hutao.Model.Metadata.Achievement;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Snap.Hutao.ViewModel;
|
||||
|
||||
|
||||
@@ -100,27 +100,42 @@ internal class WikiAvatarViewModel : ObservableObject
|
||||
/// <summary>
|
||||
/// 筛选用元素信息集合
|
||||
/// </summary>
|
||||
public IList<Selectable<string>> FilterElementInfos => filterElementInfos;
|
||||
public IList<Selectable<string>> FilterElementInfos
|
||||
{
|
||||
get => filterElementInfos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 筛选用所属国家集合
|
||||
/// </summary>
|
||||
public IList<Selectable<Pair<string, string>>> FilterAssociationInfos => filterAssociationInfos;
|
||||
public IList<Selectable<Pair<string, string>>> FilterAssociationInfos
|
||||
{
|
||||
get => filterAssociationInfos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 筛选用武器信息集合
|
||||
/// </summary>
|
||||
public IList<Selectable<Pair<string, WeaponType>>> FilterWeaponTypeInfos => filterWeaponTypeInfos;
|
||||
public IList<Selectable<Pair<string, WeaponType>>> FilterWeaponTypeInfos
|
||||
{
|
||||
get => filterWeaponTypeInfos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 筛选用星级信息集合
|
||||
/// </summary>
|
||||
public IList<Selectable<Pair<string, ItemQuality>>> FilterQualityInfos => filterQualityInfos;
|
||||
public IList<Selectable<Pair<string, ItemQuality>>> FilterQualityInfos
|
||||
{
|
||||
get => filterQualityInfos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 筛选用体型信息集合
|
||||
/// </summary>
|
||||
public IList<Selectable<Pair<string, string>>> FilterBodyInfos => filterBodyInfos;
|
||||
public IList<Selectable<Pair<string, string>>> FilterBodyInfos
|
||||
{
|
||||
get => filterBodyInfos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开页面命令
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Snap.Hutao.Web.Hoyolab;
|
||||
/// <summary>
|
||||
/// 米哈游Url端点
|
||||
/// </summary>
|
||||
[SuppressMessage("", "SA1201")]
|
||||
internal static class ApiEndpoints
|
||||
{
|
||||
/// <summary>
|
||||
@@ -21,7 +22,13 @@ internal static class ApiEndpoints
|
||||
/// <summary>
|
||||
/// 游戏记录主页
|
||||
/// </summary>
|
||||
public const string GameRecordIndex = $"{ApiTakumiRecordApi}/index?role_id={{0}}&server={{1}}";
|
||||
/// <param name="uid">uid</param>
|
||||
/// <param name="server">服务器区域</param>
|
||||
/// <returns>游戏记录主页字符串</returns>
|
||||
public static string GameRecordIndex(string uid, string server)
|
||||
{
|
||||
return $"{ApiTakumiRecordApi}/index?role_id={uid}&server={server}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 深渊信息
|
||||
|
||||
@@ -55,7 +55,7 @@ internal class GameRecordClient
|
||||
/// <returns>玩家的基础信息</returns>
|
||||
public async Task<PlayerInfo?> GetPlayerInfoAsync(User user, PlayerUid uid, CancellationToken token = default)
|
||||
{
|
||||
string url = string.Format(ApiEndpoints.GameRecordIndex, uid.Value, uid.Region);
|
||||
string url = string.Format(ApiEndpoints.GameRecordIndex(uid.Value, uid.Region));
|
||||
|
||||
Response<PlayerInfo>? resp = await httpClient
|
||||
.SetUser(user)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using Snap.Hutao.Core.Abstraction;
|
||||
using Snap.Hutao.Extension;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
|
||||
|
||||
Reference in New Issue
Block a user