migrate to TokenizingTextBox

This commit is contained in:
qhy040404
2024-02-23 18:08:30 +08:00
parent e3adc2e595
commit 3b86783493
9 changed files with 104 additions and 66 deletions

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"
@@ -74,10 +73,7 @@
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="SuggestionTemplate"> <DataTemplate x:Key="SuggestionTemplate">
<TextBlock <TextBlock VerticalAlignment="Center" Text="{Binding}"/>
Margin="12,0,0,0"
VerticalAlignment="Center"
Text="{Binding}"/>
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="AvatarListTemplate"> <DataTemplate x:Key="AvatarListTemplate">
@@ -272,18 +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
x:Name="AvatarSuggestBox" x:Name="AvatarSuggestBox"
Width="240" Width="520"
Height="36" Margin="6,0,6,0"
Margin="6,6,6,0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
ItemTemplate="{StaticResource SuggestionTemplate}" 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}"
Text="{Binding FilterToken, Mode=TwoWay}"
TokenItemTemplate="{StaticResource SuggestionTemplate}"/>
</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 Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control; using Snap.Hutao.Control;
using Snap.Hutao.ViewModel.Wiki; using Snap.Hutao.ViewModel.Wiki;
@@ -21,16 +22,16 @@ internal sealed partial class WikiAvatarPage : ScopedPage
WikiAvatarViewModel viewModel = InitializeWith<WikiAvatarViewModel>(); WikiAvatarViewModel viewModel = InitializeWith<WikiAvatarViewModel>();
InitializeComponent(); InitializeComponent();
viewModel.Initialize(new AutoSuggestBoxAccessor(AvatarSuggestBox)); viewModel.Initialize(new TokenizingTextBoxAccessor(AvatarSuggestBox));
} }
private class AutoSuggestBoxAccessor : IAutoSuggestBoxAccessor private class TokenizingTextBoxAccessor : ITokenizingTextBoxAccessor
{ {
public AutoSuggestBoxAccessor(AutoSuggestBox autoSuggestBox) public TokenizingTextBoxAccessor(TokenizingTextBox tokenizingTextBox)
{ {
AutoSuggestBox = autoSuggestBox; TokenizingTextBox = tokenizingTextBox;
} }
public AutoSuggestBox AutoSuggestBox { get; private set; } public TokenizingTextBox TokenizingTextBox { get; private set; }
} }
} }

View File

@@ -33,10 +33,7 @@
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="SuggestionTemplate"> <DataTemplate x:Key="SuggestionTemplate">
<TextBlock <TextBlock VerticalAlignment="Center" Text="{Binding}"/>
Margin="12,0,0,0"
VerticalAlignment="Center"
Text="{Binding}"/>
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="WeaponListTemplate"> <DataTemplate x:Key="WeaponListTemplate">
@@ -138,17 +135,21 @@
LocalSettingKeySuffixForCurrent="WikiWeaponPage.Weapons"/> LocalSettingKeySuffixForCurrent="WikiWeaponPage.Weapons"/>
</CommandBar.Content> </CommandBar.Content>
<AppBarElementContainer> <AppBarElementContainer>
<AutoSuggestBox <cwc:TokenizingTextBox
x:Name="WeaponSuggestBox" x:Name="WeaponSuggestBox"
Width="240" Width="520"
Height="36" Margin="16,0,6,0"
Margin="16,6,6,0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
ItemTemplate="{StaticResource SuggestionTemplate}" 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}"
SuggestedItemsSource="{Binding AvailableQueries}"
Text="{Binding FilterToken, Mode=TwoWay}"
TokenItemTemplate="{StaticResource SuggestionTemplate}"/>
</AppBarElementContainer> </AppBarElementContainer>
<AppBarButton <AppBarButton
Command="{Binding CultivateCommand}" Command="{Binding CultivateCommand}"

View File

