Compare commits

...

6 Commits

Author SHA1 Message Date
qhy040404
3b86783493 migrate to TokenizingTextBox 2024-02-23 18:08:30 +08:00
qhy040404
e3adc2e595 finish suggestion methods 2024-02-23 14:10:09 +08:00
Lightczx
c17798a8c9 code style 2024-02-23 10:22:24 +08:00
DismissedLight
fa9b39d134 Merge pull request #1412 from DGP-Studio/fix/hotkey_notify 2024-02-23 09:42:10 +08:00
qhy040404
8dc85c7a25 prompt if hotkey not registered 2024-02-23 09:39:50 +08:00
DismissedLight
5845c718e1 Merge pull request #1419 from DGP-Studio/feat/real_time_bg 2024-02-23 09:36:25 +08:00
14 changed files with 238 additions and 51 deletions

View File

@@ -41,12 +41,14 @@ internal class ScopedPage : Page
/// 应当在 InitializeComponent() 前调用 /// 应当在 InitializeComponent() 前调用
/// </summary> /// </summary>
/// <typeparam name="TViewModel">视图模型类型</typeparam> /// <typeparam name="TViewModel">视图模型类型</typeparam>
protected void InitializeWith<TViewModel>() protected TViewModel InitializeWith<TViewModel>()
where TViewModel : class, IViewModel where TViewModel : class, IViewModel
{ {
IViewModel viewModel = currentScope.ServiceProvider.GetRequiredService<TViewModel>(); IViewModel viewModel = currentScope.ServiceProvider.GetRequiredService<TViewModel>();
viewModel.CancellationToken = viewCancellationTokenSource.Token; viewModel.CancellationToken = viewCancellationTokenSource.Token;
DataContext = viewModel; DataContext = viewModel;
return (TViewModel)viewModel;
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -5,6 +5,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using Snap.Hutao.Core.LifeCycle; using Snap.Hutao.Core.LifeCycle;
using Snap.Hutao.Core.Setting; using Snap.Hutao.Core.Setting;
using Snap.Hutao.Model; using Snap.Hutao.Model;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Win32.Foundation; using Snap.Hutao.Win32.Foundation;
using Snap.Hutao.Win32.UI.Input.KeyboardAndMouse; using Snap.Hutao.Win32.UI.Input.KeyboardAndMouse;
using System.Text; using System.Text;
@@ -17,6 +18,7 @@ namespace Snap.Hutao.Core.Windowing.HotKey;
internal sealed class HotKeyCombination : ObservableObject internal sealed class HotKeyCombination : ObservableObject
{ {
private readonly ICurrentWindowReference currentWindowReference; private readonly ICurrentWindowReference currentWindowReference;
private readonly IInfoBarService infoBarService;
private readonly RuntimeOptions runtimeOptions; private readonly RuntimeOptions runtimeOptions;
private readonly string settingKey; private readonly string settingKey;
@@ -37,6 +39,7 @@ internal sealed class HotKeyCombination : ObservableObject
public HotKeyCombination(IServiceProvider serviceProvider, string settingKey, int hotKeyId, HOT_KEY_MODIFIERS defaultModifiers, VirtualKey defaultKey) public HotKeyCombination(IServiceProvider serviceProvider, string settingKey, int hotKeyId, HOT_KEY_MODIFIERS defaultModifiers, VirtualKey defaultKey)
{ {
currentWindowReference = serviceProvider.GetRequiredService<ICurrentWindowReference>(); currentWindowReference = serviceProvider.GetRequiredService<ICurrentWindowReference>();
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>(); runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
this.settingKey = settingKey; this.settingKey = settingKey;
@@ -186,6 +189,12 @@ internal sealed class HotKeyCombination : ObservableObject
HWND hwnd = currentWindowReference.GetWindowHandle(); HWND hwnd = currentWindowReference.GetWindowHandle();
BOOL result = RegisterHotKey(hwnd, hotKeyId, Modifiers, (uint)Key); BOOL result = RegisterHotKey(hwnd, hotKeyId, Modifiers, (uint)Key);
registered = result; registered = result;
if (!result)
{
infoBarService.Warning(SH.FormatCoreWindowHotkeyCombinationRegisterFailed(SH.ViewPageSettingKeyShortcutAutoClickingHeader, DisplayName));
}
return result; return result;
} }

View File

@@ -186,6 +186,9 @@
<data name="CoreWebView2HelperVersionUndetected" xml:space="preserve"> <data name="CoreWebView2HelperVersionUndetected" xml:space="preserve">
<value>未检测到 WebView2 运行时</value> <value>未检测到 WebView2 运行时</value>
</data> </data>
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
<value>[{0}] 热键 [{1}] 注册失败</value>
</data>
<data name="FilePickerExportCommit" xml:space="preserve"> <data name="FilePickerExportCommit" xml:space="preserve">
<value>导出</value> <value>导出</value>
</data> </data>

View File

@@ -86,7 +86,7 @@ internal sealed partial class AnnouncementService : IAnnouncementService
foreach (ref readonly Announcement item in CollectionsMarshal.AsSpan(listWrapper.List)) foreach (ref readonly Announcement item in CollectionsMarshal.AsSpan(listWrapper.List))
{ {
item.Subtitle = new StringBuilder(item.Subtitle).Replace("\r<br>", string.Empty).ToString(); item.Subtitle = new StringBuilder(item.Subtitle).Replace("\r<br>", string.Empty).ToString();
item.Content = AnnouncementRegex.XmlTimeTagRegex.Replace(item.Content, x => x.Groups[1].Value); item.Content = AnnouncementRegex.XmlTimeTagRegex().Replace(item.Content, x => x.Groups[1].Value);
} }
} }
} }
@@ -133,7 +133,7 @@ internal sealed partial class AnnouncementService : IAnnouncementService
continue; continue;
} }
MatchCollection matches = AnnouncementRegex.XmlTimeTagRegex.Matches(announcement.Content); MatchCollection matches = AnnouncementRegex.XmlTimeTagRegex().Matches(announcement.Content);
if (matches.Count < 2) if (matches.Count < 2)
{ {
continue; continue;

View File

@@ -298,6 +298,7 @@
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.0.240109" /> <PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.0.240109" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.0.240109" /> <PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.0.240109" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.0.240109" /> <PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" 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.2" />

View File

@@ -7,7 +7,6 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mxi="using:Microsoft.Xaml.Interactivity" xmlns:mxi="using:Microsoft.Xaml.Interactivity"
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
xmlns:shc="using:Snap.Hutao.Control" xmlns:shc="using:Snap.Hutao.Control"
xmlns:shcb="using:Snap.Hutao.Control.Behavior" xmlns:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shci="using:Snap.Hutao.Control.Image" xmlns:shci="using:Snap.Hutao.Control.Image"
@@ -73,6 +72,10 @@
</shct:DescriptionTextBlock> </shct:DescriptionTextBlock>
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="SuggestionTemplate">
<TextBlock VerticalAlignment="Center" Text="{Binding}"/>
</DataTemplate>
<DataTemplate x:Key="AvatarListTemplate"> <DataTemplate x:Key="AvatarListTemplate">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@@ -265,22 +268,20 @@
</CommandBar.Content> </CommandBar.Content>
<!--<AppBarButton Icon="{shcm:FontIcon Glyph=&#xE946;}" Label="搜索提示"/>--> <!--<AppBarButton Icon="{shcm:FontIcon Glyph=&#xE946;}" Label="搜索提示"/>-->
<AppBarElementContainer> <AppBarElementContainer>
<AutoSuggestBox <cwc:TokenizingTextBox
Width="240" x:Name="AvatarSuggestBox"
Height="36" Width="520"
Margin="6,6,6,0" Margin="6,0,6,0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
ItemsSource="{Binding FilterTokens, Mode=TwoWay}"
MaximumTokens="5"
PlaceholderText="{shcm:ResourceString Name=ViewPageWiKiAvatarAutoSuggestBoxPlaceHolder}" PlaceholderText="{shcm:ResourceString Name=ViewPageWiKiAvatarAutoSuggestBoxPlaceHolder}"
QueryIcon="{shcm:FontIcon Glyph=&#xE721;}" QueryIcon="{cw:FontIconSource Glyph=&#xE721;}"
Style="{StaticResource DefaultAutoSuggestBoxStyle}" SuggestedItemTemplate="{StaticResource SuggestionTemplate}"
Text="{Binding FilterText, Mode=TwoWay}"> SuggestedItemsSource="{Binding AvailableQueries}"
<mxi:Interaction.Behaviors> Text="{Binding FilterToken, Mode=TwoWay}"
<mxic:EventTriggerBehavior EventName="QuerySubmitted"> TokenItemTemplate="{StaticResource SuggestionTemplate}"/>
<mxic:InvokeCommandAction Command="{Binding FilterCommand}" CommandParameter="{Binding FilterText}"/>
</mxic:EventTriggerBehavior>
</mxi:Interaction.Behaviors>
</AutoSuggestBox>
</AppBarElementContainer> </AppBarElementContainer>
<AppBarButton <AppBarButton
Command="{Binding CultivateCommand}" Command="{Binding CultivateCommand}"

View File

@@ -1,6 +1,8 @@
// 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.Controls;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control; using Snap.Hutao.Control;
using Snap.Hutao.ViewModel.Wiki; using Snap.Hutao.ViewModel.Wiki;
@@ -17,7 +19,19 @@ internal sealed partial class WikiAvatarPage : ScopedPage
/// </summary> /// </summary>
public WikiAvatarPage() public WikiAvatarPage()
{ {
InitializeWith<WikiAvatarViewModel>(); WikiAvatarViewModel viewModel = InitializeWith<WikiAvatarViewModel>();
InitializeComponent(); InitializeComponent();
viewModel.Initialize(new TokenizingTextBoxAccessor(AvatarSuggestBox));
}
private class TokenizingTextBoxAccessor : ITokenizingTextBoxAccessor
{
public TokenizingTextBoxAccessor(TokenizingTextBox tokenizingTextBox)
{
TokenizingTextBox = tokenizingTextBox;
}
public TokenizingTextBox TokenizingTextBox { get; private set; }
} }
} }

