diff --git a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/AutoSuggestTokenBox.cs b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/AutoSuggestTokenBox.cs index 6ed4af22..2d6b7a45 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/AutoSuggestTokenBox.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/AutoSuggestTokenBox.cs @@ -4,11 +4,13 @@ using CommunityToolkit.WinUI.Controls; using Microsoft.UI.Xaml.Controls; using Snap.Hutao.Control.SuggestBox; +using Snap.Hutao.Model.Intrinsic.Frozen; +using System.Collections.Frozen; namespace Snap.Hutao.View.Control; [DependencyProperty("FilterCommand", typeof(ICommand))] -[DependencyProperty("ITokenizableItemsSource", typeof(IEnumerable))] +[DependencyProperty("AvailableTokens", typeof(FrozenDictionary))] internal sealed partial class AutoSuggestTokenBox : TokenizingTextBox { public AutoSuggestTokenBox() @@ -22,11 +24,6 @@ internal sealed partial class AutoSuggestTokenBox : TokenizingTextBox private void OnFilterSuggestionRequested(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args) { - if (SuggestedItemsSource is not IEnumerable availableQueries) - { - return; - } - if (string.IsNullOrWhiteSpace(Text)) { return; @@ -34,7 +31,7 @@ internal sealed partial class AutoSuggestTokenBox : TokenizingTextBox if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput) { - sender.ItemsSource = availableQueries.Where(q => q.Contains(Text, StringComparison.OrdinalIgnoreCase)); + sender.ItemsSource = AvailableTokens.Values.Where(q => q.Value.Contains(Text, StringComparison.OrdinalIgnoreCase)); } } @@ -58,18 +55,7 @@ internal sealed partial class AutoSuggestTokenBox : TokenizingTextBox return; } - if (ITokenizableItemsSource is null) - { - return; - } - - if (ITokenizableItemsSource.SingleOrDefault(i => i.Name == args.TokenText) is { } item) - { - args.Item = item.Tokenize(); - return; - } - - args.Item = new SearchToken(args.TokenText); + args.Item = AvailableTokens[args.TokenText]; } private void OnTokenItemModified(TokenizingTextBox sender, object args) diff --git a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/ISearchToken.cs b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/ISearchToken.cs deleted file mode 100644 index 7b054f10..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/ISearchToken.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Control.SuggestBox; - -internal interface ISearchToken -{ - string Value { get; set; } -} diff --git a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/ITokenizable.cs b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/ITokenizable.cs deleted file mode 100644 index bb8f32b8..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/ITokenizable.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Control.SuggestBox; - -internal interface ITokenizable -{ - string Name { get; } - - ISearchToken Tokenize(); -} diff --git a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchToken.Wiki.cs b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchToken.Wiki.cs deleted file mode 100644 index 256f51ee..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchToken.Wiki.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Model.Intrinsic.Frozen; -using Snap.Hutao.Model.Metadata.Converter; -using Windows.UI; - -namespace Snap.Hutao.Control.SuggestBox; - -internal partial class SearchToken -{ - private SearchTokenKind kind; - private bool isKindInitialized; - private object isKindInitializedLock = new(); - - public SearchToken(string value, Uri? sideIconUri) - : this(value) - { - SideIconUri = sideIconUri; - } - - public Uri? SideIconUri { get; } - - public Uri? IconUri - { - get => Kind switch - { - SearchTokenKind.AssociationTypes => AssociationTypeIconConverter.AssociationTypeNameToIconUri(Value), - SearchTokenKind.ElementNames => ElementNameIconConverter.ElementNameToIconUri(Value), - SearchTokenKind.WeaponTypes => WeaponTypeIconConverter.WeaponTypeNameToIconUri(Value), - _ => null, - }; - } - - public Color? Quality - { - get => Kind switch - { - SearchTokenKind.ItemQualities => QualityColorConverter.QualityNameToColor(Value), - _ => null, - }; - } - - public SearchTokenKind Kind - { - get - { - return LazyInitializer.EnsureInitialized(ref kind, ref isKindInitialized, ref isKindInitializedLock, GetKind); - - SearchTokenKind GetKind() - { - if (IntrinsicFrozen.AssociationTypes.Contains(Value)) - { - return SearchTokenKind.AssociationTypes; - } - - if (IntrinsicFrozen.BodyTypes.Contains(Value)) - { - return SearchTokenKind.BodyTypes; - } - - if (IntrinsicFrozen.ElementNames.Contains(Value)) - { - return SearchTokenKind.ElementNames; - } - - if (IntrinsicFrozen.FightProperties.Contains(Value)) - { - return SearchTokenKind.FightProperties; - } - - if (IntrinsicFrozen.ItemQualities.Contains(Value)) - { - return SearchTokenKind.ItemQualities; - } - - if (IntrinsicFrozen.WeaponTypes.Contains(Value)) - { - return SearchTokenKind.WeaponTypes; - } - - return SearchTokenKind.Other; - } - } - } -} diff --git a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchToken.cs b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchToken.cs index 0eb17654..1ee15e17 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchToken.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchToken.cs @@ -1,17 +1,31 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Windows.UI; + namespace Snap.Hutao.Control.SuggestBox; -internal sealed partial class SearchToken : ISearchToken +internal sealed class SearchToken { - public SearchToken(string value) + public SearchToken(string value, SearchTokenKind kind, Uri? iconUri = null, Uri? sideIconUri = null, Color? quality = null) { Value = value; + Kind = kind; + IconUri = iconUri; + SideIconUri = sideIconUri; + Quality = quality; } + public SearchTokenKind Kind { get; } + public string Value { get; set; } = default!; + public Uri? IconUri { get; } + + public Uri? SideIconUri { get; } + + public Color? Quality { get; } + public override string ToString() { return Value; diff --git a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchTokenKind.Wiki.cs b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchTokenKind.cs similarity index 87% rename from src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchTokenKind.Wiki.cs rename to src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchTokenKind.cs index 4f1868c8..f687a205 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchTokenKind.Wiki.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchTokenKind.cs @@ -6,10 +6,11 @@ namespace Snap.Hutao.Control.SuggestBox; internal enum SearchTokenKind { AssociationTypes, + Avatars, BodyTypes, ElementNames, FightProperties, ItemQualities, - Other, // Include avatar and weapon + Weapons, WeaponTypes, } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs index 343afd8b..4871692d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs @@ -1,7 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Control.SuggestBox; using Snap.Hutao.Model.Calculable; using Snap.Hutao.Model.Metadata.Abstraction; using Snap.Hutao.Model.Metadata.Converter; @@ -15,7 +14,7 @@ namespace Snap.Hutao.Model.Metadata.Avatar; /// /// 角色的接口实现部分 /// -internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource, ITokenizable +internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource { /// /// [非元数据] 搭配数据 @@ -97,9 +96,4 @@ internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IItem IsUp = isUp, }; } - - public ISearchToken Tokenize() - { - return new SearchToken(Name, AvatarSideIconConverter.IconNameToUri(SideIcon)); - } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs index f677d19b..86a4465c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs @@ -1,7 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Control.SuggestBox; using Snap.Hutao.Model.Calculable; using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Model.Metadata.Abstraction; @@ -15,7 +14,7 @@ namespace Snap.Hutao.Model.Metadata.Weapon; /// /// 武器的接口实现 /// -internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource, ITokenizable +internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource { /// /// [非元数据] 搭配数据 @@ -105,9 +104,4 @@ internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource IsUp = isUp, }; } - - public ISearchToken Tokenize() - { - return new SearchToken(Name, EquipIconConverter.IconNameToUri(Icon)); - } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml index af3cf59f..32660930 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml @@ -312,12 +312,13 @@ Margin="6,-2,6,6" HorizontalAlignment="Stretch" VerticalContentAlignment="Center" + AvailableTokens="{Binding AvailableTokens}" FilterCommand="{Binding FilterCommand}" - ITokenizableItemsSource="{Binding Avatars.SourceCollection}" ItemsSource="{Binding FilterTokens, Mode=TwoWay}" MaximumTokens="5" PlaceholderText="{shcm:ResourceString Name=ViewPageWiKiAvatarAutoSuggestBoxPlaceHolder}" QueryIcon="{cw:FontIconSource Glyph=}" + SuggestedItemTemplate="{StaticResource TokenTemplate}" SuggestedItemsSource="{Binding AvailableQueries}" Text="{Binding FilterToken, Mode=TwoWay}" TokenItemTemplate="{StaticResource TokenTemplate}"/> diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml index 4db320fc..84f3f061 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml @@ -179,12 +179,13 @@ Margin="6,-2,6,6" HorizontalAlignment="Stretch" VerticalContentAlignment="Center" + AvailableTokens="{Binding AvailableTokens}" FilterCommand="{Binding FilterCommand}" - ITokenizableItemsSource="{Binding Weapons.SourceCollection}" ItemsSource="{Binding FilterTokens, Mode=TwoWay}" MaximumTokens="5" PlaceholderText="{shcm:ResourceString Name=ViewPageWiKiWeaponAutoSuggestBoxPlaceHolder}" QueryIcon="{cw:FontIconSource Glyph=}" + SuggestedItemTemplate="{StaticResource TokenTemplate}" SuggestedItemsSource="{Binding AvailableQueries}" Text="{Binding FilterToken, Mode=TwoWay}" TokenItemTemplate="{StaticResource TokenTemplate}"/> diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/AvatarFilter.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/AvatarFilter.cs index c18c963c..177b0e37 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/AvatarFilter.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/AvatarFilter.cs @@ -66,9 +66,11 @@ internal static class AvatarFilter } break; - case SearchTokenKind.Other: + case SearchTokenKind.Avatars: matches.Add(tokens.Contains(avatar.Name)); break; + default: + throw Must.NeverHappen(); } } diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs index 99fdd8a0..08308501 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs @@ -52,9 +52,11 @@ internal static class WeaponFilter } break; - case SearchTokenKind.Other: + case SearchTokenKind.Weapons: matches.Add(tokens.Contains(weapon.Name)); break; + default: + throw Must.NeverHappen(); } } diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs index 4c55dca1..74a2c3b2 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs @@ -10,6 +10,7 @@ using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Model.Intrinsic.Frozen; using Snap.Hutao.Model.Metadata; using Snap.Hutao.Model.Metadata.Avatar; +using Snap.Hutao.Model.Metadata.Converter; using Snap.Hutao.Model.Metadata.Item; using Snap.Hutao.Model.Primitive; using Snap.Hutao.Service.Cultivation; @@ -54,7 +55,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel private BaseValueInfo? baseValueInfo; private Dictionary>? levelAvatarCurveMap; private List? promotes; - private FrozenSet availableQueries; + private FrozenDictionary availableTokens; /// /// 角色列表 @@ -87,7 +88,9 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel public string? FilterToken { get => filterToken; set => SetProperty(ref filterToken, value); } - public FrozenSet? AvailableQueries { get => availableQueries; } + public FrozenDictionary? AvailableTokens { get => availableTokens; } + + public IEnumerable? AvailableQueries { get => availableTokens.Values; } protected override async ValueTask InitializeUIAsync() { @@ -113,15 +116,15 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel Selected = Avatars.View.ElementAtOrDefault(0); FilterTokens = []; - availableQueries = FrozenSet.ToFrozenSet( - [ - .. avatars.Select(a => a.Name), - .. IntrinsicFrozen.AssociationTypes, - .. IntrinsicFrozen.BodyTypes, - .. IntrinsicFrozen.ElementNames, - .. IntrinsicFrozen.ItemQualities, - .. IntrinsicFrozen.WeaponTypes, - ]); + availableTokens = FrozenDictionary.ToFrozenDictionary( + [ + .. avatars.Select(avatar => KeyValuePair.Create(avatar.Name, new SearchToken(avatar.Name, SearchTokenKind.Avatars, sideIconUri: AvatarSideIconConverter.IconNameToUri(avatar.SideIcon)))), + .. IntrinsicFrozen.AssociationTypes.Select(assoc => KeyValuePair.Create(assoc, new SearchToken(assoc, SearchTokenKind.AssociationTypes, iconUri: AssociationTypeIconConverter.AssociationTypeNameToIconUri(assoc)))), + .. IntrinsicFrozen.BodyTypes.Select(b => KeyValuePair.Create(b, new SearchToken(b, SearchTokenKind.BodyTypes))), + .. IntrinsicFrozen.ElementNames.Select(e => KeyValuePair.Create(e, new SearchToken(e, SearchTokenKind.ElementNames, iconUri: ElementNameIconConverter.ElementNameToIconUri(e)))), + .. IntrinsicFrozen.ItemQualities.Select(i => KeyValuePair.Create(i, new SearchToken(i, SearchTokenKind.ItemQualities, quality: QualityColorConverter.QualityNameToColor(i)))), + .. IntrinsicFrozen.WeaponTypes.Select(w => KeyValuePair.Create(w, new SearchToken(w, SearchTokenKind.WeaponTypes, iconUri: WeaponTypeIconConverter.WeaponTypeNameToIconUri(w)))), + ]); return true; } diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs index 05dfbb1c..2e44caa3 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs @@ -9,6 +9,7 @@ using Snap.Hutao.Model.Entity.Primitive; using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Model.Intrinsic.Frozen; using Snap.Hutao.Model.Metadata; +using Snap.Hutao.Model.Metadata.Converter; using Snap.Hutao.Model.Metadata.Item; using Snap.Hutao.Model.Metadata.Weapon; using Snap.Hutao.Model.Primitive; @@ -51,7 +52,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel private BaseValueInfo? baseValueInfo; private Dictionary>? levelWeaponCurveMap; private List? promotes; - private FrozenSet availableQueries; + private FrozenDictionary availableTokens; /// /// 角色列表 @@ -84,7 +85,9 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel public string? FilterToken { get => filterToken; set => SetProperty(ref filterToken, value); } - public FrozenSet AvailableQueries { get => availableQueries; } + public FrozenDictionary? AvailableTokens { get => availableTokens; } + + public IEnumerable? AvailableQueries { get => availableTokens.Values; } /// protected override async Task OpenUIAsync() @@ -110,13 +113,13 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel Selected = Weapons.View.ElementAtOrDefault(0); FilterTokens = []; - availableQueries = FrozenSet.ToFrozenSet( - [ - .. weapons.Select(w => w.Name), - .. IntrinsicFrozen.ItemQualities, - .. IntrinsicFrozen.FightProperties, - .. IntrinsicFrozen.WeaponTypes, - ]); + availableTokens = FrozenDictionary.ToFrozenDictionary( + [ + .. weapons.Select(w => KeyValuePair.Create(w.Name, new SearchToken(w.Name, SearchTokenKind.Weapons, sideIconUri: EquipIconConverter.IconNameToUri(w.Icon)))), + .. IntrinsicFrozen.FightProperties.Select(f => KeyValuePair.Create(f, new SearchToken(f, SearchTokenKind.FightProperties))), + .. IntrinsicFrozen.ItemQualities.Select(i => KeyValuePair.Create(i, new SearchToken(i, SearchTokenKind.ItemQualities, quality: QualityColorConverter.QualityNameToColor(i)))), + .. IntrinsicFrozen.WeaponTypes.Select(w => KeyValuePair.Create(w, new SearchToken(w, SearchTokenKind.WeaponTypes, iconUri: WeaponTypeIconConverter.WeaponTypeNameToIconUri(w)))), + ]); } }