mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
20 Commits
fix/token_
...
feat/1487
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48991c2167 | ||
|
|
d50a6df14c | ||
|
|
4886904530 | ||
|
|
ac34376c13 | ||
|
|
3bcee3d149 | ||
|
|
3d3b03851e | ||
|
|
dc64302424 | ||
|
|
a2586b0ef2 | ||
|
|
eee84a338e | ||
|
|
be30362b52 | ||
|
|
38f36bbb82 | ||
|
|
704866b16a | ||
|
|
ca9783bc1b | ||
|
|
6e8e151fff | ||
|
|
b98dc9f5d3 | ||
|
|
206100d8ef | ||
|
|
1a74c7ca96 | ||
|
|
88528fa28d | ||
|
|
263cea9225 | ||
|
|
2879bd653a |
@@ -29,6 +29,7 @@
|
|||||||
<ResourceDictionary Source="ms-appx:///Control/Theme/TransitionCollection.xaml"/>
|
<ResourceDictionary Source="ms-appx:///Control/Theme/TransitionCollection.xaml"/>
|
||||||
<ResourceDictionary Source="ms-appx:///Control/Theme/Uri.xaml"/>
|
<ResourceDictionary Source="ms-appx:///Control/Theme/Uri.xaml"/>
|
||||||
<ResourceDictionary Source="ms-appx:///Control/Theme/WindowOverride.xaml"/>
|
<ResourceDictionary Source="ms-appx:///Control/Theme/WindowOverride.xaml"/>
|
||||||
|
<ResourceDictionary Source="ms-appx:///View/Card/Primitive/CardProgressBar.xaml"/>
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using CommunityToolkit.WinUI;
|
||||||
using CommunityToolkit.WinUI.Controls;
|
using CommunityToolkit.WinUI.Controls;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||||
using Snap.Hutao.Control.Extension;
|
using Snap.Hutao.Control.Extension;
|
||||||
|
|
||||||
namespace Snap.Hutao.Control.AutoSuggestBox;
|
namespace Snap.Hutao.Control.AutoSuggestBox;
|
||||||
@@ -20,6 +23,42 @@ internal sealed partial class AutoSuggestTokenBox : TokenizingTextBox
|
|||||||
TokenItemAdding += OnTokenItemAdding;
|
TokenItemAdding += OnTokenItemAdding;
|
||||||
TokenItemAdded += OnTokenItemModified;
|
TokenItemAdded += OnTokenItemModified;
|
||||||
TokenItemRemoved += OnTokenItemModified;
|
TokenItemRemoved += OnTokenItemModified;
|
||||||
|
Loaded += OnLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (this.FindDescendant("SuggestionsPopup") is Popup { Child: Border { Child: ListView listView } border })
|
||||||
|
{
|
||||||
|
IAppResourceProvider appResourceProvider = Ioc.Default.GetRequiredService<IAppResourceProvider>();
|
||||||
|
listView.Background = null;
|
||||||
|
listView.Margin = appResourceProvider.GetResource<Thickness>("AutoSuggestListPadding");
|
||||||
|
|
||||||
|
border.Background = appResourceProvider.GetResource<Microsoft.UI.Xaml.Media.Brush>("AutoSuggestBoxSuggestionsListBackground");
|
||||||
|
border.CornerRadius = new(0, 0, 8, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.FindDescendant("PART_AutoSuggestBox") is Microsoft.UI.Xaml.Controls.AutoSuggestBox autoSuggestBox)
|
||||||
|
{
|
||||||
|
autoSuggestBox.GotFocus += OnSuggestBoxFocusGot;
|
||||||
|
autoSuggestBox.LosingFocus += OnSuggestBoxFocusLosing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSuggestBoxFocusGot(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is Microsoft.UI.Xaml.Controls.AutoSuggestBox autoSuggestBox)
|
||||||
|
{
|
||||||
|
autoSuggestBox.ItemsSource = AvailableTokens.Values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSuggestBoxFocusLosing(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is Microsoft.UI.Xaml.Controls.AutoSuggestBox autoSuggestBox)
|
||||||
|
{
|
||||||
|
autoSuggestBox.ItemsSource = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFilterSuggestionRequested(Microsoft.UI.Xaml.Controls.AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
|
private void OnFilterSuggestionRequested(Microsoft.UI.Xaml.Controls.AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
|
||||||
@@ -32,9 +71,6 @@ internal sealed partial class AutoSuggestTokenBox : TokenizingTextBox
|
|||||||
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
|
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
|
||||||
{
|
{
|
||||||
sender.ItemsSource = AvailableTokens.Values.Where(q => q.Value.Contains(Text, StringComparison.OrdinalIgnoreCase));
|
sender.ItemsSource = AvailableTokens.Values.Where(q => q.Value.Contains(Text, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
// TODO: CornerRadius
|
|
||||||
// Popup? popup = this.FindDescendant("SuggestionsPopup") as Popup;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +81,7 @@ internal sealed partial class AutoSuggestTokenBox : TokenizingTextBox
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandExtension.TryExecute(FilterCommand, FilterCommandParameter);
|
CommandInvocation.TryExecute(FilterCommand, FilterCommandParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
|
private void OnTokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
|
||||||
@@ -60,6 +96,6 @@ internal sealed partial class AutoSuggestTokenBox : TokenizingTextBox
|
|||||||
|
|
||||||
private void OnTokenItemModified(TokenizingTextBox sender, object args)
|
private void OnTokenItemModified(TokenizingTextBox sender, object args)
|
||||||
{
|
{
|
||||||
CommandExtension.TryExecute(FilterCommand, FilterCommandParameter);
|
CommandInvocation.TryExecute(FilterCommand, FilterCommandParameter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
namespace Snap.Hutao.Control.Extension;
|
namespace Snap.Hutao.Control.Extension;
|
||||||
|
|
||||||
internal static class CommandExtension
|
internal static class CommandInvocation
|
||||||
{
|
{
|
||||||
public static bool TryExecute(this ICommand? command, object? parameter = null)
|
public static bool TryExecute(this ICommand? command, object? parameter = null)
|
||||||
{
|
{
|
||||||
@@ -2,11 +2,13 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Snap.Hutao.Control.Extension;
|
namespace Snap.Hutao.Control.Extension;
|
||||||
|
|
||||||
internal static class DependencyObjectExtension
|
internal static class DependencyObjectExtension
|
||||||
{
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static IServiceProvider ServiceProvider(this DependencyObject obj)
|
public static IServiceProvider ServiceProvider(this DependencyObject obj)
|
||||||
{
|
{
|
||||||
return Ioc.Default;
|
return Ioc.Default;
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
|
|
||||||
using Microsoft.UI.Xaml.Media;
|
using Microsoft.UI.Xaml.Media;
|
||||||
using Microsoft.UI.Xaml.Media.Imaging;
|
using Microsoft.UI.Xaml.Media.Imaging;
|
||||||
|
using Snap.Hutao.Control.Extension;
|
||||||
using Snap.Hutao.Core.Caching;
|
using Snap.Hutao.Core.Caching;
|
||||||
|
using Snap.Hutao.Core.ExceptionService;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Snap.Hutao.Control.Image;
|
namespace Snap.Hutao.Control.Image;
|
||||||
@@ -26,12 +28,11 @@ internal sealed class CachedImage : Implementation.ImageEx
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override async Task<ImageSource?> ProvideCachedResourceAsync(Uri imageUri, CancellationToken token)
|
protected override async Task<ImageSource?> ProvideCachedResourceAsync(Uri imageUri, CancellationToken token)
|
||||||
{
|
{
|
||||||
// We can only use Ioc to retrieve IImageCache, no IServiceProvider is available.
|
IImageCache imageCache = this.ServiceProvider().GetRequiredService<IImageCache>();
|
||||||
IImageCache imageCache = Ioc.Default.GetRequiredService<IImageCache>();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Verify.Operation(!string.IsNullOrEmpty(imageUri.Host), SH.ControlImageCachedImageInvalidResourceUri);
|
HutaoException.ThrowIf(string.IsNullOrEmpty(imageUri.Host), HutaoExceptionKind.ImageCacheInvalidUri, SH.ControlImageCachedImageInvalidResourceUri);
|
||||||
string file = await imageCache.GetFileFromCacheAsync(imageUri).ConfigureAwait(true); // BitmapImage need to be created by main thread.
|
string file = await imageCache.GetFileFromCacheAsync(imageUri).ConfigureAwait(true); // BitmapImage need to be created by main thread.
|
||||||
token.ThrowIfCancellationRequested(); // check token state to determine whether the operation should be canceled.
|
token.ThrowIfCancellationRequested(); // check token state to determine whether the operation should be canceled.
|
||||||
return new BitmapImage(file.ToUri()); // BitmapImage initialize with a uri will increase image quality and loading speed.
|
return new BitmapImage(file.ToUri()); // BitmapImage initialize with a uri will increase image quality and loading speed.
|
||||||
|
|||||||
@@ -7,21 +7,14 @@ using Windows.Media.Casting;
|
|||||||
|
|
||||||
namespace Snap.Hutao.Control.Image.Implementation;
|
namespace Snap.Hutao.Control.Image.Implementation;
|
||||||
|
|
||||||
internal class ImageEx : ImageExBase
|
[DependencyProperty("NineGrid", typeof(Thickness))]
|
||||||
|
internal partial class ImageEx : ImageExBase
|
||||||
{
|
{
|
||||||
private static readonly DependencyProperty NineGridProperty = DependencyProperty.Register(nameof(NineGrid), typeof(Thickness), typeof(ImageEx), new PropertyMetadata(default(Thickness)));
|
|
||||||
|
|
||||||
public ImageEx()
|
public ImageEx()
|
||||||
: base()
|
: base()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public Thickness NineGrid
|
|
||||||
{
|
|
||||||
get => (Thickness)GetValue(NineGridProperty);
|
|
||||||
set => SetValue(NineGridProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override CompositionBrush GetAlphaMask()
|
public override CompositionBrush GetAlphaMask()
|
||||||
{
|
{
|
||||||
if (IsInitialized && Image is Microsoft.UI.Xaml.Controls.Image image)
|
if (IsInitialized && Image is Microsoft.UI.Xaml.Controls.Image image)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Microsoft.UI.Composition;
|
|||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Media;
|
using Microsoft.UI.Xaml.Media;
|
||||||
using Microsoft.UI.Xaml.Media.Imaging;
|
using Microsoft.UI.Xaml.Media.Imaging;
|
||||||
|
using Snap.Hutao.Win32;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
|
|
||||||
@@ -158,7 +159,6 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
|
|||||||
if (value)
|
if (value)
|
||||||
{
|
{
|
||||||
control.LayoutUpdated += control.OnImageExBaseLayoutUpdated;
|
control.LayoutUpdated += control.OnImageExBaseLayoutUpdated;
|
||||||
|
|
||||||
control.InvalidateLazyLoading();
|
control.InvalidateLazyLoading();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -169,7 +169,7 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
|
|||||||
|
|
||||||
private static void LazyLoadingThresholdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
private static void LazyLoadingThresholdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (d is ImageExBase control && control.EnableLazyLoading)
|
if (d is ImageExBase { EnableLazyLoading: true } control)
|
||||||
{
|
{
|
||||||
control.InvalidateLazyLoading();
|
control.InvalidateLazyLoading();
|
||||||
}
|
}
|
||||||
@@ -229,9 +229,6 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
|
|||||||
|
|
||||||
private void AttachPlaceholderSource(ImageSource? source)
|
private void AttachPlaceholderSource(ImageSource? source)
|
||||||
{
|
{
|
||||||
// Setting the source at this point should call ImageExOpened/VisualStateManager.GoToState
|
|
||||||
// as we register to both the ImageOpened/ImageFailed events of the underlying control.
|
|
||||||
// We only need to call those methods if we fail in other cases before we get here.
|
|
||||||
if (PlaceholderImage is Microsoft.UI.Xaml.Controls.Image image)
|
if (PlaceholderImage is Microsoft.UI.Xaml.Controls.Image image)
|
||||||
{
|
{
|
||||||
image.Source = source;
|
image.Source = source;
|
||||||
@@ -240,6 +237,15 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
|
|||||||
{
|
{
|
||||||
brush.ImageSource = source;
|
brush.ImageSource = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (source is null)
|
||||||
|
{
|
||||||
|
VisualStateManager.GoToState(this, UnloadedState, true);
|
||||||
|
}
|
||||||
|
else if (source is BitmapSource { PixelHeight: > 0, PixelWidth: > 0 })
|
||||||
|
{
|
||||||
|
VisualStateManager.GoToState(this, LoadedState, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void SetSource(object? source)
|
private async void SetSource(object? source)
|
||||||
@@ -311,8 +317,7 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
|
|||||||
}
|
}
|
||||||
|
|
||||||
tokenSource?.Cancel();
|
tokenSource?.Cancel();
|
||||||
|
tokenSource = new();
|
||||||
tokenSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
AttachPlaceholderSource(null);
|
AttachPlaceholderSource(null);
|
||||||
|
|
||||||
@@ -443,9 +448,10 @@ internal abstract partial class ImageExBase : Microsoft.UI.Xaml.Controls.Control
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect controlRect = TransformToVisual(hostElement)
|
Rect controlRect = TransformToVisual(hostElement).TransformBounds(StructMarshal.Rect(ActualSize));
|
||||||
.TransformBounds(new Rect(0, 0, ActualWidth, ActualHeight));
|
|
||||||
double lazyLoadingThreshold = LazyLoadingThreshold;
|
double lazyLoadingThreshold = LazyLoadingThreshold;
|
||||||
|
|
||||||
|
// Left/Top 1 Threshold, Right/Bottom 2 Threshold
|
||||||
Rect hostRect = new(
|
Rect hostRect = new(
|
||||||
0 - lazyLoadingThreshold,
|
0 - lazyLoadingThreshold,
|
||||||
0 - lazyLoadingThreshold,
|
0 - lazyLoadingThreshold,
|
||||||
|
|||||||
@@ -1,151 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using Microsoft.UI.Composition;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
using Microsoft.UI.Xaml.Hosting;
|
|
||||||
using System.Numerics;
|
|
||||||
using Windows.Foundation;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Control.Layout;
|
|
||||||
|
|
||||||
internal sealed class DefaultItemCollectionTransitionProvider : ItemCollectionTransitionProvider
|
|
||||||
{
|
|
||||||
private const double DefaultAnimationDurationInMs = 300.0;
|
|
||||||
|
|
||||||
static DefaultItemCollectionTransitionProvider()
|
|
||||||
{
|
|
||||||
AnimationSlowdownFactor = 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double AnimationSlowdownFactor { get; set; }
|
|
||||||
|
|
||||||
protected override bool ShouldAnimateCore(ItemCollectionTransition transition)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void StartTransitions(IList<ItemCollectionTransition> transitions)
|
|
||||||
{
|
|
||||||
List<ItemCollectionTransition> addTransitions = [];
|
|
||||||
List<ItemCollectionTransition> removeTransitions = [];
|
|
||||||
List<ItemCollectionTransition> moveTransitions = [];
|
|
||||||
|
|
||||||
foreach (ItemCollectionTransition transition in addTransitions)
|
|
||||||
{
|
|
||||||
switch (transition.Operation)
|
|
||||||
{
|
|
||||||
case ItemCollectionTransitionOperation.Add:
|
|
||||||
addTransitions.Add(transition);
|
|
||||||
break;
|
|
||||||
case ItemCollectionTransitionOperation.Remove:
|
|
||||||
removeTransitions.Add(transition);
|
|
||||||
break;
|
|
||||||
case ItemCollectionTransitionOperation.Move:
|
|
||||||
moveTransitions.Add(transition);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StartAddTransitions(addTransitions, removeTransitions.Count > 0, moveTransitions.Count > 0);
|
|
||||||
StartRemoveTransitions(removeTransitions);
|
|
||||||
StartMoveTransitions(moveTransitions, removeTransitions.Count > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void StartAddTransitions(IList<ItemCollectionTransition> transitions, bool hasRemoveTransitions, bool hasMoveTransitions)
|
|
||||||
{
|
|
||||||
foreach (ItemCollectionTransition transition in transitions)
|
|
||||||
{
|
|
||||||
ItemCollectionTransitionProgress progress = transition.Start();
|
|
||||||
Visual visual = ElementCompositionPreview.GetElementVisual(progress.Element);
|
|
||||||
Compositor compositor = visual.Compositor;
|
|
||||||
|
|
||||||
ScalarKeyFrameAnimation fadeInAnimation = compositor.CreateScalarKeyFrameAnimation();
|
|
||||||
fadeInAnimation.InsertKeyFrame(0.0f, 0.0f);
|
|
||||||
|
|
||||||
if (hasMoveTransitions && hasRemoveTransitions)
|
|
||||||
{
|
|
||||||
fadeInAnimation.InsertKeyFrame(0.66f, 0.0f);
|
|
||||||
}
|
|
||||||
else if (hasMoveTransitions || hasRemoveTransitions)
|
|
||||||
{
|
|
||||||
fadeInAnimation.InsertKeyFrame(0.5f, 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
fadeInAnimation.InsertKeyFrame(1.0f, 1.0f);
|
|
||||||
fadeInAnimation.Duration = TimeSpan.FromMilliseconds(
|
|
||||||
DefaultAnimationDurationInMs * ((hasRemoveTransitions ? 1 : 0) + (hasMoveTransitions ? 1 : 0) + 1) * AnimationSlowdownFactor);
|
|
||||||
|
|
||||||
CompositionScopedBatch batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
|
|
||||||
visual.StartAnimation("Opacity", fadeInAnimation);
|
|
||||||
batch.End();
|
|
||||||
batch.Completed += (_, _) => progress.Complete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void StartRemoveTransitions(IList<ItemCollectionTransition> transitions)
|
|
||||||
{
|
|
||||||
foreach (ItemCollectionTransition transition in transitions)
|
|
||||||
{
|
|
||||||
ItemCollectionTransitionProgress progress = transition.Start();
|
|
||||||
Visual visual = ElementCompositionPreview.GetElementVisual(progress.Element);
|
|
||||||
Compositor compositor = visual.Compositor;
|
|
||||||
|
|
||||||
ScalarKeyFrameAnimation fadeOutAnimation = compositor.CreateScalarKeyFrameAnimation();
|
|
||||||
fadeOutAnimation.InsertExpressionKeyFrame(0.0f, "this.CurrentValue");
|
|
||||||
fadeOutAnimation.InsertKeyFrame(1.0f, 0.0f);
|
|
||||||
fadeOutAnimation.Duration = TimeSpan.FromMilliseconds(DefaultAnimationDurationInMs * AnimationSlowdownFactor);
|
|
||||||
|
|
||||||
CompositionScopedBatch batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
|
|
||||||
visual.StartAnimation(nameof(Visual.Opacity), fadeOutAnimation);
|
|
||||||
batch.End();
|
|
||||||
batch.Completed += (_, _) =>
|
|
||||||
{
|
|
||||||
visual.Opacity = 1.0f;
|
|
||||||
progress.Complete();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void StartMoveTransitions(IList<ItemCollectionTransition> transitions, bool hasRemoveAnimations)
|
|
||||||
{
|
|
||||||
foreach (ItemCollectionTransition transition in transitions)
|
|
||||||
{
|
|
||||||
ItemCollectionTransitionProgress progress = transition.Start();
|
|
||||||
Visual visual = ElementCompositionPreview.GetElementVisual(progress.Element);
|
|
||||||
Compositor compositor = visual.Compositor;
|
|
||||||
CompositionScopedBatch batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
|
|
||||||
|
|
||||||
// Animate offset.
|
|
||||||
if (transition.OldBounds.X != transition.NewBounds.X ||
|
|
||||||
transition.OldBounds.Y != transition.NewBounds.Y)
|
|
||||||
{
|
|
||||||
AnimateOffset(visual, compositor, transition.OldBounds, transition.NewBounds, hasRemoveAnimations);
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.End();
|
|
||||||
batch.Completed += (_, _) => progress.Complete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AnimateOffset(Visual visual, Compositor compositor, Rect oldBounds, Rect newBounds, bool hasRemoveAnimations)
|
|
||||||
{
|
|
||||||
Vector2KeyFrameAnimation offsetAnimation = compositor.CreateVector2KeyFrameAnimation();
|
|
||||||
|
|
||||||
offsetAnimation.SetVector2Parameter("delta", new Vector2(
|
|
||||||
(float)(oldBounds.X - newBounds.X),
|
|
||||||
(float)(oldBounds.Y - newBounds.Y)));
|
|
||||||
offsetAnimation.SetVector2Parameter("final", default);
|
|
||||||
offsetAnimation.InsertExpressionKeyFrame(0.0f, "this.CurrentValue + delta");
|
|
||||||
if (hasRemoveAnimations)
|
|
||||||
{
|
|
||||||
offsetAnimation.InsertExpressionKeyFrame(0.5f, "delta");
|
|
||||||
}
|
|
||||||
|
|
||||||
offsetAnimation.InsertExpressionKeyFrame(1.0f, "final");
|
|
||||||
offsetAnimation.Duration = TimeSpan.FromMilliseconds(
|
|
||||||
DefaultAnimationDurationInMs * ((hasRemoveAnimations ? 1 : 0) + 1) * AnimationSlowdownFactor);
|
|
||||||
|
|
||||||
visual.StartAnimation("TransformMatrix._41_42", offsetAnimation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
|
|
||||||
@@ -18,14 +19,12 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
|
|||||||
protected override void InitializeForContextCore(VirtualizingLayoutContext context)
|
protected override void InitializeForContextCore(VirtualizingLayoutContext context)
|
||||||
{
|
{
|
||||||
context.LayoutState = new UniformStaggeredLayoutState(context);
|
context.LayoutState = new UniformStaggeredLayoutState(context);
|
||||||
base.InitializeForContextCore(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override void UninitializeForContextCore(VirtualizingLayoutContext context)
|
protected override void UninitializeForContextCore(VirtualizingLayoutContext context)
|
||||||
{
|
{
|
||||||
context.LayoutState = null;
|
context.LayoutState = null;
|
||||||
base.UninitializeForContextCore(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -82,16 +81,10 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
|
|||||||
|
|
||||||
(int numberOfColumns, double columnWidth) = GetNumberOfColumnsAndWidth(availableWidth, MinItemWidth, MinColumnSpacing);
|
(int numberOfColumns, double columnWidth) = GetNumberOfColumnsAndWidth(availableWidth, MinItemWidth, MinColumnSpacing);
|
||||||
|
|
||||||
if (columnWidth != state.ColumnWidth)
|
|
||||||
{
|
|
||||||
// The items will need to be remeasured
|
|
||||||
state.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
state.ColumnWidth = columnWidth;
|
state.ColumnWidth = columnWidth;
|
||||||
|
|
||||||
// adjust for column spacing on all columns expect the first
|
double totalWidth = ((state.ColumnWidth + MinColumnSpacing) * numberOfColumns) - MinColumnSpacing;
|
||||||
double totalWidth = state.ColumnWidth + ((numberOfColumns - 1) * (state.ColumnWidth + MinColumnSpacing));
|
|
||||||
if (totalWidth > availableWidth)
|
if (totalWidth > availableWidth)
|
||||||
{
|
{
|
||||||
numberOfColumns--;
|
numberOfColumns--;
|
||||||
@@ -103,7 +96,6 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
|
|||||||
|
|
||||||
if (numberOfColumns != state.NumberOfColumns)
|
if (numberOfColumns != state.NumberOfColumns)
|
||||||
{
|
{
|
||||||
// The items will not need to be remeasured, but they will need to go into new columns
|
|
||||||
state.ClearColumns();
|
state.ClearColumns();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +162,7 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
|
|||||||
item.Element.Measure(new Size(state.ColumnWidth, availableHeight));
|
item.Element.Measure(new Size(state.ColumnWidth, availableHeight));
|
||||||
if (item.Height != item.Element.DesiredSize.Height)
|
if (item.Height != item.Element.DesiredSize.Height)
|
||||||
{
|
{
|
||||||
// this item changed size; we need to recalculate layout for everything after this
|
// this item changed size; we need to recalculate layout for everything after this item
|
||||||
state.RemoveFromIndex(i + 1);
|
state.RemoveFromIndex(i + 1);
|
||||||
item.Height = item.Element.DesiredSize.Height;
|
item.Height = item.Element.DesiredSize.Height;
|
||||||
columnHeights[columnIndex] = item.Top + item.Height;
|
columnHeights[columnIndex] = item.Top + item.Height;
|
||||||
@@ -201,16 +193,16 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
|
|||||||
// Cycle through each column and arrange the items that are within the realization bounds
|
// Cycle through each column and arrange the items that are within the realization bounds
|
||||||
for (int columnIndex = 0; columnIndex < state.NumberOfColumns; columnIndex++)
|
for (int columnIndex = 0; columnIndex < state.NumberOfColumns; columnIndex++)
|
||||||
{
|
{
|
||||||
UniformStaggeredColumnLayout layout = state.GetColumnLayout(columnIndex);
|
foreach (ref readonly UniformStaggeredItem item in CollectionsMarshal.AsSpan(state.GetColumnLayout(columnIndex)))
|
||||||
foreach (ref readonly UniformStaggeredItem item in CollectionsMarshal.AsSpan(layout))
|
|
||||||
{
|
{
|
||||||
double bottom = item.Top + item.Height;
|
double bottom = item.Top + item.Height;
|
||||||
if (bottom < context.RealizationRect.Top)
|
if (bottom < context.RealizationRect.Top)
|
||||||
{
|
{
|
||||||
// element is above the realization bounds
|
// Element is above the realization bounds
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Partial or fully in the view
|
||||||
if (item.Top <= context.RealizationRect.Bottom)
|
if (item.Top <= context.RealizationRect.Bottom)
|
||||||
{
|
{
|
||||||
double itemHorizontalOffset = (state.ColumnWidth * columnIndex) + (MinColumnSpacing * columnIndex);
|
double itemHorizontalOffset = (state.ColumnWidth * columnIndex) + (MinColumnSpacing * columnIndex);
|
||||||
@@ -229,21 +221,22 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
|
|||||||
return finalSize;
|
return finalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (int NumberOfColumns, double ColumnWidth) GetNumberOfColumnsAndWidth(double availableWidth, double minItemWidth, double minColumnSpacing)
|
private static (int NumberOfColumns, double ColumnWidth) GetNumberOfColumnsAndWidth(double availableWidth, double minItemWidth, double columnSpacing)
|
||||||
{
|
{
|
||||||
// test if the width can fit in 2 items
|
// test if the width can fit in 2 items
|
||||||
if ((2 * minItemWidth) + minColumnSpacing > availableWidth)
|
if ((2 * minItemWidth) + columnSpacing > availableWidth)
|
||||||
{
|
{
|
||||||
return (1, availableWidth);
|
return (1, availableWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
int columnCount = Math.Max(1, (int)((availableWidth + minColumnSpacing) / (minItemWidth + minColumnSpacing)));
|
int columnCount = Math.Max(1, (int)((availableWidth + columnSpacing) / (minItemWidth + columnSpacing)));
|
||||||
double columnWidthAddSpacing = (availableWidth + minColumnSpacing) / columnCount;
|
double columnWidthWithSpacing = (availableWidth + columnSpacing) / columnCount;
|
||||||
return (columnCount, columnWidthAddSpacing - minColumnSpacing);
|
return (columnCount, columnWidthWithSpacing - columnSpacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetLowestColumnIndex(in ReadOnlySpan<double> columnHeights)
|
private static int GetLowestColumnIndex(in ReadOnlySpan<double> columnHeights)
|
||||||
{
|
{
|
||||||
|
// We want to find the leftest column with the lowest height
|
||||||
int columnIndex = 0;
|
int columnIndex = 0;
|
||||||
double height = columnHeights[0];
|
double height = columnHeights[0];
|
||||||
for (int j = 1; j < columnHeights.Length; j++)
|
for (int j = 1; j < columnHeights.Length; j++)
|
||||||
@@ -260,13 +253,11 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout
|
|||||||
|
|
||||||
private static void OnMinItemWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
private static void OnMinItemWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
UniformStaggeredLayout panel = (UniformStaggeredLayout)d;
|
((UniformStaggeredLayout)d).InvalidateMeasure();
|
||||||
panel.InvalidateMeasure();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnSpacingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
private static void OnSpacingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
UniformStaggeredLayout panel = (UniformStaggeredLayout)d;
|
((UniformStaggeredLayout)d).InvalidateMeasure();
|
||||||
panel.InvalidateMeasure();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
@@ -67,46 +66,6 @@ internal sealed class UniformStaggeredLayoutState
|
|||||||
return columnLayout[columnIndex];
|
return columnLayout[columnIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clear everything that has been calculated.
|
|
||||||
/// </summary>
|
|
||||||
internal void Clear()
|
|
||||||
{
|
|
||||||
// https://github.com/DGP-Studio/Snap.Hutao/issues/1079
|
|
||||||
// The first element must be force refreshed otherwise
|
|
||||||
// it will use the old one realized
|
|
||||||
// https://github.com/DGP-Studio/Snap.Hutao/issues/1099
|
|
||||||
// Now we need to refresh the first element of each column
|
|
||||||
// https://github.com/DGP-Studio/Snap.Hutao/issues/1099
|
|
||||||
// Finally we need to refresh the whole layout when we reset
|
|
||||||
if (context.ItemCount > 0)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < context.ItemCount; i++)
|
|
||||||
{
|
|
||||||
RecycleElementAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
columnLayout.Clear();
|
|
||||||
items.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clear the layout columns so they will be recalculated.
|
|
||||||
/// </summary>
|
|
||||||
internal void ClearColumns()
|
|
||||||
{
|
|
||||||
columnLayout.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the estimated height of the layout.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The estimated height of the layout.</returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// If all of the items have been calculated then the actual height will be returned.
|
|
||||||
/// If all of the items have not been calculated then an estimated height will be calculated based on the average height of the items.
|
|
||||||
/// </remarks>
|
|
||||||
internal double GetHeight()
|
internal double GetHeight()
|
||||||
{
|
{
|
||||||
double desiredHeight = columnLayout.Values.Max(c => c.Height);
|
double desiredHeight = columnLayout.Values.Max(c => c.Height);
|
||||||
@@ -139,10 +98,37 @@ internal sealed class UniformStaggeredLayoutState
|
|||||||
return desiredHeight;
|
return desiredHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void Clear()
|
||||||
|
{
|
||||||
|
RecycleElements();
|
||||||
|
ClearColumns();
|
||||||
|
ClearItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ClearColumns()
|
||||||
|
{
|
||||||
|
columnLayout.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ClearItems()
|
||||||
|
{
|
||||||
|
items.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void RecycleElements()
|
||||||
|
{
|
||||||
|
if (context.ItemCount > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
RecycleElementAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal void RecycleElementAt(int index)
|
internal void RecycleElementAt(int index)
|
||||||
{
|
{
|
||||||
UIElement element = context.GetOrCreateElementAt(index);
|
context.RecycleElement(context.GetOrCreateElementAt(index));
|
||||||
context.RecycleElement(element);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RemoveFromIndex(int index)
|
internal void RemoveFromIndex(int index)
|
||||||
@@ -175,7 +161,7 @@ internal sealed class UniformStaggeredLayoutState
|
|||||||
{
|
{
|
||||||
for (int i = startIndex; i <= endIndex; i++)
|
for (int i = startIndex; i <= endIndex; i++)
|
||||||
{
|
{
|
||||||
if (i > items.Count)
|
if (i >= items.Count)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -184,7 +170,7 @@ internal sealed class UniformStaggeredLayoutState
|
|||||||
item.Height = 0;
|
item.Height = 0;
|
||||||
item.Top = 0;
|
item.Top = 0;
|
||||||
|
|
||||||
// We must recycle all elements to ensure that it gets the correct context
|
// We must recycle all removed elements to ensure that it gets the correct context
|
||||||
RecycleElementAt(i);
|
RecycleElementAt(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ internal sealed class UInt32Extension : MarkupExtension
|
|||||||
|
|
||||||
protected override object ProvideValue()
|
protected override object ProvideValue()
|
||||||
{
|
{
|
||||||
_ = uint.TryParse(Value, out uint result);
|
return XamlBindingHelper.ConvertValue(typeof(uint), Value);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,24 +39,6 @@ internal static class SoftwareBitmapExtension
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe double Luminance(this SoftwareBitmap softwareBitmap)
|
|
||||||
{
|
|
||||||
using (BitmapBuffer buffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.Read))
|
|
||||||
{
|
|
||||||
using (IMemoryBufferReference reference = buffer.CreateReference())
|
|
||||||
{
|
|
||||||
reference.As<IMemoryBufferByteAccess>().GetBuffer(out Span<Bgra32> bytes);
|
|
||||||
double sum = 0;
|
|
||||||
foreach (ref readonly Bgra32 pixel in bytes)
|
|
||||||
{
|
|
||||||
sum += pixel.Luminance;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sum / bytes.Length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unsafe Bgra32 GetAccentColor(this SoftwareBitmap softwareBitmap)
|
public static unsafe Bgra32 GetAccentColor(this SoftwareBitmap softwareBitmap)
|
||||||
{
|
{
|
||||||
using (BitmapBuffer buffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.Read))
|
using (BitmapBuffer buffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.Read))
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ internal partial class EqualPanel : Microsoft.UI.Xaml.Controls.Panel
|
|||||||
(d as EqualPanel)?.InvalidateMeasure();
|
(d as EqualPanel)?.InvalidateMeasure();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnHorizontalAlignmentChanged(DependencyObject sender, DependencyProperty dp)
|
private static void OnHorizontalAlignmentChanged(DependencyObject d, DependencyProperty dp)
|
||||||
{
|
{
|
||||||
InvalidateMeasure();
|
(d as EqualPanel)?.InvalidateMeasure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,8 +33,8 @@ internal partial class HorizontalEqualPanel : Microsoft.UI.Xaml.Controls.Panel
|
|||||||
protected override Size ArrangeOverride(Size finalSize)
|
protected override Size ArrangeOverride(Size finalSize)
|
||||||
{
|
{
|
||||||
int itemCount = Children.Count;
|
int itemCount = Children.Count;
|
||||||
double availableWidthPerItem = (finalSize.Width - (Spacing * (itemCount - 1))) / itemCount;
|
double availableItemWidth = (finalSize.Width - (Spacing * (itemCount - 1))) / itemCount;
|
||||||
double actualItemWidth = Math.Max(MinItemWidth, availableWidthPerItem);
|
double actualItemWidth = Math.Max(MinItemWidth, availableItemWidth);
|
||||||
|
|
||||||
double offset = 0;
|
double offset = 0;
|
||||||
foreach (UIElement child in Children)
|
foreach (UIElement child in Children)
|
||||||
@@ -46,13 +46,14 @@ internal partial class HorizontalEqualPanel : Microsoft.UI.Xaml.Controls.Panel
|
|||||||
return finalSize;
|
return finalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
private static void OnLoaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
MinWidth = (MinItemWidth * Children.Count) + (Spacing * (Children.Count - 1));
|
HorizontalEqualPanel panel = (HorizontalEqualPanel)sender;
|
||||||
|
panel.MinWidth = (panel.MinItemWidth * panel.Children.Count) + (panel.Spacing * (panel.Children.Count - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
|
private static void OnSizeChanged(object sender, SizeChangedEventArgs e)
|
||||||
{
|
{
|
||||||
InvalidateMeasure();
|
((HorizontalEqualPanel)sender).InvalidateMeasure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,11 @@ internal class ScopedPage : Page
|
|||||||
DisposeViewModel();
|
DisposeViewModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
DataContext = null;
|
if (this.IsDisposed())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Unloaded -= unloadEventHandler;
|
Unloaded -= unloadEventHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ internal enum HutaoExceptionKind
|
|||||||
|
|
||||||
// Foundation
|
// Foundation
|
||||||
ServiceTypeCastFailed,
|
ServiceTypeCastFailed,
|
||||||
|
ImageCacheInvalidUri,
|
||||||
|
|
||||||
// IO
|
// IO
|
||||||
FileSystemCreateFileInsufficientPermissions,
|
FileSystemCreateFileInsufficientPermissions,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Snap.Hutao.Core.LifeCycle;
|
using Snap.Hutao.Core.LifeCycle;
|
||||||
|
using Snap.Hutao.Service;
|
||||||
|
|
||||||
namespace Snap.Hutao.Factory.ContentDialog;
|
namespace Snap.Hutao.Factory.ContentDialog;
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
|||||||
private readonly ICurrentWindowReference currentWindowReference;
|
private readonly ICurrentWindowReference currentWindowReference;
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
private readonly ITaskContext taskContext;
|
private readonly ITaskContext taskContext;
|
||||||
|
private readonly AppOptions appOptions;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async ValueTask<ContentDialogResult> CreateForConfirmAsync(string title, string content)
|
public async ValueTask<ContentDialogResult> CreateForConfirmAsync(string title, string content)
|
||||||
@@ -27,6 +29,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
|||||||
Content = content,
|
Content = content,
|
||||||
DefaultButton = ContentDialogButton.Primary,
|
DefaultButton = ContentDialogButton.Primary,
|
||||||
PrimaryButtonText = SH.ContentDialogConfirmPrimaryButtonText,
|
PrimaryButtonText = SH.ContentDialogConfirmPrimaryButtonText,
|
||||||
|
RequestedTheme = appOptions.ElementTheme,
|
||||||
};
|
};
|
||||||
|
|
||||||
return await dialog.ShowAsync();
|
return await dialog.ShowAsync();
|
||||||
@@ -44,6 +47,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
|||||||
DefaultButton = defaultButton,
|
DefaultButton = defaultButton,
|
||||||
PrimaryButtonText = SH.ContentDialogConfirmPrimaryButtonText,
|
PrimaryButtonText = SH.ContentDialogConfirmPrimaryButtonText,
|
||||||
CloseButtonText = SH.ContentDialogCancelCloseButtonText,
|
CloseButtonText = SH.ContentDialogCancelCloseButtonText,
|
||||||
|
RequestedTheme = appOptions.ElementTheme,
|
||||||
};
|
};
|
||||||
|
|
||||||
return await dialog.ShowAsync();
|
return await dialog.ShowAsync();
|
||||||
@@ -58,6 +62,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
|||||||
XamlRoot = currentWindowReference.GetXamlRoot(),
|
XamlRoot = currentWindowReference.GetXamlRoot(),
|
||||||
Title = title,
|
Title = title,
|
||||||
Content = new ProgressBar() { IsIndeterminate = true },
|
Content = new ProgressBar() { IsIndeterminate = true },
|
||||||
|
RequestedTheme = appOptions.ElementTheme,
|
||||||
};
|
};
|
||||||
|
|
||||||
return dialog;
|
return dialog;
|
||||||
@@ -69,6 +74,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
|||||||
await taskContext.SwitchToMainThreadAsync();
|
await taskContext.SwitchToMainThreadAsync();
|
||||||
TContentDialog contentDialog = serviceProvider.CreateInstance<TContentDialog>(parameters);
|
TContentDialog contentDialog = serviceProvider.CreateInstance<TContentDialog>(parameters);
|
||||||
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
|
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
|
||||||
|
contentDialog.RequestedTheme = appOptions.ElementTheme;
|
||||||
return contentDialog;
|
return contentDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +83,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
|||||||
{
|
{
|
||||||
TContentDialog contentDialog = serviceProvider.CreateInstance<TContentDialog>(parameters);
|
TContentDialog contentDialog = serviceProvider.CreateInstance<TContentDialog>(parameters);
|
||||||
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
|
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
|
||||||
|
contentDialog.RequestedTheme = appOptions.ElementTheme;
|
||||||
return contentDialog;
|
return contentDialog;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,7 @@ internal static class MonsterRelationship
|
|||||||
5112U => 511U, // 历经百战的浊水喷吐幻灵
|
5112U => 511U, // 历经百战的浊水喷吐幻灵
|
||||||
30605U => 30603U, // 历经百战的霜剑律从
|
30605U => 30603U, // 历经百战的霜剑律从
|
||||||
30606U => 30604U, // 历经百战的幽风铃兰
|
30606U => 30604U, // 历经百战的幽风铃兰
|
||||||
|
40632U => 40613U, // 自律超算型场力发生装置
|
||||||
60402U => 60401U, // (火)岩龙蜥
|
60402U => 60401U, // (火)岩龙蜥
|
||||||
60403U => 60401U, // (冰)岩龙蜥
|
60403U => 60401U, // (冰)岩龙蜥
|
||||||
60404U => 60401U, // (雷)岩龙蜥
|
60404U => 60401U, // (雷)岩龙蜥
|
||||||
|
|||||||
@@ -2922,7 +2922,7 @@
|
|||||||
<value>〓Event Duration〓.*?\d\.\d Available throughout the entirety of Version</value>
|
<value>〓Event Duration〓.*?\d\.\d Available throughout the entirety of Version</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
||||||
<value>(?:〓Event Duration〓|Event Wish Duration|【Availability Duration】).*?(\d\.\dAfter the Version update).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
<value>(?:〓Event Duration〓|Event Wish Duration|【Availability Duration】|〓Discount Period〓).*?(\d\.\dAfter the Version update).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
||||||
<value>〓Update Maintenance Duration〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
<value>〓Update Maintenance Duration〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||||
|
|||||||
@@ -2247,7 +2247,7 @@
|
|||||||
<value>Berkas</value>
|
<value>Berkas</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
||||||
<value>进程联动</value>
|
<value>InterProcess</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||||
<value>Menjalankan perangkat lunak pada layar yang dipilih</value>
|
<value>Menjalankan perangkat lunak pada layar yang dipilih</value>
|
||||||
|
|||||||
@@ -1368,7 +1368,7 @@
|
|||||||
<value>クッキーを設定</value>
|
<value>クッキーを設定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewFeedbackHeader" xml:space="preserve">
|
<data name="ViewFeedbackHeader" xml:space="preserve">
|
||||||
<value>フィードバック センター</value>
|
<value>フィードバックセンター</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewGachaLogHeader" xml:space="preserve">
|
<data name="ViewGachaLogHeader" xml:space="preserve">
|
||||||
<value>祈願履歴</value>
|
<value>祈願履歴</value>
|
||||||
@@ -2247,7 +2247,7 @@
|
|||||||
<value>ファイル</value>
|
<value>ファイル</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
||||||
<value>进程联动</value>
|
<value>インタープロセス</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||||
<value>指定したディスプレイで実行</value>
|
<value>指定したディスプレイで実行</value>
|
||||||
@@ -2505,7 +2505,7 @@
|
|||||||
<value>アチーブメント</value>
|
<value>アチーブメント</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewpageSettingHomeCardItemDailyNoteHeader" xml:space="preserve">
|
<data name="ViewpageSettingHomeCardItemDailyNoteHeader" xml:space="preserve">
|
||||||
<value>リアルタイムノート</value>
|
<value>リアルタイムノート</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewpageSettingHomeCardItemgachaStatisticsHeader" xml:space="preserve">
|
<data name="ViewpageSettingHomeCardItemgachaStatisticsHeader" xml:space="preserve">
|
||||||
<value>祈願履歴</value>
|
<value>祈願履歴</value>
|
||||||
@@ -2859,7 +2859,7 @@
|
|||||||
<value>QRコードでログイン</value>
|
<value>QRコードでログイン</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewUserCookieOperationManualInputAction" xml:space="preserve">
|
<data name="ViewUserCookieOperationManualInputAction" xml:space="preserve">
|
||||||
<value>手入力</value>
|
<value>手動入力</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewUserCookieOperationRefreshCookieAction" xml:space="preserve">
|
<data name="ViewUserCookieOperationRefreshCookieAction" xml:space="preserve">
|
||||||
<value>Cookie 再取得</value>
|
<value>Cookie 再取得</value>
|
||||||
@@ -2922,7 +2922,7 @@
|
|||||||
<value>〓イベント期間〓.*?\d\.\d当バージョン期間オープン</value>
|
<value>〓イベント期間〓.*?\d\.\d当バージョン期間オープン</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
||||||
<value>(?:〓活动时间〓|祈愿时间|【上架时间】|〓折扣时间〓).*?(\d\.\d版本更新后).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
<value>(?:〓イベント期間〓|祈願期間|【開始日時】).*?(\d\.\dバージョンアップ完了後).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
||||||
<value>〓メンテナンス時間〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
<value>〓メンテナンス時間〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||||
|
|||||||
@@ -2247,7 +2247,7 @@
|
|||||||
<value>文件</value>
|
<value>文件</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
||||||
<value>进程联动</value>
|
<value>进程间</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||||
<value>지정한 모니터에서 실행</value>
|
<value>지정한 모니터에서 실행</value>
|
||||||
@@ -2922,7 +2922,7 @@
|
|||||||
<value>〓活动时间〓.*?\d\.\d版本期间持续开放</value>
|
<value>〓活动时间〓.*?\d\.\d版本期间持续开放</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
||||||
<value>(?:〓活动时间〓|祈愿时间|【上架时间】|〓折扣时间〓).*?(\d\.\d版本更新后).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
<value>(?:〓活动时间〓|祈愿时间|【上架时间】).*?(\d\.\d版本更新后).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
||||||
<value>〓更新时间〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
<value>〓更新时间〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||||
|
|||||||
@@ -2247,7 +2247,7 @@
|
|||||||
<value>Arquivo</value>
|
<value>Arquivo</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
||||||
<value>进程联动</value>
|
<value>Comunicações entre processos</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||||
<value>Executar o software na tela selecionada</value>
|
<value>Executar o software na tela selecionada</value>
|
||||||
@@ -2922,7 +2922,7 @@
|
|||||||
<value>〓Duração do evento〓.*?\d\.\d Disponível em toda as versões</value>
|
<value>〓Duração do evento〓.*?\d\.\d Disponível em toda as versões</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
||||||
<value>(?:〓活动时间〓|祈愿时间|【上架时间】|〓折扣时间〓).*?(\d\.\d版本更新后).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
<value>(?:〓Duração do evento〓|Duração da oração do evento|【Duração da disponibilidade】).*?(\d\.\dApós a atualização da versão).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
||||||
<value>〓Duração da manutenção da atualização.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
<value>〓Duração da manutenção da atualização.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||||
|
|||||||
@@ -2247,7 +2247,7 @@
|
|||||||
<value>文件</value>
|
<value>文件</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
<data name="ViewPageLaunchGameInterProcessHeader" xml:space="preserve">
|
||||||
<value>进程联动</value>
|
<value>进程间</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||||
<value>在指定的显示器上运行</value>
|
<value>在指定的显示器上运行</value>
|
||||||
@@ -2922,7 +2922,7 @@
|
|||||||
<value>〓活动时间〓.*?\d\.\d版本期间持续开放</value>
|
<value>〓活动时间〓.*?\d\.\d版本期间持续开放</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
|
||||||
<value>(?:〓活动时间〓|祈愿时间|【上架时间】|〓折扣时间〓).*?(\d\.\d版本更新后).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
<value>(?:〓活动时间〓|祈愿时间|【上架时间】).*?(\d\.\d版本更新后).*?~.*?&lt;t class="t_(?:gl|lc)".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
|
||||||
<value>〓更新时间〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
<value>〓更新时间〓.+?&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;</value>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ internal sealed class LaunchStatus
|
|||||||
|
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
|
||||||
public static LaunchStatus FromUnlockState(GameFpsUnlockerState unlockerState)
|
public static LaunchStatus FromUnlockerContext(GameFpsUnlockerContext unlockerState)
|
||||||
{
|
{
|
||||||
if (unlockerState.FindModuleResult == FindModuleResult.Ok)
|
if (unlockerState.FindModuleResult == FindModuleResult.Ok)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ internal sealed class LaunchExecutionBetterGenshinImpactAutomationHandlder : ILa
|
|||||||
{
|
{
|
||||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||||
{
|
{
|
||||||
if (context.Options.UseBetterGenshinImpactAutomation)
|
if (!context.Process.HasExited && context.Options.UseBetterGenshinImpactAutomation)
|
||||||
{
|
{
|
||||||
context.Logger.LogInformation("Using BetterGI to automate gameplay");
|
context.Logger.LogInformation("Using BetterGI to automate gameplay");
|
||||||
await LaunchBetterGenshinImpactAsync(context).ConfigureAwait(false);
|
await LaunchBetterGenshinImpactAsync(context).ConfigureAwait(false);
|
||||||
@@ -26,11 +26,12 @@ internal sealed class LaunchExecutionBetterGenshinImpactAutomationHandlder : ILa
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
context.Logger.LogInformation("Waiting game window to be ready");
|
context.Logger.LogInformation("Waiting game window to be ready");
|
||||||
context.Process.WaitForInputIdle();
|
|
||||||
|
SpinWait.SpinUntil(() => context.Process.MainWindowHandle != IntPtr.Zero);
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException)
|
catch (InvalidOperationException)
|
||||||
{
|
{
|
||||||
context.Logger.LogInformation("Failed to wait Input idle waiting");
|
context.Logger.LogInformation("Failed to get game window handle");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ internal sealed class LaunchExecutionStarwardPlayTimeStatisticsHandler : ILaunch
|
|||||||
{
|
{
|
||||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||||
{
|
{
|
||||||
if (context.Options.UseStarwardPlayTimeStatistics)
|
if (!context.Process.HasExited && context.Options.UseStarwardPlayTimeStatistics)
|
||||||
{
|
{
|
||||||
context.Logger.LogInformation("Using Starward to count game time");
|
context.Logger.LogInformation("Using Starward to count game time");
|
||||||
await LaunchStarwardForPlayTimeStatisticsAsync(context).ConfigureAwait(false);
|
await LaunchStarwardForPlayTimeStatisticsAsync(context).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -18,15 +18,17 @@ internal sealed class LaunchExecutionUnlockFpsHandler : ILaunchExecutionDelegate
|
|||||||
context.Progress.Report(new(LaunchPhase.UnlockingFps, SH.ServiceGameLaunchPhaseUnlockingFps));
|
context.Progress.Report(new(LaunchPhase.UnlockingFps, SH.ServiceGameLaunchPhaseUnlockingFps));
|
||||||
|
|
||||||
IProgressFactory progressFactory = context.ServiceProvider.GetRequiredService<IProgressFactory>();
|
IProgressFactory progressFactory = context.ServiceProvider.GetRequiredService<IProgressFactory>();
|
||||||
IProgress<GameFpsUnlockerState> progress = progressFactory.CreateForMainThread<GameFpsUnlockerState>(status => context.Progress.Report(LaunchStatus.FromUnlockState(status)));
|
IProgress<GameFpsUnlockerContext> progress = progressFactory.CreateForMainThread<GameFpsUnlockerContext>(c => context.Progress.Report(LaunchStatus.FromUnlockerContext(c)));
|
||||||
GameFpsUnlocker unlocker = new(context.ServiceProvider, context.Process, new(100, 20000, 3000), progress);
|
GameFpsUnlocker unlocker = new(context.ServiceProvider, context.Process, new(100, 20000, 3000), progress);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await unlocker.UnlockAsync(context.CancellationToken).ConfigureAwait(false);
|
if (await unlocker.UnlockAsync(context.CancellationToken).ConfigureAwait(false))
|
||||||
unlocker.PostUnlockAsync(context.CancellationToken).SafeForget();
|
{
|
||||||
|
unlocker.PostUnlockAsync(context.CancellationToken).SafeForget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
context.Logger.LogCritical(ex, "Unlocking FPS failed");
|
context.Logger.LogCritical(ex, "Unlocking FPS failed");
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ internal static class GameFpsAddress
|
|||||||
private const byte ASM_JMP = 0xE9;
|
private const byte ASM_JMP = 0xE9;
|
||||||
#pragma warning restore SA1310
|
#pragma warning restore SA1310
|
||||||
|
|
||||||
public static unsafe void UnsafeFindFpsAddress(GameFpsUnlockerState state, in RequiredGameModule requiredGameModule)
|
public static unsafe void UnsafeFindFpsAddress(GameFpsUnlockerContext state, in RequiredGameModule requiredGameModule)
|
||||||
{
|
{
|
||||||
bool readOk = UnsafeReadModulesMemory(state.GameProcess, requiredGameModule, out VirtualMemory localMemory);
|
bool readOk = UnsafeReadModulesMemory(state.GameProcess, requiredGameModule, out VirtualMemory localMemory);
|
||||||
HutaoException.ThrowIfNot(readOk, HutaoExceptionKind.GameFpsUnlockingFailed, SH.ServiceGameUnlockerReadModuleMemoryCopyVirtualMemoryFailed);
|
HutaoException.ThrowIfNot(readOk, HutaoExceptionKind.GameFpsUnlockingFailed, SH.ServiceGameUnlockerReadModuleMemoryCopyVirtualMemoryFailed);
|
||||||
|
|||||||
@@ -16,45 +16,46 @@ namespace Snap.Hutao.Service.Game.Unlocker;
|
|||||||
internal sealed class GameFpsUnlocker : IGameFpsUnlocker
|
internal sealed class GameFpsUnlocker : IGameFpsUnlocker
|
||||||
{
|
{
|
||||||
private readonly LaunchOptions launchOptions;
|
private readonly LaunchOptions launchOptions;
|
||||||
private readonly GameFpsUnlockerState state = new();
|
private readonly GameFpsUnlockerContext context = new();
|
||||||
|
|
||||||
public GameFpsUnlocker(IServiceProvider serviceProvider, Process gameProcess, in UnlockTimingOptions options, IProgress<GameFpsUnlockerState> progress)
|
public GameFpsUnlocker(IServiceProvider serviceProvider, Process gameProcess, in UnlockTimingOptions options, IProgress<GameFpsUnlockerContext> progress)
|
||||||
{
|
{
|
||||||
launchOptions = serviceProvider.GetRequiredService<LaunchOptions>();
|
launchOptions = serviceProvider.GetRequiredService<LaunchOptions>();
|
||||||
|
|
||||||
state.GameProcess = gameProcess;
|
context.GameProcess = gameProcess;
|
||||||
state.TimingOptions = options;
|
context.TimingOptions = options;
|
||||||
state.Progress = progress;
|
context.Progress = progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async ValueTask UnlockAsync(CancellationToken token = default)
|
public async ValueTask<bool> UnlockAsync(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
HutaoException.ThrowIfNot(state.IsUnlockerValid, HutaoExceptionKind.GameFpsUnlockingFailed, "This Unlocker is invalid");
|
HutaoException.ThrowIfNot(context.IsUnlockerValid, HutaoExceptionKind.GameFpsUnlockingFailed, "This Unlocker is invalid");
|
||||||
(FindModuleResult result, RequiredGameModule gameModule) = await GameProcessModule.FindModuleAsync(state).ConfigureAwait(false);
|
(FindModuleResult result, RequiredGameModule gameModule) = await GameProcessModule.FindModuleAsync(context).ConfigureAwait(false);
|
||||||
HutaoException.ThrowIfNot(result != FindModuleResult.TimeLimitExeeded, HutaoExceptionKind.GameFpsUnlockingFailed, SH.ServiceGameUnlockerFindModuleTimeLimitExeeded);
|
HutaoException.ThrowIfNot(result != FindModuleResult.TimeLimitExeeded, HutaoExceptionKind.GameFpsUnlockingFailed, SH.ServiceGameUnlockerFindModuleTimeLimitExeeded);
|
||||||
HutaoException.ThrowIfNot(result != FindModuleResult.NoModuleFound, HutaoExceptionKind.GameFpsUnlockingFailed, SH.ServiceGameUnlockerFindModuleNoModuleFound);
|
HutaoException.ThrowIfNot(result != FindModuleResult.NoModuleFound, HutaoExceptionKind.GameFpsUnlockingFailed, SH.ServiceGameUnlockerFindModuleNoModuleFound);
|
||||||
|
|
||||||
GameFpsAddress.UnsafeFindFpsAddress(state, gameModule);
|
GameFpsAddress.UnsafeFindFpsAddress(context, gameModule);
|
||||||
state.Report();
|
context.Report();
|
||||||
|
return context.FpsAddress != 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask PostUnlockAsync(CancellationToken token = default)
|
public async ValueTask PostUnlockAsync(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
using (PeriodicTimer timer = new(state.TimingOptions.AdjustFpsDelay))
|
using (PeriodicTimer timer = new(context.TimingOptions.AdjustFpsDelay))
|
||||||
{
|
{
|
||||||
while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false))
|
while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
if (!state.GameProcess.HasExited && state.FpsAddress != 0U)
|
if (!context.GameProcess.HasExited && context.FpsAddress != 0U)
|
||||||
{
|
{
|
||||||
UnsafeWriteProcessMemory(state.GameProcess, state.FpsAddress, launchOptions.TargetFps);
|
UnsafeWriteProcessMemory(context.GameProcess, context.FpsAddress, launchOptions.TargetFps);
|
||||||
state.Report();
|
context.Report();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
state.IsUnlockerValid = false;
|
context.IsUnlockerValid = false;
|
||||||
state.FpsAddress = 0;
|
context.FpsAddress = 0;
|
||||||
state.Report();
|
context.Report();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Snap.Hutao.Service.Game.Unlocker;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 解锁状态
|
/// 解锁状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class GameFpsUnlockerState
|
internal sealed class GameFpsUnlockerContext
|
||||||
{
|
{
|
||||||
public string Description { get; set; } = default!;
|
public string Description { get; set; } = default!;
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ internal sealed class GameFpsUnlockerState
|
|||||||
|
|
||||||
public Process GameProcess { get; set; } = default!;
|
public Process GameProcess { get; set; } = default!;
|
||||||
|
|
||||||
public IProgress<GameFpsUnlockerState> Progress { get; set; } = default!;
|
public IProgress<GameFpsUnlockerContext> Progress { get; set; } = default!;
|
||||||
|
|
||||||
public void Report()
|
public void Report()
|
||||||
{
|
{
|
||||||
@@ -13,7 +13,7 @@ namespace Snap.Hutao.Service.Game.Unlocker;
|
|||||||
|
|
||||||
internal static class GameProcessModule
|
internal static class GameProcessModule
|
||||||
{
|
{
|
||||||
public static async ValueTask<ValueResult<FindModuleResult, RequiredGameModule>> FindModuleAsync(GameFpsUnlockerState state)
|
public static async ValueTask<ValueResult<FindModuleResult, RequiredGameModule>> FindModuleAsync(GameFpsUnlockerContext state)
|
||||||
{
|
{
|
||||||
ValueStopwatch watch = ValueStopwatch.StartNew();
|
ValueStopwatch watch = ValueStopwatch.StartNew();
|
||||||
using (PeriodicTimer timer = new(state.TimingOptions.FindModuleDelay))
|
using (PeriodicTimer timer = new(state.TimingOptions.FindModuleDelay))
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ internal interface IGameFpsUnlocker
|
|||||||
{
|
{
|
||||||
ValueTask PostUnlockAsync(CancellationToken token = default);
|
ValueTask PostUnlockAsync(CancellationToken token = default);
|
||||||
|
|
||||||
ValueTask UnlockAsync(CancellationToken token = default);
|
ValueTask<bool> UnlockAsync(CancellationToken token = default);
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ namespace Snap.Hutao.Service.Hutao;
|
|||||||
[Injection(InjectAs.Scoped, typeof(IHutaoSpiralAbyssService))]
|
[Injection(InjectAs.Scoped, typeof(IHutaoSpiralAbyssService))]
|
||||||
internal sealed partial class HutaoSpiralAbyssService : IHutaoSpiralAbyssService
|
internal sealed partial class HutaoSpiralAbyssService : IHutaoSpiralAbyssService
|
||||||
{
|
{
|
||||||
private readonly TimeSpan cacheExpireTime = TimeSpan.FromHours(4);
|
private readonly TimeSpan cacheExpireTime = TimeSpan.FromHours(1);
|
||||||
|
|
||||||
private readonly IObjectCacheDbService objectCacheDbService;
|
private readonly IObjectCacheDbService objectCacheDbService;
|
||||||
private readonly HutaoSpiralAbyssClient homaClient;
|
private readonly HutaoSpiralAbyssClient homaClient;
|
||||||
|
|||||||
@@ -302,8 +302,8 @@
|
|||||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.TokenizingTextBox" Version="8.0.240109" />
|
<PackageReference Include="CommunityToolkit.WinUI.Controls.TokenizingTextBox" Version="8.0.240109" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.0.240109" />
|
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.0.240109" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.3" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.2">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.3">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
@@ -311,14 +311,14 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.1.1" />
|
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.2.0" />
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.8.8" />
|
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.8.8" />
|
||||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" />
|
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.3233" />
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" />
|
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240311000" />
|
||||||
<PackageReference Include="QRCoder" Version="1.4.3" />
|
<PackageReference Include="QRCoder" Version="1.4.3" />
|
||||||
<PackageReference Include="Snap.Discord.GameSDK" Version="1.6.0" />
|
<PackageReference Include="Snap.Discord.GameSDK" Version="1.6.0" />
|
||||||
<PackageReference Include="Snap.Hutao.Deployment.Runtime" Version="1.15.3">
|
<PackageReference Include="Snap.Hutao.Deployment.Runtime" Version="1.15.3">
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
Maximum="{Binding GuaranteeOrangeThreshold}"
|
Maximum="{Binding GuaranteeOrangeThreshold}"
|
||||||
ProgressForeground="{StaticResource OrangeColorBrush}"
|
ProgressForeground="{StaticResource OrangeColorBrush}"
|
||||||
TextForeground="{StaticResource OrangeColorBrush}"
|
TextForeground="{StaticResource OrangeColorBrush}"
|
||||||
Value="{Binding LastOrangePull}"/>
|
Value="{Binding LastOrangePull, Mode=OneWay}"/>
|
||||||
<shvcp:CardProgressBar
|
<shvcp:CardProgressBar
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Description="{Binding LastPurplePull}"
|
Description="{Binding LastPurplePull}"
|
||||||
|
|||||||
@@ -13,12 +13,9 @@ namespace Snap.Hutao.View.Card.Primitive;
|
|||||||
[DependencyProperty("Value", typeof(double))]
|
[DependencyProperty("Value", typeof(double))]
|
||||||
[DependencyProperty("Header", typeof(string))]
|
[DependencyProperty("Header", typeof(string))]
|
||||||
[DependencyProperty("Description", typeof(string))]
|
[DependencyProperty("Description", typeof(string))]
|
||||||
internal sealed partial class CardProgressBar : Grid
|
internal sealed partial class CardProgressBar : ContentControl
|
||||||
{
|
{
|
||||||
public CardProgressBar()
|
public CardProgressBar()
|
||||||
{
|
{
|
||||||
IAppResourceProvider appResourceProvider = Ioc.Default.GetRequiredService<IAppResourceProvider>();
|
|
||||||
TextForeground = appResourceProvider.GetResource<Brush>("TextFillColorPrimaryBrush");
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,39 +1,49 @@
|
|||||||
<Grid
|
<ResourceDictionary
|
||||||
x:Class="Snap.Hutao.View.Card.Primitive.CardProgressBar"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:shvcp="using:Snap.Hutao.View.Card.Primitive">
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
MinHeight="40"
|
|
||||||
Style="{ThemeResource GridCardStyle}"
|
|
||||||
mc:Ignorable="d">
|
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
<Style BasedOn="{StaticResource DefaultCardProgressBarStyle}" TargetType="shvcp:CardProgressBar"/>
|
||||||
<ColumnDefinition Width="auto"/>
|
|
||||||
<ColumnDefinition/>
|
<Style x:Key="DefaultCardProgressBarStyle" TargetType="shvcp:CardProgressBar">
|
||||||
</Grid.ColumnDefinitions>
|
<Setter Property="TextForeground" Value="{ThemeResource TextFillColorPrimaryBrush}"/>
|
||||||
<ProgressBar
|
<Setter Property="MinHeight" Value="40"/>
|
||||||
Grid.ColumnSpan="2"
|
<Setter Property="Template">
|
||||||
MinHeight="{x:Bind MinHeight, Mode=OneWay}"
|
<Setter.Value>
|
||||||
Background="Transparent"
|
<ControlTemplate>
|
||||||
CornerRadius="{StaticResource ControlCornerRadius}"
|
<Grid MinHeight="{TemplateBinding MinHeight}" Style="{ThemeResource GridCardStyle}">
|
||||||
Foreground="{x:Bind ProgressForeground, Mode=OneWay}"
|
<Grid.ColumnDefinitions>
|
||||||
Maximum="{x:Bind Maximum, Mode=OneWay}"
|
<ColumnDefinition Width="auto"/>
|
||||||
Opacity="{StaticResource LargeBackgroundProgressBarOpacity}"
|
<ColumnDefinition/>
|
||||||
Value="{x:Bind Value, Mode=OneWay}"/>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock
|
<ProgressBar
|
||||||
Grid.Column="0"
|
Grid.ColumnSpan="2"
|
||||||
Margin="6,0"
|
MinHeight="{TemplateBinding MinHeight}"
|
||||||
HorizontalAlignment="Center"
|
Background="Transparent"
|
||||||
VerticalAlignment="Center"
|
CornerRadius="{StaticResource ControlCornerRadius}"
|
||||||
Foreground="{x:Bind TextForeground, Mode=OneWay}"
|
Foreground="{Binding ProgressForeground, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
Maximum="{Binding Maximum, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
|
||||||
Text="{x:Bind Header, Mode=OneWay}"/>
|
Opacity="{StaticResource LargeBackgroundProgressBarOpacity}"
|
||||||
<TextBlock
|
Value="{Binding Value, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"/>
|
||||||
Grid.Column="1"
|
<TextBlock
|
||||||
HorizontalAlignment="Center"
|
Grid.Column="0"
|
||||||
VerticalAlignment="Center"
|
Margin="6,0"
|
||||||
Foreground="{x:Bind TextForeground, Mode=OneWay}"
|
HorizontalAlignment="Center"
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Bind Description, Mode=OneWay}"/>
|
Foreground="{Binding TextForeground, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
|
||||||
</Grid>
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
Text="{Binding Header, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"/>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="1"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="{Binding TextForeground, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
Text="{Binding Description, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"/>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -52,41 +52,49 @@
|
|||||||
Margin="0,16,0,8"
|
Margin="0,16,0,8"
|
||||||
Foreground="{ThemeResource UpPullColorBrush}"
|
Foreground="{ThemeResource UpPullColorBrush}"
|
||||||
Style="{StaticResource BaseTextBlockStyle}"
|
Style="{StaticResource BaseTextBlockStyle}"
|
||||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardUpText}"/>
|
Text="{shcm:ResourceString Name=ViewControlStatisticsCardUpText}"
|
||||||
|
Visibility="{Binding UpItems.Count, Converter={StaticResource Int32ToVisibilityConverter}}"/>
|
||||||
<ItemsControl
|
<ItemsControl
|
||||||
ItemTemplate="{StaticResource GridTemplate}"
|
ItemTemplate="{StaticResource GridTemplate}"
|
||||||
ItemsPanel="{StaticResource WrapPanelSpacing4Template}"
|
ItemsPanel="{StaticResource WrapPanelSpacing4Template}"
|
||||||
ItemsSource="{Binding UpItems}"/>
|
ItemsSource="{Binding UpItems}"
|
||||||
|
Visibility="{Binding UpItems.Count, Converter={StaticResource Int32ToVisibilityConverter}}"/>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0,16,0,8"
|
Margin="0,16,0,8"
|
||||||
Foreground="{ThemeResource OrangeColorBrush}"
|
Foreground="{ThemeResource OrangeColorBrush}"
|
||||||
Style="{StaticResource BaseTextBlockStyle}"
|
Style="{StaticResource BaseTextBlockStyle}"
|
||||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardOrangeText}"/>
|
Text="{shcm:ResourceString Name=ViewControlStatisticsCardOrangeText}"
|
||||||
|
Visibility="{Binding OrangeItems.Count, Converter={StaticResource Int32ToVisibilityConverter}}"/>
|
||||||
<ItemsControl
|
<ItemsControl
|
||||||
ItemTemplate="{StaticResource GridTemplate}"
|
ItemTemplate="{StaticResource GridTemplate}"
|
||||||
ItemsPanel="{StaticResource WrapPanelSpacing4Template}"
|
ItemsPanel="{StaticResource WrapPanelSpacing4Template}"
|
||||||
ItemsSource="{Binding OrangeItems}"/>
|
ItemsSource="{Binding OrangeItems}"
|
||||||
|
Visibility="{Binding OrangeItems.Count, Converter={StaticResource Int32ToVisibilityConverter}}"/>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0,16,0,8"
|
Margin="0,16,0,8"
|
||||||
Foreground="{ThemeResource PurpleColorBrush}"
|
Foreground="{ThemeResource PurpleColorBrush}"
|
||||||
Style="{StaticResource BaseTextBlockStyle}"
|
Style="{StaticResource BaseTextBlockStyle}"
|
||||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardPurpleText}"/>
|
Text="{shcm:ResourceString Name=ViewControlStatisticsCardPurpleText}"
|
||||||
|
Visibility="{Binding PurpleItems.Count, Converter={StaticResource Int32ToVisibilityConverter}}"/>
|
||||||
<ItemsControl
|
<ItemsControl
|
||||||
ItemTemplate="{StaticResource GridTemplate}"
|
ItemTemplate="{StaticResource GridTemplate}"
|
||||||
ItemsPanel="{StaticResource WrapPanelSpacing4Template}"
|
ItemsPanel="{StaticResource WrapPanelSpacing4Template}"
|
||||||
ItemsSource="{Binding PurpleItems}"/>
|
ItemsSource="{Binding PurpleItems}"
|
||||||
|
Visibility="{Binding PurpleItems.Count, Converter={StaticResource Int32ToVisibilityConverter}}"/>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0,16,0,8"
|
Margin="0,16,0,8"
|
||||||
Foreground="{ThemeResource BlueColorBrush}"
|
Foreground="{ThemeResource BlueColorBrush}"
|
||||||
Style="{StaticResource BaseTextBlockStyle}"
|
Style="{StaticResource BaseTextBlockStyle}"
|
||||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardBlueText}"/>
|
Text="{shcm:ResourceString Name=ViewControlStatisticsCardBlueText}"
|
||||||
|
Visibility="{Binding BlueItems.Count, Converter={StaticResource Int32ToVisibilityConverter}}"/>
|
||||||
<ItemsControl
|
<ItemsControl
|
||||||
ItemTemplate="{StaticResource GridTemplate}"
|
ItemTemplate="{StaticResource GridTemplate}"
|
||||||
ItemsPanel="{StaticResource WrapPanelSpacing4Template}"
|
ItemsPanel="{StaticResource WrapPanelSpacing4Template}"
|
||||||
ItemsSource="{Binding BlueItems}"/>
|
ItemsSource="{Binding BlueItems}"
|
||||||
|
Visibility="{Binding BlueItems.Count, Converter={StaticResource Int32ToVisibilityConverter}}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -479,10 +479,12 @@
|
|||||||
<ColumnDefinition/>
|
<ColumnDefinition/>
|
||||||
<ColumnDefinition/>
|
<ColumnDefinition/>
|
||||||
<ColumnDefinition/>
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<shvc:HutaoStatisticsCard Grid.Column="0" DataContext="{Binding HutaoCloudStatisticsViewModel.Statistics.AvatarEvent}"/>
|
<shvc:HutaoStatisticsCard Grid.Column="0" DataContext="{Binding HutaoCloudStatisticsViewModel.Statistics.AvatarEvent}"/>
|
||||||
<shvc:HutaoStatisticsCard Grid.Column="1" DataContext="{Binding HutaoCloudStatisticsViewModel.Statistics.AvatarEvent2}"/>
|
<shvc:HutaoStatisticsCard Grid.Column="1" DataContext="{Binding HutaoCloudStatisticsViewModel.Statistics.AvatarEvent2}"/>
|
||||||
<shvc:HutaoStatisticsCard Grid.Column="2" DataContext="{Binding HutaoCloudStatisticsViewModel.Statistics.WeaponEvent}"/>
|
<shvc:HutaoStatisticsCard Grid.Column="2" DataContext="{Binding HutaoCloudStatisticsViewModel.Statistics.WeaponEvent}"/>
|
||||||
|
<shvc:HutaoStatisticsCard Grid.Column="3" DataContext="{Binding HutaoCloudStatisticsViewModel.Statistics.Chronicled}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
|
|||||||
@@ -317,7 +317,6 @@
|
|||||||
QueryIcon="{cw:FontIconSource Glyph=}"
|
QueryIcon="{cw:FontIconSource Glyph=}"
|
||||||
Style="{StaticResource DefaultTokenizingTextBoxStyle}"
|
Style="{StaticResource DefaultTokenizingTextBoxStyle}"
|
||||||
SuggestedItemTemplate="{StaticResource TokenTemplate}"
|
SuggestedItemTemplate="{StaticResource TokenTemplate}"
|
||||||
SuggestedItemsSource="{Binding AvailableTokens.Values}"
|
|
||||||
Text="{Binding FilterToken, Mode=TwoWay}"
|
Text="{Binding FilterToken, Mode=TwoWay}"
|
||||||
TokenItemTemplate="{StaticResource TokenTemplate}">
|
TokenItemTemplate="{StaticResource TokenTemplate}">
|
||||||
<shca:AutoSuggestTokenBox.ItemsPanel>
|
<shca:AutoSuggestTokenBox.ItemsPanel>
|
||||||
|
|||||||
@@ -185,7 +185,6 @@
|
|||||||
PlaceholderText="{shcm:ResourceString Name=ViewPageWiKiWeaponAutoSuggestBoxPlaceHolder}"
|
PlaceholderText="{shcm:ResourceString Name=ViewPageWiKiWeaponAutoSuggestBoxPlaceHolder}"
|
||||||
QueryIcon="{cw:FontIconSource Glyph=}"
|
QueryIcon="{cw:FontIconSource Glyph=}"
|
||||||
SuggestedItemTemplate="{StaticResource TokenTemplate}"
|
SuggestedItemTemplate="{StaticResource TokenTemplate}"
|
||||||
SuggestedItemsSource="{Binding AvailableTokens.Values}"
|
|
||||||
Text="{Binding FilterToken, Mode=TwoWay}"
|
Text="{Binding FilterToken, Mode=TwoWay}"
|
||||||
TokenItemTemplate="{StaticResource TokenTemplate}">
|
TokenItemTemplate="{StaticResource TokenTemplate}">
|
||||||
<shca:AutoSuggestTokenBox.ItemsPanel>
|
<shca:AutoSuggestTokenBox.ItemsPanel>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher.Resource;
|
namespace Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher.Resource;
|
||||||
@@ -16,6 +17,6 @@ internal static class LatestPackageExtension
|
|||||||
}
|
}
|
||||||
|
|
||||||
latest.Path = pathBuilder.ToStringTrimEndReturn();
|
latest.Path = pathBuilder.ToStringTrimEndReturn();
|
||||||
latest.Name = latest.Segments[0].Path[..^4]; // .00X
|
latest.Name = Path.GetFileName(latest.Segments[0].Path[..^4]); // .00X
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using Windows.Foundation;
|
||||||
using Windows.Graphics;
|
using Windows.Graphics;
|
||||||
|
|
||||||
namespace Snap.Hutao.Win32;
|
namespace Snap.Hutao.Win32;
|
||||||
@@ -26,45 +27,26 @@ internal static class StructMarshal
|
|||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public static Rect Rect(Vector2 size)
|
||||||
/// 从 0,0 点构造一个新的<see cref="Windows.Graphics.RectInt32"/>
|
{
|
||||||
/// </summary>
|
return new(0, 0, size.X, size.Y);
|
||||||
/// <param name="size">尺寸</param>
|
}
|
||||||
/// <returns>新的实例</returns>
|
|
||||||
public static RectInt32 RectInt32(SizeInt32 size)
|
public static RectInt32 RectInt32(SizeInt32 size)
|
||||||
{
|
{
|
||||||
return new(0, 0, size.Width, size.Height);
|
return new(0, 0, size.Width, size.Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 构造一个新的<see cref="Windows.Graphics.RectInt32"/>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="point">左上角位置</param>
|
|
||||||
/// <param name="size">尺寸</param>
|
|
||||||
/// <returns>新的实例</returns>
|
|
||||||
public static RectInt32 RectInt32(PointInt32 point, Vector2 size)
|
public static RectInt32 RectInt32(PointInt32 point, Vector2 size)
|
||||||
{
|
{
|
||||||
return RectInt32(point.X, point.Y, size);
|
return RectInt32(point.X, point.Y, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 构造一个新的<see cref="Windows.Graphics.RectInt32"/>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="x">X</param>
|
|
||||||
/// <param name="y">Y</param>
|
|
||||||
/// <param name="size">尺寸</param>
|
|
||||||
/// <returns>新的实例</returns>
|
|
||||||
public static RectInt32 RectInt32(int x, int y, Vector2 size)
|
public static RectInt32 RectInt32(int x, int y, Vector2 size)
|
||||||
{
|
{
|
||||||
return new(x, y, (int)size.X, (int)size.Y);
|
return new(x, y, (int)size.X, (int)size.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 构造一个新的<see cref="Windows.Graphics.RectInt32"/>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="point">左上角位置</param>
|
|
||||||
/// <param name="size">尺寸</param>
|
|
||||||
/// <returns>新的实例</returns>
|
|
||||||
public static RectInt32 RectInt32(PointInt32 point, SizeInt32 size)
|
public static RectInt32 RectInt32(PointInt32 point, SizeInt32 size)
|
||||||
{
|
{
|
||||||
return new(point.X, point.Y, size.Width, size.Height);
|
return new(point.X, point.Y, size.Width, size.Height);
|
||||||
|
|||||||
Reference in New Issue
Block a user