@@ -1,7 +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 Microsoft.UI.Xaml.Controls; using CommunityToolkit.WinUI.Controls;
using Snap.Hutao.Control; using Snap.Hutao.Control;
using Snap.Hutao.ViewModel.Wiki; using Snap.Hutao.ViewModel.Wiki;
@@ -21,16 +21,16 @@ internal sealed partial class WikiWeaponPage : ScopedPage
WikiWeaponViewModel viewModel = InitializeWith<WikiWeaponViewModel>(); WikiWeaponViewModel viewModel = InitializeWith<WikiWeaponViewModel>();
InitializeComponent(); InitializeComponent();
viewModel.Initialize(new AutoSuggestBoxAccessor(WeaponSuggestBox)); viewModel.Initialize(new TokenizingTextBoxAccessor(WeaponSuggestBox));
} }
private class AutoSuggestBoxAccessor : IAutoSuggestBoxAccessor private class TokenizingTextBoxAccessor : ITokenizingTextBoxAccessor
{ {
public AutoSuggestBoxAccessor(AutoSuggestBox autoSuggestBox) public TokenizingTextBoxAccessor(TokenizingTextBox tokenizingTextBox)
{ {
AutoSuggestBox = autoSuggestBox; TokenizingTextBox = tokenizingTextBox;
} }
public AutoSuggestBox AutoSuggestBox { get; private set; } public TokenizingTextBox TokenizingTextBox { get; private set; }
} }
} }

View File

@@ -1,12 +1,12 @@
// 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.Controls; using CommunityToolkit.WinUI.Controls;
using Snap.Hutao.Control; using Snap.Hutao.Control;
namespace Snap.Hutao.ViewModel.Wiki; namespace Snap.Hutao.ViewModel.Wiki;
internal interface IAutoSuggestBoxAccessor : IXamlElementAccessor internal interface ITokenizingTextBoxAccessor : IXamlElementAccessor
{ {
AutoSuggestBox AutoSuggestBox { get; } TokenizingTextBox TokenizingTextBox { get; }
} }

View File

