mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
impl #961
This commit is contained in:
@@ -163,7 +163,7 @@ internal sealed partial class Activation : IActivation
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
currentWindowReference.Window = serviceProvider.GetRequiredService<MainWindow>();
|
||||
serviceProvider.GetRequiredService<MainWindow>();
|
||||
|
||||
serviceProvider
|
||||
.GetRequiredService<IMetadataService>()
|
||||
@@ -270,7 +270,7 @@ internal sealed partial class Activation : IActivation
|
||||
|
||||
if (currentWindowReference.Window is null)
|
||||
{
|
||||
currentWindowReference.Window = serviceProvider.GetRequiredService<LaunchGameWindow>();
|
||||
serviceProvider.GetRequiredService<LaunchGameWindow>();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Core.Windowing;
|
||||
using Windows.Win32.Foundation;
|
||||
using WinRT.Interop;
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle;
|
||||
|
||||
@@ -11,4 +14,11 @@ internal static class CurrentWindowReferenceExtension
|
||||
{
|
||||
return reference.Window.Content.XamlRoot;
|
||||
}
|
||||
|
||||
public static HWND GetWindowHandle(this ICurrentWindowReference reference)
|
||||
{
|
||||
return reference.Window is IWindowOptionsSource optionsSource
|
||||
? optionsSource.WindowOptions.Hwnd
|
||||
: (HWND)WindowNative.GetWindowHandle(reference.Window);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,5 +7,8 @@ namespace Snap.Hutao.Core.LifeCycle;
|
||||
|
||||
internal interface ICurrentWindowReference
|
||||
{
|
||||
/// <summary>
|
||||
/// Only set in WindowController
|
||||
/// </summary>
|
||||
public Window Window { get; set; }
|
||||
}
|
||||
@@ -81,4 +81,6 @@ internal static class SettingKeys
|
||||
public const string IsHomeCardGachaStatisticsPresented = "IsHomeCardGachaStatisticsPresented";
|
||||
public const string IsHomeCardAchievementPresented = "IsHomeCardAchievementPresented";
|
||||
public const string IsHomeCardDailyNotePresented = "IsHomeCardDailyNotePresented";
|
||||
|
||||
public const string HotKeyMouseClickRepeatForever = "HotKeyMouseClickRepeatForever";
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Core.LifeCycle;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Model;
|
||||
using System.Text;
|
||||
using Windows.System;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.Input.KeyboardAndMouse;
|
||||
using static Windows.Win32.PInvoke;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.HotKey;
|
||||
|
||||
[SuppressMessage("", "SA1124")]
|
||||
internal sealed class HotKeyCombination : ObservableObject
|
||||
{
|
||||
private readonly ICurrentWindowReference currentWindowReference;
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
|
||||
private readonly string settingKey;
|
||||
private readonly int hotKeyId;
|
||||
private readonly HotKeyParameter defaultHotKeyParameter;
|
||||
|
||||
private bool registered;
|
||||
|
||||
private bool modifierHasWindows;
|
||||
private bool modifierHasControl;
|
||||
private bool modifierHasShift;
|
||||
private bool modifierHasAlt;
|
||||
private NameValue<VirtualKey> keyNameValue;
|
||||
private HOT_KEY_MODIFIERS modifiers;
|
||||
private VirtualKey key;
|
||||
private bool isEnabled;
|
||||
|
||||
public HotKeyCombination(IServiceProvider serviceProvider, string settingKey, int hotKeyId, HOT_KEY_MODIFIERS defaultModifiers, VirtualKey defaultKey)
|
||||
{
|
||||
currentWindowReference = serviceProvider.GetRequiredService<ICurrentWindowReference>();
|
||||
runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
|
||||
this.settingKey = settingKey;
|
||||
this.hotKeyId = hotKeyId;
|
||||
defaultHotKeyParameter = new(defaultModifiers, defaultKey);
|
||||
|
||||
// Initialize Property backing fields
|
||||
{
|
||||
// Retrieve from LocalSetting
|
||||
isEnabled = LocalSetting.Get($"{settingKey}.IsEnabled", true);
|
||||
|
||||
HotKeyParameter actual = LocalSettingGetHotKeyParameter();
|
||||
modifiers = actual.Modifiers;
|
||||
InitializeModifiersComposeFields();
|
||||
key = actual.Key;
|
||||
|
||||
keyNameValue = VirtualKeys.GetList().Single(v => v.Value == key);
|
||||
}
|
||||
}
|
||||
|
||||
#region Binding Property
|
||||
public bool ModifierHasWindows
|
||||
{
|
||||
get => modifierHasWindows;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref modifierHasWindows, value))
|
||||
{
|
||||
UpdateModifiers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ModifierHasControl
|
||||
{
|
||||
get => modifierHasControl;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref modifierHasControl, value))
|
||||
{
|
||||
UpdateModifiers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ModifierHasShift
|
||||
{
|
||||
get => modifierHasShift;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref modifierHasShift, value))
|
||||
{
|
||||
UpdateModifiers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ModifierHasAlt
|
||||
{
|
||||
get => modifierHasAlt;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref modifierHasAlt, value))
|
||||
{
|
||||
UpdateModifiers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public NameValue<VirtualKey> KeyNameValue
|
||||
{
|
||||
get => keyNameValue;
|
||||
set
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (SetProperty(ref keyNameValue, value))
|
||||
{
|
||||
Key = value.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public HOT_KEY_MODIFIERS Modifiers
|
||||
{
|
||||
get => modifiers;
|
||||
private set
|
||||
{
|
||||
if (SetProperty(ref modifiers, value))
|
||||
{
|
||||
OnPropertyChanged(nameof(DisplayName));
|
||||
LocalSettingSetHotKeyParameterAndRefresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public VirtualKey Key
|
||||
{
|
||||
get => key;
|
||||
private set
|
||||
{
|
||||
if (SetProperty(ref key, value))
|
||||
{
|
||||
OnPropertyChanged(nameof(DisplayName));
|
||||
LocalSettingSetHotKeyParameterAndRefresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => isEnabled;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref isEnabled, value))
|
||||
{
|
||||
LocalSetting.Set($"{settingKey}.IsEnabled", value);
|
||||
|
||||
_ = (value, registered) switch
|
||||
{
|
||||
(true, false) => RegisterForCurrentWindow(),
|
||||
(false, true) => UnregisterForCurrentWindow(),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string DisplayName { get => ToString(); }
|
||||
|
||||
public bool RegisterForCurrentWindow()
|
||||
{
|
||||
if (!runtimeOptions.IsElevated || !IsEnabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (registered)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
HWND hwnd = currentWindowReference.GetWindowHandle();
|
||||
registered = RegisterHotKey(hwnd, hotKeyId, Modifiers, (uint)Key);
|
||||
return registered;
|
||||
}
|
||||
|
||||
public bool UnregisterForCurrentWindow()
|
||||
{
|
||||
if (!runtimeOptions.IsElevated)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!registered)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
HWND hwnd = currentWindowReference.GetWindowHandle();
|
||||
registered = UnregisterHotKey(hwnd, hotKeyId);
|
||||
return registered;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder stringBuilder = new();
|
||||
|
||||
if (Modifiers.HasFlag(HOT_KEY_MODIFIERS.MOD_WIN))
|
||||
{
|
||||
stringBuilder.Append("Win").Append(" + ");
|
||||
}
|
||||
|
||||
if (Modifiers.HasFlag(HOT_KEY_MODIFIERS.MOD_CONTROL))
|
||||
{
|
||||
stringBuilder.Append("Ctrl").Append(" + ");
|
||||
}
|
||||
|
||||
if (Modifiers.HasFlag(HOT_KEY_MODIFIERS.MOD_SHIFT))
|
||||
{
|
||||
stringBuilder.Append("Shift").Append(" + ");
|
||||
}
|
||||
|
||||
if (Modifiers.HasFlag(HOT_KEY_MODIFIERS.MOD_ALT))
|
||||
{
|
||||
stringBuilder.Append("Alt").Append(" + ");
|
||||
}
|
||||
|
||||
stringBuilder.Append(Key);
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
private void UpdateModifiers()
|
||||
{
|
||||
HOT_KEY_MODIFIERS modifiers = default;
|
||||
|
||||
if (ModifierHasWindows)
|
||||
{
|
||||
modifiers |= HOT_KEY_MODIFIERS.MOD_WIN;
|
||||
}
|
||||
|
||||
if (ModifierHasControl)
|
||||
{
|
||||
modifiers |= HOT_KEY_MODIFIERS.MOD_CONTROL;
|
||||
}
|
||||
|
||||
if (ModifierHasShift)
|
||||
{
|
||||
modifiers |= HOT_KEY_MODIFIERS.MOD_SHIFT;
|
||||
}
|
||||
|
||||
if (ModifierHasAlt)
|
||||
{
|
||||
modifiers |= HOT_KEY_MODIFIERS.MOD_ALT;
|
||||
}
|
||||
|
||||
Modifiers = modifiers;
|
||||
}
|
||||
|
||||
private void InitializeModifiersComposeFields()
|
||||
{
|
||||
if (Modifiers.HasFlag(HOT_KEY_MODIFIERS.MOD_WIN))
|
||||
{
|
||||
modifierHasWindows = true;
|
||||
}
|
||||
|
||||
if (Modifiers.HasFlag(HOT_KEY_MODIFIERS.MOD_CONTROL))
|
||||
{
|
||||
modifierHasControl = true;
|
||||
}
|
||||
|
||||
if (Modifiers.HasFlag(HOT_KEY_MODIFIERS.MOD_SHIFT))
|
||||
{
|
||||
modifierHasShift = true;
|
||||
}
|
||||
|
||||
if (Modifiers.HasFlag(HOT_KEY_MODIFIERS.MOD_ALT))
|
||||
{
|
||||
modifierHasAlt = true;
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe HotKeyParameter LocalSettingGetHotKeyParameter()
|
||||
{
|
||||
fixed (HotKeyParameter* pDefaultHotKey = &defaultHotKeyParameter)
|
||||
{
|
||||
int value = LocalSetting.Get(settingKey, *(int*)pDefaultHotKey);
|
||||
return *(HotKeyParameter*)&value;
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void LocalSettingSetHotKeyParameterAndRefresh()
|
||||
{
|
||||
HotKeyParameter current = new(Modifiers, Key);
|
||||
LocalSetting.Set(settingKey, *(int*)¤t);
|
||||
|
||||
UnregisterForCurrentWindow();
|
||||
RegisterForCurrentWindow();
|
||||
}
|
||||
}
|
||||
@@ -2,69 +2,38 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.Input.KeyboardAndMouse;
|
||||
using static Windows.Win32.PInvoke;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.HotKey;
|
||||
|
||||
[SuppressMessage("", "CA1001")]
|
||||
internal sealed class HotKeyController : IHotKeyController
|
||||
[ConstructorGenerated]
|
||||
internal sealed partial class HotKeyController : IHotKeyController
|
||||
{
|
||||
private const int DefaultId = 100000;
|
||||
private static readonly WaitCallback RunMouseClickRepeatForever = MouseClickRepeatForever;
|
||||
|
||||
private readonly object locker = new();
|
||||
private readonly WaitCallback runMouseClickRepeatForever;
|
||||
|
||||
private readonly HotKeyOptions hotKeyOptions;
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
|
||||
private volatile CancellationTokenSource? cancellationTokenSource;
|
||||
|
||||
public HotKeyController(IServiceProvider serviceProvider)
|
||||
public void RegisterAll()
|
||||
{
|
||||
hotKeyOptions = serviceProvider.GetRequiredService<HotKeyOptions>();
|
||||
runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
runMouseClickRepeatForever = MouseClickRepeatForever;
|
||||
hotKeyOptions.MouseClickRepeatForeverKeyCombination.RegisterForCurrentWindow();
|
||||
}
|
||||
|
||||
public bool Register(in HWND hwnd)
|
||||
public void UnregisterAll()
|
||||
{
|
||||
if (runtimeOptions.IsElevated)
|
||||
{
|
||||
return RegisterHotKey(hwnd, DefaultId, default, (uint)VIRTUAL_KEY.VK_F8);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Unregister(in HWND hwnd)
|
||||
{
|
||||
if (runtimeOptions.IsElevated)
|
||||
{
|
||||
return UnregisterHotKey(hwnd, DefaultId);
|
||||
}
|
||||
|
||||
return false;
|
||||
hotKeyOptions.MouseClickRepeatForeverKeyCombination.UnregisterForCurrentWindow();
|
||||
}
|
||||
|
||||
public void OnHotKeyPressed(in HotKeyParameter parameter)
|
||||
{
|
||||
if (parameter is { Key: VIRTUAL_KEY.VK_F8, NativeModifier: 0 })
|
||||
if (parameter.Equals(hotKeyOptions.MouseClickRepeatForeverKeyCombination))
|
||||
{
|
||||
lock (locker)
|
||||
{
|
||||
if (hotKeyOptions.IsMouseClickRepeatForeverOn)
|
||||
{
|
||||
cancellationTokenSource?.Cancel();
|
||||
cancellationTokenSource = default;
|
||||
hotKeyOptions.IsMouseClickRepeatForeverOn = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
cancellationTokenSource = new();
|
||||
ThreadPool.QueueUserWorkItem(runMouseClickRepeatForever, cancellationTokenSource.Token);
|
||||
hotKeyOptions.IsMouseClickRepeatForeverOn = true;
|
||||
}
|
||||
}
|
||||
ToggleMouseClickRepeatForever();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +45,7 @@ internal sealed class HotKeyController : IHotKeyController
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SH007")]
|
||||
private unsafe void MouseClickRepeatForever(object? state)
|
||||
private static unsafe void MouseClickRepeatForever(object? state)
|
||||
{
|
||||
CancellationToken token = (CancellationToken)state!;
|
||||
|
||||
@@ -102,4 +71,25 @@ internal sealed class HotKeyController : IHotKeyController
|
||||
Thread.Sleep(Random.Shared.Next(100, 150));
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleMouseClickRepeatForever()
|
||||
{
|
||||
lock (locker)
|
||||
{
|
||||
if (hotKeyOptions.IsMouseClickRepeatForeverOn)
|
||||
{
|
||||
// Turn off
|
||||
cancellationTokenSource?.Cancel();
|
||||
cancellationTokenSource = default;
|
||||
hotKeyOptions.IsMouseClickRepeatForeverOn = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Turn on
|
||||
cancellationTokenSource = new();
|
||||
ThreadPool.QueueUserWorkItem(RunMouseClickRepeatForever, cancellationTokenSource.Token);
|
||||
hotKeyOptions.IsMouseClickRepeatForeverOn = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,34 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Model;
|
||||
using Windows.System;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.HotKey;
|
||||
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed class HotKeyOptions : ObservableObject
|
||||
internal sealed partial class HotKeyOptions : ObservableObject
|
||||
{
|
||||
private bool isVirtualKeyF8Pressed;
|
||||
private bool isMouseClickRepeatForeverOn;
|
||||
private HotKeyCombination mouseClickRepeatForeverKeyCombination;
|
||||
|
||||
public bool IsMouseClickRepeatForeverOn { get => isVirtualKeyF8Pressed; set => SetProperty(ref isVirtualKeyF8Pressed, value); }
|
||||
public HotKeyOptions(IServiceProvider serviceProvider)
|
||||
{
|
||||
mouseClickRepeatForeverKeyCombination = new(serviceProvider, SettingKeys.HotKeyMouseClickRepeatForever, 100000, default, VirtualKey.F8);
|
||||
}
|
||||
|
||||
public List<NameValue<VirtualKey>> VirtualKeys { get; } = HotKey.VirtualKeys.GetList();
|
||||
|
||||
public bool IsMouseClickRepeatForeverOn
|
||||
{
|
||||
get => isMouseClickRepeatForeverOn;
|
||||
set => SetProperty(ref isMouseClickRepeatForeverOn, value);
|
||||
}
|
||||
|
||||
public HotKeyCombination MouseClickRepeatForeverKeyCombination
|
||||
{
|
||||
get => mouseClickRepeatForeverKeyCombination;
|
||||
set => SetProperty(ref mouseClickRepeatForeverKeyCombination, value);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,43 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Windows.System;
|
||||
using Windows.Win32.UI.Input.KeyboardAndMouse;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.HotKey;
|
||||
|
||||
internal readonly struct HotKeyParameter
|
||||
/// <summary>
|
||||
/// HotKeyParameter
|
||||
/// The size of this struct must be sizeof(LPARAM) or 4
|
||||
/// </summary>
|
||||
internal readonly struct HotKeyParameter : IEquatable<HotKeyCombination>
|
||||
{
|
||||
public readonly ushort NativeModifier;
|
||||
public readonly VIRTUAL_KEY Key;
|
||||
public readonly ushort NativeModifiers;
|
||||
public readonly VIRTUAL_KEY NativeKey;
|
||||
|
||||
public readonly HOT_KEY_MODIFIERS Modifier
|
||||
public HotKeyParameter(HOT_KEY_MODIFIERS modifiers, VirtualKey key)
|
||||
{
|
||||
get => (HOT_KEY_MODIFIERS)NativeModifier;
|
||||
NativeModifiers = (ushort)modifiers;
|
||||
NativeKey = (VIRTUAL_KEY)key;
|
||||
}
|
||||
|
||||
public readonly HOT_KEY_MODIFIERS Modifiers
|
||||
{
|
||||
get => (HOT_KEY_MODIFIERS)NativeModifiers;
|
||||
}
|
||||
|
||||
public readonly VirtualKey Key
|
||||
{
|
||||
get => (VirtualKey)NativeKey;
|
||||
}
|
||||
|
||||
public bool Equals(HotKeyCombination? other)
|
||||
{
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Modifiers == other.Modifiers && Key == other.Key;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Windows.Win32.Foundation;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.HotKey;
|
||||
|
||||
internal interface IHotKeyController
|
||||
{
|
||||
void OnHotKeyPressed(in HotKeyParameter parameter);
|
||||
|
||||
bool Register(in HWND hwnd);
|
||||
void RegisterAll();
|
||||
|
||||
bool Unregister(in HWND hwnd);
|
||||
void UnregisterAll();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model;
|
||||
using Windows.System;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.HotKey;
|
||||
|
||||
internal static class VirtualKeys
|
||||
{
|
||||
private static readonly List<NameValue<VirtualKey>> Values = CollectionsNameValue.ListFromEnum<VirtualKey>();
|
||||
|
||||
public static List<NameValue<VirtualKey>> GetList()
|
||||
{
|
||||
return Values;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using Microsoft.UI.Composition.SystemBackdrops;
|
||||
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.Service;
|
||||
using System.IO;
|
||||
@@ -32,8 +33,9 @@ internal sealed class WindowController
|
||||
this.options = options;
|
||||
this.serviceProvider = serviceProvider;
|
||||
|
||||
// Window reference must be set before Window Subclass created
|
||||
serviceProvider.GetRequiredService<ICurrentWindowReference>().Window = window;
|
||||
subclass = new(window, options, serviceProvider);
|
||||
|
||||
InitializeCore();
|
||||
}
|
||||
|
||||
@@ -78,7 +80,7 @@ internal sealed class WindowController
|
||||
private void RecoverOrInitWindowSize()
|
||||
{
|
||||
// Set first launch size
|
||||
double scale = options.GetWindowScale();
|
||||
double scale = options.GetRasterizationScale();
|
||||
SizeInt32 scaledSize = options.InitSize.Scale(scale);
|
||||
RectInt32 rect = StructMarshal.RectInt32(scaledSize);
|
||||
|
||||
@@ -108,14 +110,14 @@ internal sealed class WindowController
|
||||
// prevent save value when we are maximized.
|
||||
if (!windowPlacement.showCmd.HasFlag(SHOW_WINDOW_CMD.SW_SHOWMAXIMIZED))
|
||||
{
|
||||
double scale = 1 / options.GetWindowScale();
|
||||
double scale = 1.0 / options.GetRasterizationScale();
|
||||
LocalSetting.Set(SettingKeys.WindowRect, (CompactRect)window.AppWindow.GetRect().Scale(scale));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnOptionsPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(AppOptions.BackdropType))
|
||||
if (e.PropertyName is nameof(AppOptions.BackdropType))
|
||||
{
|
||||
if (sender is AppOptions options)
|
||||
{
|
||||
@@ -198,7 +200,7 @@ internal sealed class WindowController
|
||||
{
|
||||
AppWindowTitleBar appTitleBar = window.AppWindow.TitleBar;
|
||||
|
||||
double scale = options.GetWindowScale();
|
||||
double scale = options.GetRasterizationScale();
|
||||
|
||||
// 48 is the navigation button leftInset
|
||||
RectInt32 dragRect = StructMarshal.RectInt32(48, 0, options.TitleBar.ActualSize).Scale(scale);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Input;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.Graphics;
|
||||
@@ -20,6 +21,11 @@ internal readonly struct WindowOptions
|
||||
/// </summary>
|
||||
public readonly HWND Hwnd;
|
||||
|
||||
/// <summary>
|
||||
/// 非客户端区域指针源
|
||||
/// </summary>
|
||||
public readonly InputNonClientPointerSource InputNonClientPointerSource;
|
||||
|
||||
/// <summary>
|
||||
/// 标题栏元素
|
||||
/// </summary>
|
||||
@@ -50,6 +56,7 @@ internal readonly struct WindowOptions
|
||||
public WindowOptions(Window window, FrameworkElement titleBar, SizeInt32 initSize, bool persistSize = false)
|
||||
{
|
||||
Hwnd = (HWND)WindowNative.GetWindowHandle(window);
|
||||
InputNonClientPointerSource = InputNonClientPointerSource.GetForWindowId(window.AppWindow.Id);
|
||||
TitleBar = titleBar;
|
||||
InitSize = initSize;
|
||||
PersistSize = persistSize;
|
||||
@@ -59,7 +66,7 @@ internal readonly struct WindowOptions
|
||||
/// 获取窗体当前的DPI缩放比
|
||||
/// </summary>
|
||||
/// <returns>缩放比</returns>
|
||||
public double GetWindowScale()
|
||||
public double GetRasterizationScale()
|
||||
{
|
||||
uint dpi = GetDpiForWindow(Hwnd);
|
||||
return Math.Round(dpi / 96D, 2, MidpointRounding.AwayFromZero);
|
||||
|
||||
@@ -45,7 +45,7 @@ internal sealed class WindowSubclass : IDisposable
|
||||
{
|
||||
windowProc = OnSubclassProcedure;
|
||||
bool windowHooked = SetWindowSubclass(options.Hwnd, windowProc, WindowSubclassId, 0);
|
||||
hotKeyController.Register(options.Hwnd);
|
||||
hotKeyController.RegisterAll();
|
||||
|
||||
bool titleBarHooked = true;
|
||||
|
||||
@@ -72,7 +72,7 @@ internal sealed class WindowSubclass : IDisposable
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
hotKeyController.Unregister(options.Hwnd);
|
||||
hotKeyController.UnregisterAll();
|
||||
|
||||
RemoveWindowSubclass(options.Hwnd, windowProc, WindowSubclassId);
|
||||
windowProc = null;
|
||||
@@ -93,7 +93,7 @@ internal sealed class WindowSubclass : IDisposable
|
||||
{
|
||||
if (window is IMinMaxInfoHandler handler)
|
||||
{
|
||||
handler.HandleMinMaxInfo(ref *(MINMAXINFO*)lParam.Value, options.GetWindowScale());
|
||||
handler.HandleMinMaxInfo(ref *(MINMAXINFO*)lParam.Value, options.GetRasterizationScale());
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -5,6 +5,7 @@ using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.LifeCycle;
|
||||
using Snap.Hutao.Core.Windowing;
|
||||
using Windows.Storage.Pickers;
|
||||
using Windows.Win32.Foundation;
|
||||
using WinRT.Interop;
|
||||
|
||||
namespace Snap.Hutao.Factory.Picker;
|
||||
@@ -79,10 +80,8 @@ internal sealed partial class PickerFactory : IPickerFactory
|
||||
{
|
||||
// Create a folder picker.
|
||||
T picker = new();
|
||||
nint hwnd = currentWindowReference.Window is IWindowOptionsSource optionsSource
|
||||
? (nint)optionsSource.WindowOptions.Hwnd
|
||||
: WindowNative.GetWindowHandle(currentWindowReference.Window);
|
||||
|
||||
HWND hwnd = currentWindowReference.GetWindowHandle();
|
||||
InitializeWithWindow.Initialize(picker, hwnd);
|
||||
|
||||
return picker;
|
||||
|
||||
13
src/Snap.Hutao/Snap.Hutao/Model/CollectionsNameValue.cs
Normal file
13
src/Snap.Hutao/Snap.Hutao/Model/CollectionsNameValue.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model;
|
||||
|
||||
internal static class CollectionsNameValue
|
||||
{
|
||||
public static List<NameValue<T>> ListFromEnum<T>()
|
||||
where T : struct, Enum
|
||||
{
|
||||
return Enum.GetValues<T>().Select(x => new NameValue<T>(x.ToString(), x)).ToList();
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ namespace Snap.Hutao.Model;
|
||||
/// <summary>
|
||||
/// 封装带有名称描述的值
|
||||
/// 在绑定枚举变量时非常有用
|
||||
/// https://github.com/microsoft/microsoft-ui-xaml/issues/4266
|
||||
/// 直接绑定枚举变量会显示 Windows.Foundation.IReference{T}
|
||||
/// </summary>
|
||||
/// <typeparam name="T">包含值的类型</typeparam>
|
||||
[HighQuality]
|
||||
|
||||
@@ -2300,6 +2300,15 @@
|
||||
<data name="ViewPageSettingIsAdvancedLaunchOptionsEnabledHeader" xml:space="preserve">
|
||||
<value>启动高级功能</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingKeyShortcutAutoClickingDescription" xml:space="preserve">
|
||||
<value>更改自动连点功能的快捷键</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingKeyShortcutAutoClickingHeader" xml:space="preserve">
|
||||
<value>自动连点</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingKeyShortcutHeader" xml:space="preserve">
|
||||
<value>快捷键</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOfficialSiteNavigate" xml:space="preserve">
|
||||
<value>前往官网</value>
|
||||
</data>
|
||||
|
||||
@@ -19,12 +19,7 @@ namespace Snap.Hutao.Service;
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed partial class AppOptions : DbStoreOptions
|
||||
{
|
||||
private readonly List<NameValue<BackdropType>> supportedBackdropTypesInner = new()
|
||||
{
|
||||
new("Acrylic", BackdropType.Acrylic),
|
||||
new("Mica", BackdropType.Mica),
|
||||
new("MicaAlt", BackdropType.MicaAlt),
|
||||
};
|
||||
private readonly List<NameValue<BackdropType>> supportedBackdropTypesInner = CollectionsNameValue.ListFromEnum<BackdropType>();
|
||||
|
||||
private readonly List<NameValue<string>> supportedCulturesInner = new()
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Snap.Hutao.Service.Game.Scheme;
|
||||
/// 启动方案
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal class LaunchScheme
|
||||
internal class LaunchScheme : IEquatable<ChannelOptions>
|
||||
{
|
||||
/// <summary>
|
||||
/// 显示名称
|
||||
@@ -72,9 +72,10 @@ internal class LaunchScheme
|
||||
/// </summary>
|
||||
/// <param name="multiChannel">多通道</param>
|
||||
/// <returns>是否相等</returns>
|
||||
public bool MultiChannelEqual(in ChannelOptions multiChannel)
|
||||
[SuppressMessage("", "SH002")]
|
||||
public bool Equals(ChannelOptions other)
|
||||
{
|
||||
return Channel == multiChannel.Channel && SubChannel == multiChannel.SubChannel;
|
||||
return Channel == other.Channel && SubChannel == other.SubChannel;
|
||||
}
|
||||
|
||||
public bool ExecutableMatches(string gameFileName)
|
||||
|
||||
@@ -83,6 +83,11 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
infoBarService.Error(ex, SH.ServiceMetadataRequestFailed);
|
||||
return false;
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
if (ex.StatusCode is HttpStatusCode.Forbidden or HttpStatusCode.NotFound)
|
||||
|
||||
@@ -195,6 +195,45 @@
|
||||
SelectedItem="{Binding SelectedBackdropType, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageSettingKeyShortcutHeader}"/>
|
||||
<cwc:SettingsCard
|
||||
Description="{shcm:ResourceString Name=ViewPageSettingKeyShortcutAutoClickingDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageSettingKeyShortcutAutoClickingHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="16">
|
||||
<CheckBox
|
||||
MinWidth="64"
|
||||
VerticalAlignment="Center"
|
||||
Content="Win"
|
||||
IsChecked="{Binding HotKeyOptions.MouseClickRepeatForeverKeyCombination.ModifierHasWindows, Mode=TwoWay}"/>
|
||||
<CheckBox
|
||||
MinWidth="64"
|
||||
VerticalAlignment="Center"
|
||||
Content="Ctrl"
|
||||
IsChecked="{Binding HotKeyOptions.MouseClickRepeatForeverKeyCombination.ModifierHasControl, Mode=TwoWay}"/>
|
||||
<CheckBox
|
||||
MinWidth="64"
|
||||
VerticalAlignment="Center"
|
||||
Content="Shift"
|
||||
IsChecked="{Binding HotKeyOptions.MouseClickRepeatForeverKeyCombination.ModifierHasShift, Mode=TwoWay}"/>
|
||||
<CheckBox
|
||||
MinWidth="64"
|
||||
VerticalAlignment="Center"
|
||||
Content="Alt"
|
||||
IsChecked="{Binding HotKeyOptions.MouseClickRepeatForeverKeyCombination.ModifierHasAlt, Mode=TwoWay}"/>
|
||||
<ComboBox
|
||||
VerticalAlignment="Center"
|
||||
DisplayMemberPath="Name"
|
||||
ItemsSource="{Binding HotKeyOptions.VirtualKeys}"
|
||||
SelectedItem="{Binding HotKeyOptions.MouseClickRepeatForeverKeyCombination.KeyNameValue, Mode=TwoWay}"/>
|
||||
<ToggleSwitch
|
||||
MinWidth="120"
|
||||
VerticalAlignment="Center"
|
||||
IsOn="{Binding HotKeyOptions.MouseClickRepeatForeverKeyCombination.IsEnabled, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
|
||||
</cwc:SettingsCard>
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewpageSettingHomeHeader}"/>
|
||||
<cwc:SettingsExpander
|
||||
Description="{shcm:ResourceString Name=ViewpageSettingHomeCardDescription}"
|
||||
@@ -295,9 +334,7 @@
|
||||
Header="{shcm:ResourceString Name=ViewPageSettingSetDataFolderHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsClickEnabled="True"/>
|
||||
|
||||
<cwc:SettingsCard
|
||||
Margin="0,3,0,0"
|
||||
ActionIcon="{shcm:FontIcon Glyph=}"
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageSettingStorageOpenAction}"
|
||||
Command="{Binding OpenCacheFolderCommand}"
|
||||
@@ -318,7 +355,6 @@
|
||||
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
|
||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewPageSettingDangerousHeader}"/>
|
||||
|
||||
<InfoBar
|
||||
IsClosable="False"
|
||||
IsOpen="{Binding Options.IsAdvancedLaunchOptionsEnabled}"
|
||||
|
||||
@@ -29,17 +29,14 @@
|
||||
IsChecked="{x:Bind HotKeyOptions.IsMouseClickRepeatForeverOn, Mode=OneWay}"
|
||||
IsHitTestVisible="False"
|
||||
Visibility="{x:Bind RuntimeOptions.IsElevated}">
|
||||
<Grid>
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Column="0" Text="F8"/>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="6,0,0,0"
|
||||
Text="{shcm:ResourceString Name=ViewTitleAutoClicking}"/>
|
||||
<TextBlock Grid.Column="0" Text="{x:Bind HotKeyOptions.MouseClickRepeatForeverKeyCombination.DisplayName, Mode=OneWay}"/>
|
||||
<TextBlock Grid.Column="1" Text="{shcm:ResourceString Name=ViewTitleAutoClicking}"/>
|
||||
</Grid>
|
||||
</ToggleButton>
|
||||
</Grid>
|
||||
|
||||
@@ -125,7 +125,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
|
||||
{
|
||||
SelectedScheme = KnownSchemes
|
||||
.Where(scheme => scheme.IsOversea == options.IsOversea)
|
||||
.Single(scheme => scheme.MultiChannelEqual(options));
|
||||
.Single(scheme => scheme.Equals(options));
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
using Snap.Hutao.Core.Setting;
|
||||
|
||||
namespace Snap.Hutao.ViewModel;
|
||||
namespace Snap.Hutao.ViewModel.Setting;
|
||||
|
||||
internal sealed class HomeCardOptions
|
||||
{
|
||||
@@ -11,6 +11,7 @@ using Snap.Hutao.Core.IO.DataTransfer;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Core.Shell;
|
||||
using Snap.Hutao.Core.Windowing;
|
||||
using Snap.Hutao.Core.Windowing.HotKey;
|
||||
using Snap.Hutao.Factory.ContentDialog;
|
||||
using Snap.Hutao.Factory.Picker;
|
||||
using Snap.Hutao.Model;
|
||||
@@ -51,6 +52,7 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
private readonly IPickerFactory pickerFactory;
|
||||
private readonly HotKeyOptions hotKeyOptions;
|
||||
private readonly IUserService userService;
|
||||
private readonly ITaskContext taskContext;
|
||||
private readonly AppOptions appOptions;
|
||||
@@ -63,18 +65,14 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
|
||||
/// </summary>
|
||||
public AppOptions Options { get => appOptions; }
|
||||
|
||||
/// <summary>
|
||||
/// 胡桃选项
|
||||
/// </summary>
|
||||
public RuntimeOptions HutaoOptions { get => runtimeOptions; }
|
||||
|
||||
/// <summary>
|
||||
/// 胡桃用户选项
|
||||
/// </summary>
|
||||
public HutaoUserOptions UserOptions { get => hutaoUserOptions; }
|
||||
|
||||
public HomeCardOptions HomeCardOptions { get => homeCardOptions; }
|
||||
|
||||
public HotKeyOptions HotKeyOptions { get => hotKeyOptions; }
|
||||
|
||||
public HutaoPassportViewModel Passport { get => hutaoPassportViewModel; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user