mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
impl #526
This commit is contained in:
@@ -12,7 +12,7 @@ internal enum SchemeType
|
||||
/// <summary>
|
||||
/// 国际服
|
||||
/// </summary>
|
||||
Mihoyo,
|
||||
Hoyoverse,
|
||||
|
||||
/// <summary>
|
||||
/// 国服官服
|
||||
|
||||
@@ -13,6 +13,8 @@ internal sealed partial class SettingEntry
|
||||
/// </summary>
|
||||
public const string GamePath = "GamePath";
|
||||
|
||||
public const string GamePathEntries = "GamePathEntries";
|
||||
|
||||
/// <summary>
|
||||
/// PowerShell 路径
|
||||
/// </summary>
|
||||
|
||||
@@ -2189,6 +2189,9 @@
|
||||
<data name="ViewPageOpenScreenshotFolderAction" xml:space="preserve">
|
||||
<value>打开截图文件夹</value>
|
||||
</data>
|
||||
<data name="ViewPageResetGamePathAction" xml:space="preserve">
|
||||
<value>选择游戏路径</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingAboutHeader" xml:space="preserve">
|
||||
<value>关于 胡桃</value>
|
||||
</data>
|
||||
|
||||
@@ -6,6 +6,8 @@ using Snap.Hutao.Core.Windowing;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Service.Game.PathAbstraction;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
@@ -15,20 +17,12 @@ namespace Snap.Hutao.Service;
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed partial class AppOptions : DbStoreOptions
|
||||
{
|
||||
private string? gamePath;
|
||||
private string? powerShellPath;
|
||||
private bool? isEmptyHistoryWishVisible;
|
||||
private BackdropType? backdropType;
|
||||
private CultureInfo? currentCulture;
|
||||
private bool? isAdvancedLaunchOptionsEnabled;
|
||||
private string? geetestCustomCompositeUrl;
|
||||
|
||||
public string GamePath
|
||||
{
|
||||
get => GetOption(ref gamePath, SettingEntry.GamePath);
|
||||
set => SetOption(ref gamePath, SettingEntry.GamePath, value);
|
||||
}
|
||||
|
||||
public string PowerShellPath
|
||||
{
|
||||
get
|
||||
@@ -80,14 +74,6 @@ internal sealed partial class AppOptions : DbStoreOptions
|
||||
set => SetOption(ref currentCulture, SettingEntry.Culture, value, value => value.Name);
|
||||
}
|
||||
|
||||
public bool IsAdvancedLaunchOptionsEnabled
|
||||
{
|
||||
// DO NOT MOVE TO OTHER CLASS
|
||||
// We use this property in SettingPage binding
|
||||
get => GetOption(ref isAdvancedLaunchOptionsEnabled, SettingEntry.IsAdvancedLaunchOptionsEnabled);
|
||||
set => SetOption(ref isAdvancedLaunchOptionsEnabled, SettingEntry.IsAdvancedLaunchOptionsEnabled, value);
|
||||
}
|
||||
|
||||
public string GeetestCustomCompositeUrl
|
||||
{
|
||||
get => GetOption(ref geetestCustomCompositeUrl, SettingEntry.GeetestCustomCompositeUrl);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Service.Game.PathAbstraction;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
@@ -9,39 +11,6 @@ namespace Snap.Hutao.Service;
|
||||
|
||||
internal static class AppOptionsExtension
|
||||
{
|
||||
public static bool TryGetGameFolderAndFileName(this AppOptions appOptions, [NotNullWhen(true)] out string? gameFolder, [NotNullWhen(true)] out string? gameFileName)
|
||||
{
|
||||
string gamePath = appOptions.GamePath;
|
||||
|
||||
gameFolder = Path.GetDirectoryName(gamePath);
|
||||
if (string.IsNullOrEmpty(gameFolder))
|
||||
{
|
||||
gameFileName = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
gameFileName = Path.GetFileName(gamePath);
|
||||
if (string.IsNullOrEmpty(gameFileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryGetGamePathAndGameFileName(this AppOptions appOptions, out string gamePath, [NotNullWhen(true)] out string? gameFileName)
|
||||
{
|
||||
gamePath = appOptions.GamePath;
|
||||
|
||||
gameFileName = Path.GetFileName(gamePath);
|
||||
if (string.IsNullOrEmpty(gameFileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static NameValue<CultureInfo>? GetCurrentCultureForSelectionOrDefault(this AppOptions appOptions)
|
||||
{
|
||||
return appOptions.Cultures.SingleOrDefault(c => c.Value == appOptions.CurrentCulture);
|
||||
|
||||
@@ -13,11 +13,11 @@ namespace Snap.Hutao.Service.Game.Configuration;
|
||||
[Injection(InjectAs.Singleton, typeof(IGameChannelOptionsService))]
|
||||
internal sealed partial class GameChannelOptionsService : IGameChannelOptionsService
|
||||
{
|
||||
private readonly AppOptions appOptions;
|
||||
private readonly LaunchOptions launchOptions;
|
||||
|
||||
public ChannelOptions GetChannelOptions()
|
||||
{
|
||||
string gamePath = appOptions.GamePath;
|
||||
string gamePath = launchOptions.GamePath;
|
||||
string configPath = Path.Combine(Path.GetDirectoryName(gamePath) ?? string.Empty, ConfigFileName);
|
||||
bool isOversea = string.Equals(Path.GetFileName(gamePath), GenshinImpactFileName, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
@@ -38,7 +38,7 @@ internal sealed partial class GameChannelOptionsService : IGameChannelOptionsSer
|
||||
|
||||
public bool SetChannelOptions(LaunchScheme scheme)
|
||||
{
|
||||
string gamePath = appOptions.GamePath;
|
||||
string gamePath = launchOptions.GamePath;
|
||||
string? directory = Path.GetDirectoryName(gamePath);
|
||||
ArgumentException.ThrowIfNullOrEmpty(directory);
|
||||
string configPath = Path.Combine(directory, ConfigFileName);
|
||||
|
||||
@@ -5,6 +5,7 @@ using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.Game.Account;
|
||||
using Snap.Hutao.Service.Game.Configuration;
|
||||
using Snap.Hutao.Service.Game.Package;
|
||||
using Snap.Hutao.Service.Game.PathAbstraction;
|
||||
using Snap.Hutao.Service.Game.Process;
|
||||
using Snap.Hutao.Service.Game.Scheme;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
@@ -6,6 +6,8 @@ using Microsoft.UI.Windowing;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Service.Game.PathAbstraction;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using Windows.Graphics;
|
||||
using Windows.Win32.Foundation;
|
||||
@@ -24,7 +26,10 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
private readonly int primaryScreenHeight;
|
||||
private readonly int primaryScreenFps;
|
||||
|
||||
private string? gamePath;
|
||||
private ImmutableList<GamePathEntry>? gamePathEntries;
|
||||
private bool? isEnabled;
|
||||
private bool? isAdvancedLaunchOptionsEnabled;
|
||||
private bool? isFullScreen;
|
||||
private bool? isBorderless;
|
||||
private bool? isExclusive;
|
||||
@@ -85,12 +90,32 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
}
|
||||
}
|
||||
|
||||
public string GamePath
|
||||
{
|
||||
get => GetOption(ref gamePath, SettingEntry.GamePath);
|
||||
set => SetOption(ref gamePath, SettingEntry.GamePath, value);
|
||||
}
|
||||
|
||||
public ImmutableList<GamePathEntry> GamePathEntries
|
||||
{
|
||||
// Because DbStoreOptions can't detect collection change, We use
|
||||
// ImmutableList to imply that the whole list needs to be replaced
|
||||
get => GetOption(ref gamePathEntries, SettingEntry.GamePathEntries, raw => JsonSerializer.Deserialize<ImmutableList<GamePathEntry>>(raw), []);
|
||||
set => SetOption(ref gamePathEntries, SettingEntry.GamePathEntries, value, value => JsonSerializer.Serialize(value));
|
||||
}
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => GetOption(ref isEnabled, SettingEntry.LaunchIsLaunchOptionsEnabled, true);
|
||||
set => SetOption(ref isEnabled, SettingEntry.LaunchIsLaunchOptionsEnabled, value);
|
||||
}
|
||||
|
||||
public bool IsAdvancedLaunchOptionsEnabled
|
||||
{
|
||||
get => GetOption(ref isAdvancedLaunchOptionsEnabled, SettingEntry.IsAdvancedLaunchOptionsEnabled);
|
||||
set => SetOption(ref isAdvancedLaunchOptionsEnabled, SettingEntry.IsAdvancedLaunchOptionsEnabled, value);
|
||||
}
|
||||
|
||||
public bool IsFullScreen
|
||||
{
|
||||
get => GetOption(ref isFullScreen, SettingEntry.LaunchIsFullScreen);
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Service.Game.PathAbstraction;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
|
||||
namespace Snap.Hutao.Service.Game;
|
||||
|
||||
internal static class LaunchOptionsExtension
|
||||
{
|
||||
public static bool TryGetGameFolderAndFileName(this LaunchOptions options, [NotNullWhen(true)] out string? gameFolder, [NotNullWhen(true)] out string? gameFileName)
|
||||
{
|
||||
string gamePath = options.GamePath;
|
||||
|
||||
gameFolder = Path.GetDirectoryName(gamePath);
|
||||
if (string.IsNullOrEmpty(gameFolder))
|
||||
{
|
||||
gameFileName = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
gameFileName = Path.GetFileName(gamePath);
|
||||
if (string.IsNullOrEmpty(gameFileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryGetGamePathAndGameFileName(this LaunchOptions options, out string gamePath, [NotNullWhen(true)] out string? gameFileName)
|
||||
{
|
||||
gamePath = options.GamePath;
|
||||
|
||||
gameFileName = Path.GetFileName(gamePath);
|
||||
if (string.IsNullOrEmpty(gameFileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static ImmutableList<GamePathEntry> GetGamePathEntries(this LaunchOptions options, out GamePathEntry? entry)
|
||||
{
|
||||
string gamePath = options.GamePath;
|
||||
|
||||
if (string.IsNullOrEmpty(gamePath))
|
||||
{
|
||||
entry = default;
|
||||
return options.GamePathEntries;
|
||||
}
|
||||
|
||||
if (options.GamePathEntries.SingleOrDefault(entry => string.Equals(entry.Path, options.GamePath, StringComparison.OrdinalIgnoreCase)) is { } existed)
|
||||
{
|
||||
entry = existed;
|
||||
return options.GamePathEntries;
|
||||
}
|
||||
|
||||
entry = GamePathEntry.Create(options.GamePath);
|
||||
return [.. options.GamePathEntries, entry];
|
||||
}
|
||||
|
||||
public static ImmutableList<GamePathEntry> RemoveGamePathEntry(this LaunchOptions options, GamePathEntry? entry, out GamePathEntry? selected)
|
||||
{
|
||||
if (entry is not null)
|
||||
{
|
||||
if (string.Equals(options.GamePath, entry.Path, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
options.GamePath = string.Empty;
|
||||
}
|
||||
|
||||
options.GamePathEntries = options.GamePathEntries.Remove(entry);
|
||||
}
|
||||
|
||||
return options.GetGamePathEntries(out selected);
|
||||
}
|
||||
|
||||
public static ImmutableList<GamePathEntry> UpdateGamePathAndRefreshEntries(this LaunchOptions options, string gamePath)
|
||||
{
|
||||
options.GamePath = gamePath;
|
||||
ImmutableList<GamePathEntry> entries = options.GetGamePathEntries(out _);
|
||||
options.GamePathEntries = entries;
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,12 @@ internal sealed partial class GamePackageService : IGamePackageService
|
||||
{
|
||||
private readonly PackageConverter packageConverter;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly LaunchOptions launchOptions;
|
||||
private readonly ITaskContext taskContext;
|
||||
private readonly AppOptions appOptions;
|
||||
|
||||
public async ValueTask<bool> EnsureGameResourceAsync(LaunchScheme launchScheme, IProgress<PackageReplaceStatus> progress)
|
||||
{
|
||||
if (!appOptions.TryGetGameFolderAndFileName(out string? gameFolder, out string? gameFileName))
|
||||
if (!launchOptions.TryGetGameFolderAndFileName(out string? gameFolder, out string? gameFileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -58,7 +58,7 @@ internal sealed partial class GamePackageService : IGamePackageService
|
||||
string exeName = launchScheme.IsOversea ? GenshinImpactFileName : YuanShenFileName;
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
appOptions.GamePath = Path.Combine(gameFolder, exeName);
|
||||
launchOptions.UpdateGamePathAndRefreshEntries(Path.Combine(gameFolder, exeName));
|
||||
}
|
||||
|
||||
await packageConverter.EnsureDeprecatedFilesAndSdkAsync(resource, gameFolder).ConfigureAwait(false);
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Game.PathAbstraction;
|
||||
|
||||
internal sealed class GamePathEntry
|
||||
{
|
||||
[JsonPropertyName("Path")]
|
||||
public string Path { get; set; } = default!;
|
||||
|
||||
[JsonIgnore]
|
||||
public GamePathKind Kind { get => GetKind(Path); }
|
||||
|
||||
public static GamePathEntry Create(string path)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Path = path,
|
||||
};
|
||||
}
|
||||
|
||||
private static GamePathKind GetKind(string path)
|
||||
{
|
||||
return GamePathKind.None;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Game.PathAbstraction;
|
||||
|
||||
internal enum GamePathKind
|
||||
{
|
||||
None,
|
||||
ChineseClient,
|
||||
OverseaClient,
|
||||
ChineseCloud,
|
||||
OverseaCloud,
|
||||
}
|
||||
@@ -2,20 +2,21 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Service.Game.Locator;
|
||||
using Snap.Hutao.Service.Game.PathAbstraction;
|
||||
|
||||
namespace Snap.Hutao.Service.Game;
|
||||
namespace Snap.Hutao.Service.Game.PathAbstraction;
|
||||
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton, typeof(IGamePathService))]
|
||||
internal sealed partial class GamePathService : IGamePathService
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly AppOptions appOptions;
|
||||
private readonly LaunchOptions launchOptions;
|
||||
|
||||
public async ValueTask<ValueResult<bool, string>> SilentGetGamePathAsync()
|
||||
{
|
||||
// Cannot find in setting
|
||||
if (string.IsNullOrEmpty(appOptions.GamePath))
|
||||
if (string.IsNullOrEmpty(launchOptions.GamePath))
|
||||
{
|
||||
IGameLocatorFactory locatorFactory = serviceProvider.GetRequiredService<IGameLocatorFactory>();
|
||||
|
||||
@@ -40,7 +41,7 @@ internal sealed partial class GamePathService : IGamePathService
|
||||
if (isOk)
|
||||
{
|
||||
// Save result.
|
||||
appOptions.GamePath = path;
|
||||
launchOptions.UpdateGamePathAndRefreshEntries(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -48,9 +49,9 @@ internal sealed partial class GamePathService : IGamePathService
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(appOptions.GamePath))
|
||||
if (!string.IsNullOrEmpty(launchOptions.GamePath))
|
||||
{
|
||||
return new(true, appOptions.GamePath);
|
||||
return new(true, launchOptions.GamePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Game;
|
||||
namespace Snap.Hutao.Service.Game.PathAbstraction;
|
||||
|
||||
internal interface IGamePathService
|
||||
{
|
||||
@@ -52,7 +52,7 @@ internal sealed partial class GameProcessService : IGameProcessService
|
||||
return;
|
||||
}
|
||||
|
||||
if (!appOptions.TryGetGamePathAndGameFileName(out string gamePath, out string? gameFileName))
|
||||
if (!launchOptions.TryGetGamePathAndGameFileName(out string gamePath, out string? gameFileName))
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrEmpty(gamePath);
|
||||
return; // null check passing, actually never reach.
|
||||
@@ -73,7 +73,7 @@ internal sealed partial class GameProcessService : IGameProcessService
|
||||
await Starward.LaunchForPlayTimeStatisticsAsync(isOversea).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (runtimeOptions.IsElevated && appOptions.IsAdvancedLaunchOptionsEnabled && launchOptions.UnlockFps)
|
||||
if (runtimeOptions.IsElevated && launchOptions.IsAdvancedLaunchOptionsEnabled && launchOptions.UnlockFps)
|
||||
{
|
||||
progress.Report(new(LaunchPhase.UnlockingFps, SH.ServiceGameLaunchPhaseUnlockingFps));
|
||||
try
|
||||
|
||||
@@ -38,7 +38,12 @@
|
||||
|
||||
<DataTemplate x:Key="GameAccountListTemplate">
|
||||
<Grid>
|
||||
<StackPanel Margin="0,12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0" Margin="0,12">
|
||||
<TextBlock Text="{Binding Name}"/>
|
||||
<TextBlock
|
||||
Opacity="0.8"
|
||||
@@ -47,9 +52,9 @@
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
x:Name="ButtonPanel"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal"
|
||||
Visibility="Collapsed">
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
MinWidth="48"
|
||||
Margin="4,8"
|
||||
@@ -78,264 +83,290 @@
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
ToolTipService.ToolTip="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountRemoveToolTip}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<Grid.Resources>
|
||||
<Storyboard x:Name="ButtonPanelVisibleStoryboard">
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonPanel" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<Visibility>Visible</Visibility>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
|
||||
<Storyboard x:Name="ButtonPanelCollapsedStoryboard">
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonPanel" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<Visibility>Collapsed</Visibility>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</Grid.Resources>
|
||||
|
||||
<mxi:Interaction.Behaviors>
|
||||
<mxic:EventTriggerBehavior EventName="PointerEntered">
|
||||
<mxim:ControlStoryboardAction Storyboard="{StaticResource ButtonPanelVisibleStoryboard}"/>
|
||||
</mxic:EventTriggerBehavior>
|
||||
<mxic:EventTriggerBehavior EventName="PointerExited">
|
||||
<mxim:ControlStoryboardAction Storyboard="{StaticResource ButtonPanelCollapsedStoryboard}"/>
|
||||
</mxic:EventTriggerBehavior>
|
||||
</mxi:Interaction.Behaviors>
|
||||
<DataTemplate x:Key="GamePathEntryListTemplate">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Padding="0,16"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Path}"/>
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
MinWidth="48"
|
||||
Margin="4,8,0,8"
|
||||
VerticalAlignment="Stretch"
|
||||
Command="{Binding DataContext.RemoveGamePathEntryCommand, Source={StaticResource BindingProxy}}"
|
||||
CommandParameter="{Binding}"
|
||||
Content=""
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
ToolTipService.ToolTip="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountRemoveToolTip}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</Page.Resources>
|
||||
<Grid>
|
||||
<Grid
|
||||
Height="{StaticResource AppBarThemeCompactHeight}"
|
||||
VerticalAlignment="Top"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,0,0,1"
|
||||
IsHitTestVisible="False"/>
|
||||
<Pivot>
|
||||
<Pivot.RightHeader>
|
||||
<CommandBar DefaultLabelPosition="Right">
|
||||
<CommandBar.Content>
|
||||
<TextBlock
|
||||
Margin="12,14,12,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding LaunchStatusOptions.LaunchStatus.Description}"/>
|
||||
</CommandBar.Content>
|
||||
<AppBarButton
|
||||
Command="{Binding OpenScreenshotFolderCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="{shcm:ResourceString Name=ViewPageOpenScreenshotFolderAction}"/>
|
||||
<AppBarButton
|
||||
Command="{Binding LaunchCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="{shcm:ResourceString Name=ViewPageLaunchGameAction}"/>
|
||||
</CommandBar>
|
||||
</Pivot.RightHeader>
|
||||
<Grid Visibility="{Binding GamePathSelectedAndValid, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Grid
|
||||
Height="{StaticResource AppBarThemeCompactHeight}"
|
||||
VerticalAlignment="Top"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,0,0,1"
|
||||
IsHitTestVisible="False"/>
|
||||
<Pivot>
|
||||
<Pivot.RightHeader>
|
||||
<CommandBar DefaultLabelPosition="Right">
|
||||
<CommandBar.Content>
|
||||
<TextBlock
|
||||
Margin="12,14,12,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding LaunchStatusOptions.LaunchStatus.Description}"/>
|
||||
</CommandBar.Content>
|
||||
<AppBarButton
|
||||
Command="{Binding OpenScreenshotFolderCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="{shcm:ResourceString Name=ViewPageOpenScreenshotFolderAction}"/>
|
||||
<AppBarButton
|
||||
Command="{Binding ResetGamePathCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="{shcm:ResourceString Name=ViewPageResetGamePathAction}"/>
|
||||
<AppBarButton
|
||||
Command="{Binding LaunchCommand}"
|
||||
Icon="{shcm:FontIcon Glyph=}"
|
||||
Label="{shcm:ResourceString Name=ViewPageLaunchGameAction}"/>
|
||||
</CommandBar>
|
||||
</Pivot.RightHeader>
|
||||
|
||||
<PivotItem Header="{shcm:ResourceString Name=ViewPageLaunchGameOptionsHeader}">
|
||||
<ScrollViewer Grid.RowSpan="2">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition MaxWidth="1000"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Margin="16" Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<InfoBar
|
||||
IsClosable="False"
|
||||
IsOpen="True"
|
||||
Message="{shcm:ResourceString Name=ViewPageLaunchGameConfigurationSaveHint}"
|
||||
Severity="Informational"/>
|
||||
|
||||
<!-- 文件 -->
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageLaunchGameFileHeader}"/>
|
||||
<cwc:SettingsCard
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameSwitchSchemeHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsEnabled="{Binding RuntimeOptions.IsElevated}">
|
||||
<cwc:SettingsCard.Description>
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageLaunchGameSwitchSchemeDescription}"/>
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource SystemControlErrorTextForegroundBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewPageLaunchGameSwitchSchemeWarning}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard.Description>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<shvc:Elevation Margin="0,0,36,0" Visibility="{Binding RuntimeOptions.IsElevated, Converter={StaticResource BoolToVisibilityRevertConverter}}"/>
|
||||
<shc:SizeRestrictedContentControl>
|
||||
<shccs:ComboBox2
|
||||
DisplayMemberPath="DisplayName"
|
||||
EnableMemberPath="IsNotCompatOnly"
|
||||
ItemsSource="{Binding KnownSchemes}"
|
||||
SelectedItem="{Binding SelectedScheme, Mode=TwoWay}"
|
||||
Style="{StaticResource DefaultComboBoxStyle}"/>
|
||||
</shc:SizeRestrictedContentControl>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard>
|
||||
|
||||
<!-- 注册表 -->
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageLaunchGameRegistryHeader}"/>
|
||||
<cwc:SettingsCard
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountDetectAction}"
|
||||
Command="{Binding DetectGameAccountCommand}"
|
||||
Description="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsClickEnabled="True"/>
|
||||
<Border Style="{StaticResource BorderCardStyle}">
|
||||
<ListView
|
||||
ItemTemplate="{StaticResource GameAccountListTemplate}"
|
||||
ItemsSource="{Binding GameAccounts}"
|
||||
SelectedItem="{Binding SelectedGameAccount, Mode=TwoWay}"/>
|
||||
</Border>
|
||||
|
||||
<!-- 进程 -->
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageLaunchGameProcessHeader}"/>
|
||||
<cwc:SettingsExpander
|
||||
shch:SettingsExpanderHelper.IsItemsEnabled="{Binding LaunchOptions.IsEnabled}"
|
||||
Description="{shcm:ResourceString Name=ViewPageLaunchGameArgumentsDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameArgumentsHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsExpanded="True">
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsEnabled, Mode=TwoWay}"/>
|
||||
<cwc:SettingsExpander.Items>
|
||||
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceExclusiveDescription}" Header="-window-mode exclusive">
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsExclusive, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceFullscreenDescription}" Header="-screen-fullscreen">
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsFullScreen, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceBorderlessDescription}" Header="-popupwindow">
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsBorderless, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceAspectRatioDescription}" Header="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceAspectRatioHeader}">
|
||||
<shc:SizeRestrictedContentControl Margin="0,0,136,0">
|
||||
<ComboBox
|
||||
ItemsSource="{Binding LaunchOptions.AspectRatios}"
|
||||
PlaceholderText="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceAspectRatioPlaceHolder}"
|
||||
SelectedItem="{Binding LaunchOptions.SelectedAspectRatio, Mode=TwoWay}"/>
|
||||
</shc:SizeRestrictedContentControl>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceScreenWidthDescription}" Header="-screen-width">
|
||||
<StackPanel Orientation="Horizontal" Spacing="16">
|
||||
<NumberBox
|
||||
Width="156"
|
||||
Padding="12,6,0,0"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{Binding LaunchOptions.IsScreenWidthEnabled}"
|
||||
Value="{Binding LaunchOptions.ScreenWidth, Mode=TwoWay}"/>
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsScreenWidthEnabled, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceScreenHeightDescription}" Header="-screen-height">
|
||||
<StackPanel Orientation="Horizontal" Spacing="16">
|
||||
<NumberBox
|
||||
Width="156"
|
||||
Padding="12,6,0,0"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{Binding LaunchOptions.IsScreenHeightEnabled}"
|
||||
Value="{Binding LaunchOptions.ScreenHeight, Mode=TwoWay}"/>
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsScreenHeightEnabled, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameMonitorsDescription}" Header="-monitor">
|
||||
<StackPanel Orientation="Horizontal" Spacing="16">
|
||||
<shc:SizeRestrictedContentControl>
|
||||
<ComboBox
|
||||
DisplayMemberPath="Name"
|
||||
IsEnabled="{Binding LaunchOptions.IsMonitorEnabled}"
|
||||
ItemsSource="{Binding LaunchOptions.Monitors}"
|
||||
SelectedItem="{Binding LaunchOptions.Monitor, Mode=TwoWay}"/>
|
||||
</shc:SizeRestrictedContentControl>
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsMonitorEnabled, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard>
|
||||
</cwc:SettingsExpander.Items>
|
||||
</cwc:SettingsExpander>
|
||||
<cwc:SettingsCard
|
||||
Description="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsEnabled="{Binding RuntimeOptions.IsElevated}"
|
||||
Visibility="{Binding AppOptions.IsAdvancedLaunchOptionsEnabled, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<shvc:Elevation Margin="0,0,36,0" Visibility="{Binding RuntimeOptions.IsElevated, Converter={StaticResource BoolToVisibilityRevertConverter}}"/>
|
||||
<NumberBox
|
||||
MinWidth="156"
|
||||
Padding="10,8,0,0"
|
||||
Maximum="720"
|
||||
Minimum="60"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{Binding LaunchOptions.TargetFps, Mode=TwoWay}"/>
|
||||
<ToggleSwitch
|
||||
Width="120"
|
||||
IsOn="{Binding LaunchOptions.UnlockFps, Mode=TwoWay}"
|
||||
OffContent="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsOff}"
|
||||
OnContent="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsOn}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard>
|
||||
|
||||
<!-- 进程间 -->
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageLaunchGameInterProcessHeader}"/>
|
||||
<cwc:SettingsCard
|
||||
Description="{shcm:ResourceString Name=ViewPageLaunchGamePlayTimeDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGamePlayTimeHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}">
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.UseStarwardPlayTimeStatistics, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard
|
||||
Description="{shcm:ResourceString Name=ViewPageLaunchGameDiscordActivityDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameDiscordActivityHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}">
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.SetDiscordActivityWhenPlaying, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</PivotItem>
|
||||
<PivotItem Header="{shcm:ResourceString Name=ViewPageLaunchGameResourceHeader}">
|
||||
<Grid>
|
||||
<ScrollViewer Visibility="{Binding GameResource, Converter={StaticResource EmptyObjectToBoolConverter}}">
|
||||
<PivotItem Header="{shcm:ResourceString Name=ViewPageLaunchGameOptionsHeader}">
|
||||
<ScrollViewer Grid.RowSpan="2">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition MaxWidth="1000"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel>
|
||||
<shvc:LaunchGameResourceExpander
|
||||
Margin="16,16,16,0"
|
||||
cw:Effects.Shadow="{ThemeResource CompatCardShadow}"
|
||||
DataContext="{Binding GameResource.PreDownloadGame.Latest, Mode=OneWay}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameResourcePreDownloadHeader}"
|
||||
Visibility="{Binding FallbackValue={StaticResource VisibilityCollapsed}, Converter={StaticResource EmptyObjectToVisibilityConverter}}"/>
|
||||
<ItemsControl
|
||||
Margin="0,0,0,0"
|
||||
ItemTemplate="{StaticResource GameResourceTemplate}"
|
||||
ItemsSource="{Binding GameResource.PreDownloadGame.Diffs, Mode=OneWay}"/>
|
||||
<shvc:LaunchGameResourceExpander
|
||||
Margin="16,16,16,0"
|
||||
cw:Effects.Shadow="{ThemeResource CompatCardShadow}"
|
||||
DataContext="{Binding GameResource.Game.Latest, Mode=OneWay}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameResourceLatestHeader}"/>
|
||||
<ItemsControl
|
||||
Margin="0,0,0,16"
|
||||
ItemTemplate="{StaticResource GameResourceTemplate}"
|
||||
ItemsSource="{Binding GameResource.Game.Diffs, Mode=OneWay}"/>
|
||||
<StackPanel Margin="16" Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<InfoBar
|
||||
IsClosable="False"
|
||||
IsOpen="True"
|
||||
Message="{shcm:ResourceString Name=ViewPageLaunchGameConfigurationSaveHint}"
|
||||
Severity="Informational"/>
|
||||
|
||||
<!-- 文件 -->
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageLaunchGameFileHeader}"/>
|
||||
<cwc:SettingsCard
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameSwitchSchemeHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsEnabled="{Binding RuntimeOptions.IsElevated}">
|
||||
<cwc:SettingsCard.Description>
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageLaunchGameSwitchSchemeDescription}"/>
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource SystemControlErrorTextForegroundBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewPageLaunchGameSwitchSchemeWarning}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard.Description>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<shvc:Elevation Margin="0,0,36,0" Visibility="{Binding RuntimeOptions.IsElevated, Converter={StaticResource BoolToVisibilityRevertConverter}}"/>
|
||||
<shc:SizeRestrictedContentControl>
|
||||
<shccs:ComboBox2
|
||||
DisplayMemberPath="DisplayName"
|
||||
EnableMemberPath="IsNotCompatOnly"
|
||||
ItemsSource="{Binding KnownSchemes}"
|
||||
SelectedItem="{Binding SelectedScheme, Mode=TwoWay}"
|
||||
Style="{StaticResource DefaultComboBoxStyle}"/>
|
||||
</shc:SizeRestrictedContentControl>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard>
|
||||
|
||||
<!-- 注册表 -->
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageLaunchGameRegistryHeader}"/>
|
||||
<cwc:SettingsCard
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountDetectAction}"
|
||||
Command="{Binding DetectGameAccountCommand}"
|
||||
Description="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsClickEnabled="True"/>
|
||||
<Border Style="{StaticResource BorderCardStyle}">
|
||||
<ListView
|
||||
ItemTemplate="{StaticResource GameAccountListTemplate}"
|
||||
ItemsSource="{Binding GameAccounts}"
|
||||
SelectedItem="{Binding SelectedGameAccount, Mode=TwoWay}"/>
|
||||
</Border>
|
||||
|
||||
<!-- 进程 -->
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageLaunchGameProcessHeader}"/>
|
||||
<cwc:SettingsExpander
|
||||
shch:SettingsExpanderHelper.IsItemsEnabled="{Binding LaunchOptions.IsEnabled}"
|
||||
Description="{shcm:ResourceString Name=ViewPageLaunchGameArgumentsDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameArgumentsHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsExpanded="True">
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsEnabled, Mode=TwoWay}"/>
|
||||
<cwc:SettingsExpander.Items>
|
||||
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceExclusiveDescription}" Header="-window-mode exclusive">
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsExclusive, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceFullscreenDescription}" Header="-screen-fullscreen">
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsFullScreen, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceBorderlessDescription}" Header="-popupwindow">
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsBorderless, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceAspectRatioDescription}" Header="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceAspectRatioHeader}">
|
||||
<shc:SizeRestrictedContentControl Margin="0,0,136,0">
|
||||
<ComboBox
|
||||
ItemsSource="{Binding LaunchOptions.AspectRatios}"
|
||||
PlaceholderText="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceAspectRatioPlaceHolder}"
|
||||
SelectedItem="{Binding LaunchOptions.SelectedAspectRatio, Mode=TwoWay}"/>
|
||||
</shc:SizeRestrictedContentControl>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceScreenWidthDescription}" Header="-screen-width">
|
||||
<StackPanel Orientation="Horizontal" Spacing="16">
|
||||
<NumberBox
|
||||
Width="156"
|
||||
Padding="12,6,0,0"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{Binding LaunchOptions.IsScreenWidthEnabled}"
|
||||
Value="{Binding LaunchOptions.ScreenWidth, Mode=TwoWay}"/>
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsScreenWidthEnabled, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceScreenHeightDescription}" Header="-screen-height">
|
||||
<StackPanel Orientation="Horizontal" Spacing="16">
|
||||
<NumberBox
|
||||
Width="156"
|
||||
Padding="12,6,0,0"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{Binding LaunchOptions.IsScreenHeightEnabled}"
|
||||
Value="{Binding LaunchOptions.ScreenHeight, Mode=TwoWay}"/>
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsScreenHeightEnabled, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard Description="{shcm:ResourceString Name=ViewPageLaunchGameMonitorsDescription}" Header="-monitor">
|
||||
<StackPanel Orientation="Horizontal" Spacing="16">
|
||||
<shc:SizeRestrictedContentControl>
|
||||
<ComboBox
|
||||
DisplayMemberPath="Name"
|
||||
IsEnabled="{Binding LaunchOptions.IsMonitorEnabled}"
|
||||
ItemsSource="{Binding LaunchOptions.Monitors}"
|
||||
SelectedItem="{Binding LaunchOptions.Monitor, Mode=TwoWay}"/>
|
||||
</shc:SizeRestrictedContentControl>
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsMonitorEnabled, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard>
|
||||
</cwc:SettingsExpander.Items>
|
||||
</cwc:SettingsExpander>
|
||||
<cwc:SettingsCard
|
||||
Description="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsEnabled="{Binding RuntimeOptions.IsElevated}"
|
||||
Visibility="{Binding LaunchOptions.IsAdvancedLaunchOptionsEnabled, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<shvc:Elevation Margin="0,0,36,0" Visibility="{Binding RuntimeOptions.IsElevated, Converter={StaticResource BoolToVisibilityRevertConverter}}"/>
|
||||
<NumberBox
|
||||
MinWidth="156"
|
||||
Padding="10,8,0,0"
|
||||
Maximum="720"
|
||||
Minimum="60"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{Binding LaunchOptions.TargetFps, Mode=TwoWay}"/>
|
||||
<ToggleSwitch
|
||||
Width="120"
|
||||
IsOn="{Binding LaunchOptions.UnlockFps, Mode=TwoWay}"
|
||||
OffContent="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsOff}"
|
||||
OnContent="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsOn}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard>
|
||||
|
||||
<!-- 进程间 -->
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageLaunchGameInterProcessHeader}"/>
|
||||
<cwc:SettingsCard
|
||||
Description="{shcm:ResourceString Name=ViewPageLaunchGamePlayTimeDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGamePlayTimeHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}">
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.UseStarwardPlayTimeStatistics, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard
|
||||
Description="{shcm:ResourceString Name=ViewPageLaunchGameDiscordActivityDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameDiscordActivityHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}">
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.SetDiscordActivityWhenPlaying, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
<shvc:LoadingView IsLoading="{Binding GameResource, Converter={StaticResource EmptyObjectToBoolRevertConverter}}"/>
|
||||
</Grid>
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
</PivotItem>
|
||||
<PivotItem Header="{shcm:ResourceString Name=ViewPageLaunchGameResourceHeader}">
|
||||
<Grid>
|
||||
<ScrollViewer Visibility="{Binding GameResource, Converter={StaticResource EmptyObjectToBoolConverter}}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition MaxWidth="1000"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel>
|
||||
<shvc:LaunchGameResourceExpander
|
||||
Margin="16,16,16,0"
|
||||
cw:Effects.Shadow="{ThemeResource CompatCardShadow}"
|
||||
DataContext="{Binding GameResource.PreDownloadGame.Latest, Mode=OneWay}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameResourcePreDownloadHeader}"
|
||||
Visibility="{Binding FallbackValue={StaticResource VisibilityCollapsed}, Converter={StaticResource EmptyObjectToVisibilityConverter}}"/>
|
||||
<ItemsControl
|
||||
Margin="0,0,0,0"
|
||||
ItemTemplate="{StaticResource GameResourceTemplate}"
|
||||
ItemsSource="{Binding GameResource.PreDownloadGame.Diffs, Mode=OneWay}"/>
|
||||
<shvc:LaunchGameResourceExpander
|
||||
Margin="16,16,16,0"
|
||||
cw:Effects.Shadow="{ThemeResource CompatCardShadow}"
|
||||
DataContext="{Binding GameResource.Game.Latest, Mode=OneWay}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameResourceLatestHeader}"/>
|
||||
<ItemsControl
|
||||
Margin="0,0,0,16"
|
||||
ItemTemplate="{StaticResource GameResourceTemplate}"
|
||||
ItemsSource="{Binding GameResource.Game.Diffs, Mode=OneWay}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
<shvc:LoadingView IsLoading="{Binding GameResource, Converter={StaticResource EmptyObjectToBoolRevertConverter}}"/>
|
||||
</Grid>
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
</Grid>
|
||||
<Grid Visibility="{Binding GamePathSelectedAndValid, Converter={StaticResource BoolToVisibilityRevertConverter}}">
|
||||
<StackPanel
|
||||
MaxWidth="600"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Spacing="3">
|
||||
<Border Style="{ThemeResource BorderCardStyle}">
|
||||
<ListView
|
||||
ItemTemplate="{StaticResource GamePathEntryListTemplate}"
|
||||
ItemsSource="{Binding GamePathEntries}"
|
||||
SelectedItem="{Binding SelectedGamePathEntry, Mode=TwoWay}"/>
|
||||
</Border>
|
||||
<cwc:SettingsCard
|
||||
ActionIcon="{shcm:FontIcon Glyph=}"
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageSettingSetGamePathAction}"
|
||||
Command="{Binding SetGamePathCommand}"
|
||||
Header="{shcm:ResourceString Name=ViewPageSettingSetGamePathHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsClickEnabled="True">
|
||||
<cwc:SettingsCard.Description>
|
||||
<StackPanel>
|
||||
<TextBlock Foreground="{ThemeResource SystemErrorTextColor}" Text="{shcm:ResourceString Name=ViewPageSettingSetGamePathHint}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard.Description>
|
||||
</cwc:SettingsCard>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</shc:ScopedPage>
|
||||
@@ -307,20 +307,6 @@
|
||||
</cwc:SettingsExpander>
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageSettingGameHeader}"/>
|
||||
<cwc:SettingsCard
|
||||
ActionIcon="{shcm:FontIcon Glyph=}"
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageSettingSetGamePathAction}"
|
||||
Command="{Binding SetGamePathCommand}"
|
||||
Header="{shcm:ResourceString Name=ViewPageSettingSetGamePathHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||
IsClickEnabled="True">
|
||||
<cwc:SettingsCard.Description>
|
||||
<StackPanel>
|
||||
<TextBlock Foreground="{ThemeResource SystemErrorTextColor}" Text="{shcm:ResourceString Name=ViewPageSettingSetGamePathHint}"/>
|
||||
<TextBlock Text="{Binding AppOptions.GamePath}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard.Description>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard
|
||||
ActionIcon="{shcm:FontIcon Glyph=}"
|
||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageSettingSetGamePathAction}"
|
||||
@@ -418,13 +404,13 @@
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
|
||||
Text="{shcm:ResourceString Name=ViewPageSettingFeaturesDangerousHint}"
|
||||
Visibility="{Binding AppOptions.IsAdvancedLaunchOptionsEnabled, Converter={StaticResource BoolToVisibilityConverter}}"/>
|
||||
<TextBlock Text="{shcm:ResourceString Name=ViewPageSettingIsAdvancedLaunchOptionsEnabledDescription}" Visibility="{Binding AppOptions.IsAdvancedLaunchOptionsEnabled, Converter={StaticResource BoolToVisibilityRevertConverter}}"/>
|
||||
Visibility="{Binding LaunchOptions.IsAdvancedLaunchOptionsEnabled, Converter={StaticResource BoolToVisibilityConverter}}"/>
|
||||
<TextBlock Text="{shcm:ResourceString Name=ViewPageSettingIsAdvancedLaunchOptionsEnabledDescription}" Visibility="{Binding LaunchOptions.IsAdvancedLaunchOptionsEnabled, Converter={StaticResource BoolToVisibilityRevertConverter}}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard.Description>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<shvc:Elevation Visibility="{Binding HutaoOptions.IsElevated, Converter={StaticResource BoolToVisibilityRevertConverter}}"/>
|
||||
<ToggleSwitch Width="120" IsOn="{Binding AppOptions.IsAdvancedLaunchOptionsEnabled, Mode=TwoWay}"/>
|
||||
<ToggleSwitch Width="120" IsOn="{Binding LaunchOptions.IsAdvancedLaunchOptionsEnabled, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard
|
||||
@@ -435,10 +421,7 @@
|
||||
IsClickEnabled="True">
|
||||
<cwc:SettingsCard.Description>
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
|
||||
Text="{shcm:ResourceString Name=ViewPageSettingDangerousHint}"
|
||||
Visibility="{Binding AppOptions.IsAdvancedLaunchOptionsEnabled, Converter={StaticResource BoolToVisibilityConverter}}"/>
|
||||
<TextBlock Foreground="{ThemeResource SystemFillColorCriticalBrush}" Text="{shcm:ResourceString Name=ViewPageSettingDangerousHint}"/>
|
||||
<TextBlock Text="{shcm:ResourceString Name=ViewPageSettingDeleteUserDescription}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard.Description>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Snap.Hutao.Control.Extension;
|
||||
using Snap.Hutao.Core;
|
||||
@@ -11,13 +12,15 @@ using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service;
|
||||
using Snap.Hutao.Service.Game;
|
||||
using Snap.Hutao.Service.Game.Configuration;
|
||||
using Snap.Hutao.Service.Game.Locator;
|
||||
using Snap.Hutao.Service.Game.Package;
|
||||
using Snap.Hutao.Service.Game.PathAbstraction;
|
||||
using Snap.Hutao.Service.Game.Scheme;
|
||||
using Snap.Hutao.Service.Navigation;
|
||||
using Snap.Hutao.Service.Notification;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.View.Dialog;
|
||||
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
|
||||
@@ -38,7 +41,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
|
||||
|
||||
private readonly IContentDialogFactory contentDialogFactory;
|
||||
private readonly LaunchStatusOptions launchStatusOptions;
|
||||
private readonly INavigationService navigationService;
|
||||
private readonly IGameLocatorFactory gameLocatorFactory;
|
||||
private readonly IProgressFactory progressFactory;
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly ResourceClient resourceClient;
|
||||
@@ -54,6 +57,9 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
|
||||
private ObservableCollection<GameAccount>? gameAccounts;
|
||||
private GameAccount? selectedGameAccount;
|
||||
private GameResource? gameResource;
|
||||
private bool gamePathSelectedAndValid;
|
||||
private ImmutableList<GamePathEntry> gamePathEntries;
|
||||
private GamePathEntry? selectedGamePathEntry;
|
||||
|
||||
public List<LaunchScheme> KnownSchemes { get; } = KnownLaunchSchemes.Get();
|
||||
|
||||
@@ -99,68 +105,127 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
|
||||
|
||||
public GameResource? GameResource { get => gameResource; set => SetProperty(ref gameResource, value); }
|
||||
|
||||
protected override async ValueTask<bool> InitializeUIAsync()
|
||||
public bool GamePathSelectedAndValid
|
||||
{
|
||||
if (File.Exists(AppOptions.GamePath))
|
||||
get => gamePathSelectedAndValid;
|
||||
set
|
||||
{
|
||||
try
|
||||
if (SetProperty(ref gamePathSelectedAndValid, value) && value)
|
||||
{
|
||||
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
|
||||
InitializeUICoreAsync().SafeForget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ImmutableList<GamePathEntry> GamePathEntries { get => gamePathEntries; set => SetProperty(ref gamePathEntries, value); }
|
||||
|
||||
public GamePathEntry? SelectedGamePathEntry
|
||||
{
|
||||
get => selectedGamePathEntry;
|
||||
set => UpdateSelectedGamePathEntry(value, true);
|
||||
}
|
||||
|
||||
protected override ValueTask<bool> InitializeUIAsync()
|
||||
{
|
||||
GamePathEntries = launchOptions.GetGamePathEntries(out GamePathEntry? entry);
|
||||
SelectedGamePathEntry = entry;
|
||||
return ValueTask.FromResult(true);
|
||||
}
|
||||
|
||||
private async ValueTask InitializeUICoreAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
|
||||
{
|
||||
ChannelOptions options = gameService.GetChannelOptions();
|
||||
if (string.IsNullOrEmpty(options.ConfigFilePath))
|
||||
{
|
||||
ChannelOptions options = gameService.GetChannelOptions();
|
||||
if (string.IsNullOrEmpty(options.ConfigFilePath))
|
||||
try
|
||||
{
|
||||
try
|
||||
SelectedScheme = KnownSchemes.Single(scheme => scheme.Equals(options));
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
if (!IgnoredInvalidChannelOptions.Contains(options))
|
||||
{
|
||||
SelectedScheme = KnownSchemes.Single(scheme => scheme.Equals(options));
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
if (!IgnoredInvalidChannelOptions.Contains(options))
|
||||
{
|
||||
// 后台收集
|
||||
throw new NotSupportedException($"不支持的 MultiChannel: {options}");
|
||||
}
|
||||
// 后台收集
|
||||
throw new NotSupportedException($"不支持的 MultiChannel: {options}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
infoBarService.Warning(SH.FormatViewModelLaunchGameMultiChannelReadFail(options.ConfigFilePath));
|
||||
}
|
||||
|
||||
ObservableCollection<GameAccount> accounts = gameService.GameAccountCollection;
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
GameAccounts = accounts;
|
||||
|
||||
// Sync uid
|
||||
if (memoryCache.TryRemove(DesiredUid, out object? value) && value is string uid)
|
||||
{
|
||||
SelectedGameAccount = GameAccounts.FirstOrDefault(g => g.AttachUid == uid);
|
||||
}
|
||||
|
||||
// Try set to the current account.
|
||||
SelectedGameAccount ??= gameService.DetectCurrentGameAccount();
|
||||
}
|
||||
}
|
||||
catch (UserdataCorruptedException ex)
|
||||
{
|
||||
infoBarService.Error(ex);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
else
|
||||
{
|
||||
infoBarService.Warning(SH.FormatViewModelLaunchGameMultiChannelReadFail(options.ConfigFilePath));
|
||||
}
|
||||
|
||||
ObservableCollection<GameAccount> accounts = gameService.GameAccountCollection;
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
GameAccounts = accounts;
|
||||
|
||||
// Sync uid
|
||||
if (memoryCache.TryRemove(DesiredUid, out object? value) && value is string uid)
|
||||
{
|
||||
SelectedGameAccount = GameAccounts.FirstOrDefault(g => g.AttachUid == uid);
|
||||
}
|
||||
|
||||
// Try set to the current account.
|
||||
SelectedGameAccount ??= gameService.DetectCurrentGameAccount();
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (UserdataCorruptedException ex)
|
||||
{
|
||||
infoBarService.Warning(SH.ViewModelLaunchGamePathInvalid);
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
await navigationService
|
||||
.NavigateAsync<View.Page.SettingPage>(INavigationAwaiter.Default, true)
|
||||
.ConfigureAwait(false);
|
||||
infoBarService.Error(ex);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSelectedGamePathEntry(GamePathEntry? value, bool setBack)
|
||||
{
|
||||
if (SetProperty(ref selectedGamePathEntry, value) && setBack)
|
||||
{
|
||||
launchOptions.GamePath = value?.Path ?? string.Empty;
|
||||
GamePathSelectedAndValid = File.Exists(launchOptions.GamePath);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("SetGamePathCommand")]
|
||||
private async Task SetGamePathAsync()
|
||||
{
|
||||
IGameLocator locator = gameLocatorFactory.Create(GameLocationSource.Manual);
|
||||
|
||||
(bool isOk, string path) = await locator.LocateGamePathAsync().ConfigureAwait(false);
|
||||
if (!isOk)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return true;
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
try
|
||||
{
|
||||
GamePathEntries = launchOptions.UpdateGamePathAndRefreshEntries(path);
|
||||
}
|
||||
catch (SqliteException ex)
|
||||
{
|
||||
// 文件夹权限不足,无法写入数据库
|
||||
infoBarService.Error(ex, SH.ViewModelSettingSetGamePathDatabaseFailedTitle);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("ResetGamePathCommand")]
|
||||
private void ResetGamePath()
|
||||
{
|
||||
SelectedGamePathEntry = default;
|
||||
}
|
||||
|
||||
[Command("RemoveGamePathEntryCommand")]
|
||||
private void RemoveGamePathEntry(GamePathEntry? entry)
|
||||
{
|
||||
GamePathEntries = launchOptions.RemoveGamePathEntry(entry, out GamePathEntry? selected);
|
||||
SelectedGamePathEntry = selected;
|
||||
}
|
||||
|
||||
[Command("LaunchCommand")]
|
||||
@@ -187,6 +252,11 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
|
||||
infoBarService.Warning(SH.ViewModelLaunchGameEnsureGameResourceFail);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
GamePathEntries = launchOptions.GetGamePathEntries(out GamePathEntry? entry);
|
||||
UpdateSelectedGamePathEntry(entry, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (SelectedGameAccount is not null)
|
||||
@@ -265,7 +335,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
|
||||
[Command("OpenScreenshotFolderCommand")]
|
||||
private async Task OpenScreenshotFolderAsync()
|
||||
{
|
||||
string game = appOptions.GamePath;
|
||||
string game = LaunchOptions.GamePath;
|
||||
string? directory = Path.GetDirectoryName(game);
|
||||
ArgumentException.ThrowIfNullOrEmpty(directory);
|
||||
string screenshot = Path.Combine(directory, "ScreenShot");
|
||||
|
||||
@@ -16,6 +16,7 @@ using Snap.Hutao.Factory.Picker;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Service;
|
||||
using Snap.Hutao.Service.GachaLog.QueryProvider;
|
||||
using Snap.Hutao.Service.Game;
|
||||
using Snap.Hutao.Service.Game.Locator;
|
||||
using Snap.Hutao.Service.Hutao;
|
||||
using Snap.Hutao.Service.Navigation;
|
||||
@@ -53,6 +54,7 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
|
||||
private readonly HutaoUserOptions hutaoUserOptions;
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
private readonly LaunchOptions launchOptions;
|
||||
private readonly HotKeyOptions hotKeyOptions;
|
||||
private readonly IUserService userService;
|
||||
private readonly ITaskContext taskContext;
|
||||
@@ -74,6 +76,8 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
|
||||
|
||||
public HotKeyOptions HotKeyOptions { get => hotKeyOptions; }
|
||||
|
||||
public LaunchOptions LaunchOptions { get => launchOptions; }
|
||||
|
||||
public HutaoPassportViewModel Passport { get => hutaoPassportViewModel; }
|
||||
|
||||
public NameValue<BackdropType>? SelectedBackdropType
|
||||
@@ -157,27 +161,6 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
|
||||
await Launcher.LaunchUriAsync(new("ms-windows-store://pdp/?productid=9PH4NXJ2JN52"));
|
||||
}
|
||||
|
||||
[Command("SetGamePathCommand")]
|
||||
private async Task SetGamePathAsync()
|
||||
{
|
||||
IGameLocator locator = gameLocatorFactory.Create(GameLocationSource.Manual);
|
||||
|
||||
(bool isOk, string path) = await locator.LocateGamePathAsync().ConfigureAwait(false);
|
||||
if (isOk)
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
try
|
||||
{
|
||||
AppOptions.GamePath = path;
|
||||
}
|
||||
catch (SqliteException ex)
|
||||
{
|
||||
// 文件夹权限不足,无法写入数据库
|
||||
infoBarService.Error(ex, SH.ViewModelSettingSetGamePathDatabaseFailedTitle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Command("SetPowerShellPathCommand")]
|
||||
private async Task SetPowerShellPathAsync()
|
||||
{
|
||||
@@ -193,7 +176,7 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
|
||||
[Command("DeleteGameWebCacheCommand")]
|
||||
private void DeleteGameWebCache()
|
||||
{
|
||||
string gamePath = AppOptions.GamePath;
|
||||
string gamePath = launchOptions.GamePath;
|
||||
|
||||
if (!string.IsNullOrEmpty(gamePath))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user