View File

@@ -32,6 +32,10 @@
Source="{Binding Converter={StaticResource PropertyDescriptor}}"/> Source="{Binding Converter={StaticResource PropertyDescriptor}}"/>
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="SuggestionTemplate">
<TextBlock VerticalAlignment="Center" Text="{Binding}"/>
</DataTemplate>
<DataTemplate x:Key="WeaponListTemplate"> <DataTemplate x:Key="WeaponListTemplate">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@@ -131,21 +135,21 @@
LocalSettingKeySuffixForCurrent="WikiWeaponPage.Weapons"/> LocalSettingKeySuffixForCurrent="WikiWeaponPage.Weapons"/>
</CommandBar.Content> </CommandBar.Content>
<AppBarElementContainer> <AppBarElementContainer>
<AutoSuggestBox <cwc:TokenizingTextBox
Width="240" x:Name="WeaponSuggestBox"
Height="36" Width="520"
Margin="16,6,6,0" Margin="16,0,6,0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
ItemTemplate="{StaticResource SuggestionTemplate}"
ItemsSource="{Binding FilterTokens, Mode=TwoWay}"
MaximumTokens="5"
PlaceholderText="{shcm:ResourceString Name=ViewPageWiKiWeaponAutoSuggestBoxPlaceHolder}" PlaceholderText="{shcm:ResourceString Name=ViewPageWiKiWeaponAutoSuggestBoxPlaceHolder}"
QueryIcon="{shcm:FontIcon Glyph=&#xE721;}" QueryIcon="{cw:FontIconSource Glyph=&#xE721;}"
Text="{Binding FilterText, Mode=TwoWay}"> SuggestedItemTemplate="{StaticResource SuggestionTemplate}"
<mxi:Interaction.Behaviors> SuggestedItemsSource="{Binding AvailableQueries}"
<mxic:EventTriggerBehavior EventName="QuerySubmitted"> Text="{Binding FilterToken, Mode=TwoWay}"
<mxic:InvokeCommandAction Command="{Binding FilterCommand}" CommandParameter="{Binding FilterText}"/> TokenItemTemplate="{StaticResource SuggestionTemplate}"/>
</mxic:EventTriggerBehavior>
</mxi:Interaction.Behaviors>
</AutoSuggestBox>
</AppBarElementContainer> </AppBarElementContainer>
<AppBarButton <AppBarButton
Command="{Binding CultivateCommand}" Command="{Binding CultivateCommand}"