@@ -5,5 +5,5 @@ namespace Snap.Hutao.ViewModel.Wiki;
internal interface IWikiViewModelInitialization internal interface IWikiViewModelInitialization
{ {
void Initialize(IAutoSuggestBoxAccessor accessor); void Initialize(ITokenizingTextBoxAccessor accessor);
} }

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 Microsoft.UI.Xaml.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;
@@ -20,6 +21,7 @@ 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.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;
@@ -48,7 +50,8 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel, IWiki
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;
@@ -79,15 +82,20 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel, IWiki
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 void Initialize(IAutoSuggestBoxAccessor accessor) public string? FilterToken { get => filterToken; set => SetProperty(ref filterToken, value); }
public FrozenSet<string>? AvailableQueries { get => availableQueries; }
public void Initialize(ITokenizingTextBoxAccessor accessor)
{ {
accessor.AutoSuggestBox.TextChanged += OnFilterSuggestionRequested; accessor.TokenizingTextBox.TextChanged += OnFilterSuggestionRequested;
accessor.AutoSuggestBox.SuggestionChosen += OnFilterSuggestionChosen; accessor.TokenizingTextBox.QuerySubmitted += OnQuerySubmitted;
accessor.AutoSuggestBox.QuerySubmitted += ApplyFilter; accessor.TokenizingTextBox.TokenItemAdded += OnTokenItemModified;
accessor.TokenizingTextBox.TokenItemRemoved += OnTokenItemModified;
} }
protected override async ValueTask<bool> InitializeUIAsync() protected override async ValueTask<bool> InitializeUIAsync()
@@ -112,6 +120,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel, IWiki
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>( availableQueries = FrozenSet.ToFrozenSet<string>(
[ [
@@ -231,36 +240,46 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel, IWiki
return; return;
} }
if (string.IsNullOrWhiteSpace(FilterText)) if (string.IsNullOrWhiteSpace(FilterToken))
{ {
return; return;
} }
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput) if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{ {
sender.ItemsSource = availableQueries.Where(q => q.Contains(FilterText, StringComparison.OrdinalIgnoreCase)); sender.ItemsSource = availableQueries.Where(q => q.Contains(FilterToken, StringComparison.OrdinalIgnoreCase));
} }
} }
private void OnFilterSuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args) private void OnQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{ {
sender.Text = args.SelectedItem.ToString(); if (args.ChosenSuggestion is not null)
{
return;
}
ApplyFilter();
} }
private void ApplyFilter(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args) private void OnTokenItemModified(TokenizingTextBox sender, object args)
{
ApplyFilter();
}
private void ApplyFilter()
{ {
if (Avatars is null) if (Avatars is null)
{ {
return; return;
} }
if (string.IsNullOrWhiteSpace(FilterText)) if (FilterTokens.IsNullOrEmpty())
{ {
Avatars.Filter = default!; Avatars.Filter = default!;
return; return;
} }
Avatars.Filter = AvatarFilter.Compile(FilterText); 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,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 Microsoft.UI.Xaml.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;
@@ -45,7 +46,8 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel, IWiki
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;
@@ -76,15 +78,20 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel, IWiki
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 void Initialize(IAutoSuggestBoxAccessor accessor) public string? FilterToken { get => filterToken; set => SetProperty(ref filterToken, value); }
public FrozenSet<string> AvailableQueries { get => availableQueries; }
public void Initialize(ITokenizingTextBoxAccessor accessor)
{ {
accessor.AutoSuggestBox.TextChanged += OnFilterSuggestionRequested; accessor.TokenizingTextBox.TextChanged += OnFilterSuggestionRequested;
accessor.AutoSuggestBox.SuggestionChosen += OnFilterSuggestionChosen; accessor.TokenizingTextBox.QuerySubmitted += OnQuerySubmitted;
accessor.AutoSuggestBox.QuerySubmitted += ApplyFilter; accessor.TokenizingTextBox.TokenItemAdded += OnTokenItemModified;
accessor.TokenizingTextBox.TokenItemRemoved += OnTokenItemModified;
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -109,6 +116,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel, IWiki
Weapons = new(list, true); Weapons = new(list, true);
Selected = Weapons.View.ElementAtOrDefault(0); Selected = Weapons.View.ElementAtOrDefault(0);
FilterTokens = [];
availableQueries = FrozenSet.ToFrozenSet( availableQueries = FrozenSet.ToFrozenSet(
[ [
@@ -213,36 +221,46 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel, IWiki
return; return;
} }
if (string.IsNullOrWhiteSpace(FilterText)) if (string.IsNullOrWhiteSpace(FilterToken))
{ {
return; return;
} }
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput) if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{ {
sender.ItemsSource = availableQueries.Where(q => q.Contains(FilterText, StringComparison.OrdinalIgnoreCase)); sender.ItemsSource = availableQueries.Where(q => q.Contains(FilterToken, StringComparison.OrdinalIgnoreCase));
} }
} }
private void OnFilterSuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args) private void OnQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{ {
sender.Text = args.SelectedItem.ToString(); if (args.ChosenSuggestion is not null)
{
return;
}
ApplyFilter();
} }
private void ApplyFilter(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args) private void OnTokenItemModified(TokenizingTextBox sender, object args)
{
ApplyFilter();
}
private void ApplyFilter()
{ {
if (Weapons is null) if (Weapons is null)
{ {
return; return;
} }
if (string.IsNullOrWhiteSpace(FilterText)) if (FilterTokens.IsNullOrEmpty())
{ {
Weapons.Filter = default!; Weapons.Filter = default!;
return; return;
} }
Weapons.Filter = WeaponFilter.Compile(FilterText); Weapons.Filter = WeaponFilter.Compile(string.Join(' ', FilterTokens));
if (Selected is not null && Weapons.Contains(Selected)) if (Selected is not null && Weapons.Contains(Selected))
{ {