This commit is contained in:
Lightczx
2023-11-07 15:37:53 +08:00
parent b0ecd048b6
commit 5e734ac689
25 changed files with 530 additions and 95 deletions

View File

@@ -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
{

View File

@@ -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);
}
}

View File

@@ -7,5 +7,8 @@ namespace Snap.Hutao.Core.LifeCycle;
internal interface ICurrentWindowReference
{
/// <summary>
/// Only set in WindowController
/// </summary>
public Window Window { get; set; }
}

View File

@@ -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";
}

View File

@@ -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*)&current);
UnregisterForCurrentWindow();
RegisterForCurrentWindow();
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View 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();
}
}

View File

@@ -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]

View File

@@ -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>

View File

@@ -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()
{

View File

@@ -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)

View File

@@ -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)

View File

@@ -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=&#xE92E;}">
<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=&#xE8DE;}"
IsClickEnabled="True"/>
<cwc:SettingsCard
Margin="0,3,0,0"
ActionIcon="{shcm:FontIcon Glyph=&#xE76C;}"
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}"

View File

@@ -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>

View File

@@ -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)
{

View File

@@ -3,7 +3,7 @@
using Snap.Hutao.Core.Setting;
namespace Snap.Hutao.ViewModel;
namespace Snap.Hutao.ViewModel.Setting;
internal sealed class HomeCardOptions
{

View File

@@ -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>