View File

@@ -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 CommunityToolkit.WinUI.Controls;
using Snap.Hutao.Control; using Snap.Hutao.Control;
using Snap.Hutao.ViewModel.Wiki; using Snap.Hutao.ViewModel.Wiki;
@@ -17,7 +18,19 @@ internal sealed partial class WikiWeaponPage : ScopedPage
/// </summary> /// </summary>
public WikiWeaponPage() public WikiWeaponPage()
{ {
InitializeWith<WikiWeaponViewModel>(); WikiWeaponViewModel viewModel = InitializeWith<WikiWeaponViewModel>();
InitializeComponent(); InitializeComponent();
viewModel.Initialize(new TokenizingTextBoxAccessor(WeaponSuggestBox));
}
private class TokenizingTextBoxAccessor : ITokenizingTextBoxAccessor
{
public TokenizingTextBoxAccessor(TokenizingTextBox tokenizingTextBox)
{
TokenizingTextBox = tokenizingTextBox;
}
public TokenizingTextBox TokenizingTextBox { get; private set; }
} }
} }

View File

@@ -0,0 +1,12 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Controls;
using Snap.Hutao.Control;
namespace Snap.Hutao.ViewModel.Wiki;
internal interface ITokenizingTextBoxAccessor : IXamlElementAccessor
{
TokenizingTextBox TokenizingTextBox { get; }
}

View File

@@ -0,0 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.ViewModel.Wiki;
internal interface IWikiViewModelInitialization
{
void Initialize(ITokenizingTextBoxAccessor accessor);
}

View File

@@ -1,11 +1,14 @@
// 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.Controls;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control.Collection.AdvancedCollectionView; using Snap.Hutao.Control.Collection.AdvancedCollectionView;
using Snap.Hutao.Factory.ContentDialog; using Snap.Hutao.Factory.ContentDialog;
using Snap.Hutao.Model.Calculable; using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Model.Entity.Primitive; using Snap.Hutao.Model.Entity.Primitive;
using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Intrinsic.Frozen;
using Snap.Hutao.Model.Metadata; using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Avatar; using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Item; using Snap.Hutao.Model.Metadata.Item;
@@ -17,6 +20,8 @@ using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.User; using Snap.Hutao.Service.User;
using Snap.Hutao.View.Dialog; using Snap.Hutao.View.Dialog;
using Snap.Hutao.Web.Response; using Snap.Hutao.Web.Response;
using System.Collections.Frozen;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using CalculateAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta; using CalculateAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta;
using CalculateClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient; using CalculateClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient;
@@ -32,7 +37,7 @@ namespace Snap.Hutao.ViewModel.Wiki;
[HighQuality] [HighQuality]
[ConstructorGenerated] [ConstructorGenerated]
[Injection(InjectAs.Scoped)] [Injection(InjectAs.Scoped)]
internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel, IWikiViewModelInitialization
{ {
private readonly IContentDialogFactory contentDialogFactory; private readonly IContentDialogFactory contentDialogFactory;
private readonly ICultivationService cultivationService; private readonly ICultivationService cultivationService;
@@ -45,10 +50,12 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
private AdvancedCollectionView<Avatar>? avatars; private AdvancedCollectionView<Avatar>? avatars;
private Avatar? selected; private Avatar? selected;
private string? filterText; private ObservableCollection<string>? filterTokens;
private string? filterToken;
private BaseValueInfo? baseValueInfo; private BaseValueInfo? baseValueInfo;
private Dictionary<Level, Dictionary<GrowCurveType, float>>? levelAvatarCurveMap; private Dictionary<Level, Dictionary<GrowCurveType, float>>? levelAvatarCurveMap;
private List<Promote>? promotes; private List<Promote>? promotes;
private FrozenSet<string> availableQueries;
/// <summary> /// <summary>
/// 角色列表 /// 角色列表
@@ -75,9 +82,21 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
public BaseValueInfo? BaseValueInfo { get => baseValueInfo; set => SetProperty(ref baseValueInfo, value); } public BaseValueInfo? BaseValueInfo { get => baseValueInfo; set => SetProperty(ref baseValueInfo, value); }
/// <summary> /// <summary>
/// 筛选文本 /// 保存的筛选标志
/// </summary> /// </summary>
public string? FilterText { get => filterText; set => SetProperty(ref filterText, value); } public ObservableCollection<string>? FilterTokens { get => filterTokens; set => SetProperty(ref filterTokens, value); }
public string? FilterToken { get => filterToken; set => SetProperty(ref filterToken, value); }
public FrozenSet<string>? AvailableQueries { get => availableQueries; }
public void Initialize(ITokenizingTextBoxAccessor accessor)
{
accessor.TokenizingTextBox.TextChanged += OnFilterSuggestionRequested;
accessor.TokenizingTextBox.QuerySubmitted += OnQuerySubmitted;
accessor.TokenizingTextBox.TokenItemAdded += OnTokenItemModified;
accessor.TokenizingTextBox.TokenItemRemoved += OnTokenItemModified;
}
protected override async ValueTask<bool> InitializeUIAsync() protected override async ValueTask<bool> InitializeUIAsync()
{ {
@@ -101,6 +120,18 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
await taskContext.SwitchToMainThreadAsync(); await taskContext.SwitchToMainThreadAsync();
Avatars = new(list, true); Avatars = new(list, true);
Selected = Avatars.View.ElementAtOrDefault(0); Selected = Avatars.View.ElementAtOrDefault(0);
FilterTokens = [];
availableQueries = FrozenSet.ToFrozenSet<string>(
[
.. avatars.Select(a => a.Name),
.. IntrinsicFrozen.AssociationTypes,
.. IntrinsicFrozen.BodyTypes,
.. IntrinsicFrozen.ElementNames,
.. IntrinsicFrozen.ItemQualities,
.. IntrinsicFrozen.WeaponTypes,
]);
return true; return true;
} }
@@ -202,21 +233,53 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
BaseValueInfo = new(avatar.MaxLevel, propertyCurveValues, levelAvatarCurveMap, avatarPromoteMap); BaseValueInfo = new(avatar.MaxLevel, propertyCurveValues, levelAvatarCurveMap, avatarPromoteMap);
} }
[Command("FilterCommand")] private void OnFilterSuggestionRequested(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
private void ApplyFilter(string? input)
{ {
if (Avatars is null) if (Avatars is null)
{ {
return; return;
} }
if (string.IsNullOrWhiteSpace(input)) if (string.IsNullOrWhiteSpace(FilterToken))
{
return;
}
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
sender.ItemsSource = availableQueries.Where(q => q.Contains(FilterToken, StringComparison.OrdinalIgnoreCase));
}
}
private void OnQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if (args.ChosenSuggestion is not null)
{
return;
}
ApplyFilter();
}
private void OnTokenItemModified(TokenizingTextBox sender, object args)
{
ApplyFilter();
}
private void ApplyFilter()
{
if (Avatars is null)
{
return;
}
if (FilterTokens.IsNullOrEmpty())
{ {
Avatars.Filter = default!; Avatars.Filter = default!;
return; return;
} }
Avatars.Filter = AvatarFilter.Compile(input); Avatars.Filter = AvatarFilter.Compile(string.Join(' ', FilterTokens));
if (Selected is not null && Avatars.Contains(Selected)) if (Selected is not null && Avatars.Contains(Selected))
{ {

View File

@@ -1,11 +1,14 @@
// 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.Controls;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control.Collection.AdvancedCollectionView; using Snap.Hutao.Control.Collection.AdvancedCollectionView;
using Snap.Hutao.Factory.ContentDialog; using Snap.Hutao.Factory.ContentDialog;
using Snap.Hutao.Model.Calculable; using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Model.Entity.Primitive; using Snap.Hutao.Model.Entity.Primitive;
using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Intrinsic.Frozen;
using Snap.Hutao.Model.Metadata; using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Item; using Snap.Hutao.Model.Metadata.Item;
using Snap.Hutao.Model.Metadata.Weapon; using Snap.Hutao.Model.Metadata.Weapon;
@@ -17,6 +20,7 @@ using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.User; using Snap.Hutao.Service.User;
using Snap.Hutao.View.Dialog; using Snap.Hutao.View.Dialog;
using Snap.Hutao.Web.Response; using Snap.Hutao.Web.Response;
using System.Collections.Frozen;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using CalculateAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta; using CalculateAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta;
using CalculateClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient; using CalculateClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient;
@@ -29,7 +33,7 @@ namespace Snap.Hutao.ViewModel.Wiki;
/// </summary> /// </summary>
[ConstructorGenerated] [ConstructorGenerated]
[Injection(InjectAs.Scoped)] [Injection(InjectAs.Scoped)]
internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel, IWikiViewModelInitialization
{ {
private readonly IContentDialogFactory contentDialogFactory; private readonly IContentDialogFactory contentDialogFactory;
private readonly CalculateClient calculateClient; private readonly CalculateClient calculateClient;
@@ -42,10 +46,12 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
private AdvancedCollectionView<Weapon>? weapons; private AdvancedCollectionView<Weapon>? weapons;
private Weapon? selected; private Weapon? selected;
private string? filterText; private List<string>? filterTokens;
private string? filterToken;
private BaseValueInfo? baseValueInfo; private BaseValueInfo? baseValueInfo;
private Dictionary<Level, Dictionary<GrowCurveType, float>>? levelWeaponCurveMap; private Dictionary<Level, Dictionary<GrowCurveType, float>>? levelWeaponCurveMap;
private List<Promote>? promotes; private List<Promote>? promotes;
private FrozenSet<string> availableQueries;
/// <summary> /// <summary>
/// 角色列表 /// 角色列表
@@ -72,9 +78,21 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
public BaseValueInfo? BaseValueInfo { get => baseValueInfo; set => SetProperty(ref baseValueInfo, value); } public BaseValueInfo? BaseValueInfo { get => baseValueInfo; set => SetProperty(ref baseValueInfo, value); }
/// <summary> /// <summary>
/// 筛选文本 /// 保存的筛选标志
/// </summary> /// </summary>
public string? FilterText { get => filterText; set => SetProperty(ref filterText, value); } public List<string>? FilterTokens { get => filterTokens; set => SetProperty(ref filterTokens, value); }
public string? FilterToken { get => filterToken; set => SetProperty(ref filterToken, value); }
public FrozenSet<string> AvailableQueries { get => availableQueries; }
public void Initialize(ITokenizingTextBoxAccessor accessor)
{
accessor.TokenizingTextBox.TextChanged += OnFilterSuggestionRequested;
accessor.TokenizingTextBox.QuerySubmitted += OnQuerySubmitted;
accessor.TokenizingTextBox.TokenItemAdded += OnTokenItemModified;
accessor.TokenizingTextBox.TokenItemRemoved += OnTokenItemModified;
}
/// <inheritdoc/> /// <inheritdoc/>
protected override async Task OpenUIAsync() protected override async Task OpenUIAsync()
@@ -98,6 +116,15 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
Weapons = new(list, true); Weapons = new(list, true);
Selected = Weapons.View.ElementAtOrDefault(0); Selected = Weapons.View.ElementAtOrDefault(0);
FilterTokens = [];
availableQueries = FrozenSet.ToFrozenSet(
[
.. weapons.Select(w => w.Name),
.. IntrinsicFrozen.ItemQualities,
.. IntrinsicFrozen.FightProperties,
.. IntrinsicFrozen.WeaponTypes,
]);
} }
} }
@@ -187,21 +214,53 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
BaseValueInfo = new(weapon.MaxLevel, propertyCurveValues, levelWeaponCurveMap, weaponPromoteMap); BaseValueInfo = new(weapon.MaxLevel, propertyCurveValues, levelWeaponCurveMap, weaponPromoteMap);
} }
[Command("FilterCommand")] private void OnFilterSuggestionRequested(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
private void ApplyFilter(string? input)
{ {
if (Weapons is null) if (Weapons is null)
{ {
return; return;
} }
if (string.IsNullOrWhiteSpace(input)) if (string.IsNullOrWhiteSpace(FilterToken))
{
return;
}
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
sender.ItemsSource = availableQueries.Where(q => q.Contains(FilterToken, StringComparison.OrdinalIgnoreCase));
}
}
private void OnQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if (args.ChosenSuggestion is not null)
{
return;
}
ApplyFilter();
}
private void OnTokenItemModified(TokenizingTextBox sender, object args)
{
ApplyFilter();
}
private void ApplyFilter()
{
if (Weapons is null)
{
return;
}
if (FilterTokens.IsNullOrEmpty())
{ {
Weapons.Filter = default!; Weapons.Filter = default!;
return; return;
} }
Weapons.Filter = WeaponFilter.Compile(input); Weapons.Filter = WeaponFilter.Compile(string.Join(' ', FilterTokens));
if (Selected is not null && Weapons.Contains(Selected)) if (Selected is not null && Weapons.Contains(Selected))
{ {

View File

@@ -22,9 +22,6 @@ internal static partial class AnnouncementRegex
/// <inheritdoc cref="SH.WebAnnouncementMatchPermanentActivityTime"/> /// <inheritdoc cref="SH.WebAnnouncementMatchPermanentActivityTime"/>
public static readonly Regex PermanentActivityAfterUpdateTimeRegex = new(SH.WebAnnouncementMatchPermanentActivityTime, RegexOptions.Compiled); public static readonly Regex PermanentActivityAfterUpdateTimeRegex = new(SH.WebAnnouncementMatchPermanentActivityTime, RegexOptions.Compiled);
/// <inheritdoc cref="XmlTagRegex"/>
public static readonly Regex XmlTimeTagRegex = XmlTagRegex();
[GeneratedRegex("&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;", RegexOptions.Multiline)] [GeneratedRegex("&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;", RegexOptions.Multiline)]
private static partial Regex XmlTagRegex(); public static partial Regex XmlTimeTagRegex();
} }