support or in wiki search

This commit is contained in:
qhy040404
2024-03-03 21:03:28 +08:00
parent 9619835cc2
commit e35c03ac90
10 changed files with 411 additions and 82 deletions

View File

@@ -0,0 +1,54 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Control;
using Snap.Hutao.Model.Intrinsic;
namespace Snap.Hutao.Model.Metadata.Converter;
internal sealed class AssociationTypeIconConverter : ValueConverter<AssociationType, Uri?>
{
private static readonly Dictionary<string, AssociationType> LocalizedNameToAssociationType = new()
{
[SH.ModelIntrinsicAssociationTypeMondstadt] = AssociationType.ASSOC_TYPE_MONDSTADT,
[SH.ModelIntrinsicAssociationTypeLiyue] = AssociationType.ASSOC_TYPE_LIYUE,
[SH.ModelIntrinsicAssociationTypeFatui] = AssociationType.ASSOC_TYPE_FATUI,
[SH.ModelIntrinsicAssociationTypeInazuma] = AssociationType.ASSOC_TYPE_INAZUMA,
[SH.ModelIntrinsicAssociationTypeRanger] = AssociationType.ASSOC_TYPE_RANGER,
[SH.ModelIntrinsicAssociationTypeSumeru] = AssociationType.ASSOC_TYPE_SUMERU,
[SH.ModelIntrinsicAssociationTypeFontaine] = AssociationType.ASSOC_TYPE_FONTAINE,
[SH.ModelIntrinsicAssociationTypeNatlan] = AssociationType.ASSOC_TYPE_NATLAN,
[SH.ModelIntrinsicAssociationTypeSnezhnaya] = AssociationType.ASSOC_TYPE_SNEZHNAYA,
};
public static Uri? AssociationTypeNameToIconUri(string associationTypeName)
{
return AssociationTypeToIconUri(LocalizedNameToAssociationType.GetValueOrDefault(associationTypeName));
}
public static Uri? AssociationTypeToIconUri(AssociationType type)
{
string? association = type switch
{
AssociationType.ASSOC_TYPE_MONDSTADT => "Mengde",
AssociationType.ASSOC_TYPE_LIYUE => "Liyue",
AssociationType.ASSOC_TYPE_FATUI => null,
AssociationType.ASSOC_TYPE_INAZUMA => "Inazuma",
AssociationType.ASSOC_TYPE_RANGER => null,
AssociationType.ASSOC_TYPE_SUMERU => "Sumeru",
AssociationType.ASSOC_TYPE_FONTAINE => "Fontaine",
AssociationType.ASSOC_TYPE_NATLAN => null,
AssociationType.ASSOC_TYPE_SNEZHNAYA => null,
_ => throw Must.NeverHappen(),
};
return association is null
? null
: Web.HutaoEndpoints.StaticRaw("ChapterIcon", $"UI_ChapterIcon_{association}.png").ToUri();
}
public override Uri? Convert(AssociationType from)
{
return AssociationTypeToIconUri(from);
}
}

View File

@@ -15,10 +15,24 @@ namespace Snap.Hutao.Model.Metadata.Converter;
[HighQuality]
internal sealed class QualityColorConverter : ValueConverter<QualityType, Color>
{
/// <inheritdoc/>
public override Color Convert(QualityType from)
private static readonly Dictionary<string, QualityType> LocalizedNameToQualityType = new()
{
return from switch
[SH.ModelIntrinsicItemQualityWhite] = QualityType.QUALITY_WHITE,
[SH.ModelIntrinsicItemQualityGreen] = QualityType.QUALITY_GREEN,
[SH.ModelIntrinsicItemQualityBlue] = QualityType.QUALITY_BLUE,
[SH.ModelIntrinsicItemQualityPurple] = QualityType.QUALITY_PURPLE,
[SH.ModelIntrinsicItemQualityOrange] = QualityType.QUALITY_ORANGE,
[SH.ModelIntrinsicItemQualityRed] = QualityType.QUALITY_ORANGE_SP,
};
public static Color QualityNameToColor(string qualityName)
{
return QualityToColor(LocalizedNameToQualityType.GetValueOrDefault(qualityName));
}
public static Color QualityToColor(QualityType quality)
{
return quality switch
{
QualityType.QUALITY_WHITE => StructMarshal.Color(0xFF72778B),
QualityType.QUALITY_GREEN => StructMarshal.Color(0xFF2A8F72),
@@ -28,4 +42,10 @@ internal sealed class QualityColorConverter : ValueConverter<QualityType, Color>
_ => Colors.Transparent,
};
}
/// <inheritdoc/>
public override Color Convert(QualityType from)
{
return QualityToColor(from);
}
}

View File

@@ -12,6 +12,20 @@ namespace Snap.Hutao.Model.Metadata.Converter;
[HighQuality]
internal sealed class WeaponTypeIconConverter : ValueConverter<WeaponType, Uri>
{
private static readonly Dictionary<string, WeaponType> LocalizedNameToWeaponType = new()
{
[SH.ModelIntrinsicWeaponTypeSwordOneHand] = WeaponType.WEAPON_SWORD_ONE_HAND,
[SH.ModelIntrinsicWeaponTypeBow] = WeaponType.WEAPON_BOW,
[SH.ModelIntrinsicWeaponTypePole] = WeaponType.WEAPON_POLE,
[SH.ModelIntrinsicWeaponTypeClaymore] = WeaponType.WEAPON_CLAYMORE,
[SH.ModelIntrinsicWeaponTypeCatalyst] = WeaponType.WEAPON_CATALYST,
};
public static Uri WeaponTypeNameToIconUri(string weaponTypeName)
{
return WeaponTypeToIconUri(LocalizedNameToWeaponType.GetValueOrDefault(weaponTypeName));
}
/// <summary>
/// 将武器类型转换为图标链接
/// </summary>

View File

@@ -72,8 +72,45 @@
</shct:DescriptionTextBlock>
</DataTemplate>
<DataTemplate x:Key="SuggestionTemplate">
<TextBlock VerticalAlignment="Center" Text="{Binding}"/>
<DataTemplate x:Key="TokenTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle
Grid.Column="0"
Width="4"
Height="22"
Margin="0,0,8,0"
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
RadiusX="2"
RadiusY="2"
Visibility="{Binding Quality, Converter={StaticResource EmptyObjectToVisibilityConverter}}">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding Quality}"/>
</Rectangle.Fill>
</Rectangle>
<shci:MonoChrome
Grid.Column="0"
Width="22"
Height="22"
Margin="0,0,4,0"
Source="{Binding IconUri, Mode=OneWay}"
Visibility="{Binding IconUri, Converter={StaticResource EmptyObjectToVisibilityConverter}}"/>
<shci:CachedImage
Grid.Column="0"
Width="22"
Height="22"
Margin="0,0,4,0"
Source="{Binding SideIconUri, Mode=OneWay}"
Visibility="{Binding SideIconUri, Converter={StaticResource EmptyObjectToVisibilityConverter}}"/>
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
Text="{Binding Value}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="AvatarListTemplate">
@@ -270,18 +307,18 @@
<AppBarElementContainer>
<cwc:TokenizingTextBox
x:Name="AvatarSuggestBox"
Width="520"
Margin="6,0,6,0"
Width="600"
Height="44"
Margin="6,-2,6,6"
HorizontalAlignment="Stretch"
VerticalContentAlignment="Center"
ItemsSource="{Binding FilterTokens, Mode=TwoWay}"
MaximumTokens="5"
PlaceholderText="{shcm:ResourceString Name=ViewPageWiKiAvatarAutoSuggestBoxPlaceHolder}"
QueryIcon="{cw:FontIconSource Glyph=&#xE721;}"
SuggestedItemTemplate="{StaticResource SuggestionTemplate}"
SuggestedItemsSource="{Binding AvailableQueries}"
Text="{Binding FilterToken, Mode=TwoWay}"
TokenItemTemplate="{StaticResource SuggestionTemplate}"/>
TokenItemTemplate="{StaticResource TokenTemplate}"/>
</AppBarElementContainer>
<AppBarButton
Command="{Binding CultivateCommand}"

View File

@@ -32,8 +32,45 @@
Source="{Binding Converter={StaticResource PropertyDescriptor}}"/>
</DataTemplate>
<DataTemplate x:Key="SuggestionTemplate">
<TextBlock VerticalAlignment="Center" Text="{Binding}"/>
<DataTemplate x:Key="TokenTemplate">
<Grid VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle
Grid.Column="0"
Width="4"
Height="22"
Margin="0,0,8,0"
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
RadiusX="2"
RadiusY="2"
Visibility="{Binding Quality, Converter={StaticResource EmptyObjectToVisibilityConverter}}">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding Quality}"/>
</Rectangle.Fill>
</Rectangle>
<shci:MonoChrome
Grid.Column="0"
Width="22"
Height="22"
Margin="0,0,4,0"
Source="{Binding IconUri, Mode=OneWay}"
Visibility="{Binding IconUri, Converter={StaticResource EmptyObjectToVisibilityConverter}}"/>
<shci:CachedImage
Grid.Column="0"
Width="22"
Height="22"
Margin="0,0,4,0"
Source="{Binding SideIconUri, Mode=OneWay}"
Visibility="{Binding SideIconUri, Converter={StaticResource EmptyObjectToVisibilityConverter}}"/>
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
Text="{Binding Value}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="WeaponListTemplate">
@@ -137,19 +174,18 @@
<AppBarElementContainer>
<cwc:TokenizingTextBox
x:Name="WeaponSuggestBox"
Width="520"
Margin="16,0,6,0"
Width="600"
Height="44"
Margin="6,-2,6,6"
HorizontalAlignment="Stretch"
VerticalContentAlignment="Center"
ItemTemplate="{StaticResource SuggestionTemplate}"
ItemsSource="{Binding FilterTokens, Mode=TwoWay}"
MaximumTokens="5"
PlaceholderText="{shcm:ResourceString Name=ViewPageWiKiWeaponAutoSuggestBoxPlaceHolder}"
QueryIcon="{cw:FontIconSource Glyph=&#xE721;}"
SuggestedItemTemplate="{StaticResource SuggestionTemplate}"
SuggestedItemsSource="{Binding AvailableQueries}"
Text="{Binding FilterToken, Mode=TwoWay}"
TokenItemTemplate="{StaticResource SuggestionTemplate}"/>
TokenItemTemplate="{StaticResource TokenTemplate}"/>
</AppBarElementContainer>
<AppBarButton
Command="{Binding CultivateCommand}"

View File

@@ -1,9 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.Extensions.Primitives;
using Snap.Hutao.Model.Intrinsic.Frozen;
using Snap.Hutao.Model.Metadata.Avatar;
using System.Collections.ObjectModel;
namespace Snap.Hutao.ViewModel.Wiki;
@@ -17,55 +17,58 @@ internal static class AvatarFilter
/// </summary>
/// <param name="input">输入</param>
/// <returns>筛选操作</returns>
public static Predicate<Avatar> Compile(string input)
public static Predicate<Avatar> Compile(ObservableCollection<SearchToken> input)
{
return (Avatar avatar) => DoFilter(input, avatar);
}
private static bool DoFilter(string input, Avatar avatar)
private static bool DoFilter(ObservableCollection<SearchToken> input, Avatar avatar)
{
List<bool> matches = [];
foreach (StringSegment segment in new StringTokenizer(input, [' ']))
foreach (IGrouping<SearchTokenKind, string> tokens in input.GroupBy(token => token.Kind, token => token.Value))
{
string value = segment.ToString();
if (avatar.Name == value)
switch (tokens.Key)
{
matches.Add(true);
continue;
}
case SearchTokenKind.ElementNames:
if (IntrinsicFrozen.ElementNames.Overlaps(tokens))
{
matches.Add(tokens.Contains(avatar.FetterInfo.VisionBefore));
}
if (IntrinsicFrozen.ElementNames.Contains(value))
{
matches.Add(avatar.FetterInfo.VisionBefore == value);
continue;
}
break;
case SearchTokenKind.AssociationTypes:
if (IntrinsicFrozen.AssociationTypes.Overlaps(tokens))
{
matches.Add(tokens.Contains(avatar.FetterInfo.Association.GetLocalizedDescriptionOrDefault()));
}
if (IntrinsicFrozen.AssociationTypes.Contains(value))
{
matches.Add(avatar.FetterInfo.Association.GetLocalizedDescriptionOrDefault() == value);
continue;
}
break;
case SearchTokenKind.WeaponTypes:
if (IntrinsicFrozen.WeaponTypes.Overlaps(tokens))
{
matches.Add(tokens.Contains(avatar.Weapon.GetLocalizedDescriptionOrDefault()));
}
if (IntrinsicFrozen.WeaponTypes.Contains(value))
{
matches.Add(avatar.Weapon.GetLocalizedDescriptionOrDefault() == value);
continue;
}
break;
case SearchTokenKind.ItemQualities:
if (IntrinsicFrozen.ItemQualities.Overlaps(tokens))
{
matches.Add(tokens.Contains(avatar.Quality.GetLocalizedDescriptionOrDefault()));
}
if (IntrinsicFrozen.ItemQualities.Contains(value))
{
matches.Add(avatar.Quality.GetLocalizedDescriptionOrDefault() == value);
continue;
}
break;
case SearchTokenKind.BodyTypes:
if (IntrinsicFrozen.BodyTypes.Overlaps(tokens))
{
matches.Add(tokens.Contains(avatar.Body.GetLocalizedDescriptionOrDefault()));
}
if (IntrinsicFrozen.BodyTypes.Contains(value))
{
matches.Add(avatar.Body.GetLocalizedDescriptionOrDefault() == value);
continue;
break;
case SearchTokenKind.Other:
matches.Add(tokens.Contains(avatar.Name));
break;
}
matches.Add(false);
}
return matches.Count > 0 && matches.Aggregate((a, b) => a && b);

View File

@@ -0,0 +1,118 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic.Frozen;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Model.Metadata.Weapon;
using Windows.UI;
namespace Snap.Hutao.ViewModel.Wiki;
internal class SearchToken
{
private SearchTokenKind kind;
private bool isKindInitialized;
private object isKindInitializedLock = new();
public SearchToken(string value)
{
Value = value;
}
public SearchToken(Avatar avatar)
{
Value = avatar.Name;
SideIconUri = AvatarSideIconConverter.IconNameToUri(avatar.SideIcon);
}
public SearchToken(Weapon weapon)
{
Value = weapon.Name;
SideIconUri = EquipIconConverter.IconNameToUri(weapon.Icon);
}
public string Value { get; }
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;
}
}
}
public override string ToString()
{
return Value;
}
}
[SuppressMessage("", "SA1201")]
internal enum SearchTokenKind
{
AssociationTypes,
BodyTypes,
ElementNames,
FightProperties,
ItemQualities,
Other, // Include avatar and weapon
WeaponTypes,
}

View File

@@ -1,9 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.Extensions.Primitives;
using Snap.Hutao.Model.Intrinsic.Frozen;
using Snap.Hutao.Model.Metadata.Weapon;
using System.Collections.ObjectModel;
namespace Snap.Hutao.ViewModel.Wiki;
@@ -17,41 +17,43 @@ internal static class WeaponFilter
/// </summary>
/// <param name="input">输入</param>
/// <returns>筛选操作</returns>
public static Predicate<Weapon> Compile(string input)
public static Predicate<Weapon> Compile(ObservableCollection<SearchToken> input)
{
return (Weapon weapon) => DoFilter(input, weapon);
}
private static bool DoFilter(string input, Weapon weapon)
private static bool DoFilter(ObservableCollection<SearchToken> input, Weapon weapon)
{
List<bool> matches = [];
foreach (StringSegment segment in new StringTokenizer(input, [' ']))
foreach (IGrouping<SearchTokenKind, string> tokens in input.GroupBy(token => token.Kind, token => token.Value))
{
string value = segment.ToString();
if (weapon.Name == value)
switch (tokens.Key)
{
matches.Add(true);
continue;
}
case SearchTokenKind.WeaponTypes:
if (IntrinsicFrozen.WeaponTypes.Overlaps(tokens))
{
matches.Add(tokens.Contains(weapon.WeaponType.GetLocalizedDescriptionOrDefault()));
}
if (IntrinsicFrozen.WeaponTypes.Contains(value))
{
matches.Add(weapon.WeaponType.GetLocalizedDescriptionOrDefault() == value);
continue;
}
break;
case SearchTokenKind.ItemQualities:
if (IntrinsicFrozen.ItemQualities.Overlaps(tokens))
{
matches.Add(tokens.Contains(weapon.Quality.GetLocalizedDescriptionOrDefault()));
}
if (IntrinsicFrozen.ItemQualities.Contains(value))
{
matches.Add(weapon.Quality.GetLocalizedDescriptionOrDefault() == value);
continue;
}
break;
case SearchTokenKind.FightProperties:
if (IntrinsicFrozen.FightProperties.Overlaps(tokens))
{
matches.Add(tokens.Contains(weapon.GrowCurves.ElementAtOrDefault(1)?.Type.GetLocalizedDescriptionOrDefault()));
}
if (IntrinsicFrozen.FightProperties.Contains(value))
{
matches.Add(weapon.GrowCurves.ElementAtOrDefault(1)?.Type.GetLocalizedDescriptionOrDefault() == value);
continue;
break;
case SearchTokenKind.Other:
matches.Add(tokens.Contains(weapon.Name));
break;
}
}

View File

@@ -50,7 +50,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel, IWiki
private AdvancedCollectionView<Avatar>? avatars;
private Avatar? selected;
private ObservableCollection<string>? filterTokens;
private ObservableCollection<SearchToken>? filterTokens;
private string? filterToken;
private BaseValueInfo? baseValueInfo;
private Dictionary<Level, Dictionary<GrowCurveType, float>>? levelAvatarCurveMap;
@@ -84,7 +84,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel, IWiki
/// <summary>
/// 保存的筛选标志
/// </summary>
public ObservableCollection<string>? FilterTokens { get => filterTokens; set => SetProperty(ref filterTokens, value); }
public ObservableCollection<SearchToken>? FilterTokens { get => filterTokens; set => SetProperty(ref filterTokens, value); }
public string? FilterToken { get => filterToken; set => SetProperty(ref filterToken, value); }
@@ -94,6 +94,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel, IWiki
{
accessor.TokenizingTextBox.TextChanged += OnFilterSuggestionRequested;
accessor.TokenizingTextBox.QuerySubmitted += OnQuerySubmitted;
accessor.TokenizingTextBox.TokenItemAdding += OnTokenItemAdding;
accessor.TokenizingTextBox.TokenItemAdded += OnTokenItemModified;
accessor.TokenizingTextBox.TokenItemRemoved += OnTokenItemModified;
}
@@ -261,6 +262,27 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel, IWiki
ApplyFilter();
}
private void OnTokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
{
if (string.IsNullOrWhiteSpace(args.TokenText))
{
return;
}
if (Avatars is null)
{
return;
}
if (Avatars.SourceCollection.SingleOrDefault(a => a.Name == args.TokenText) is { } avatar)
{
args.Item = new SearchToken(avatar);
return;
}
args.Item = new SearchToken(args.TokenText);
}
private void OnTokenItemModified(TokenizingTextBox sender, object args)
{
ApplyFilter();
@@ -279,7 +301,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel, IWiki
return;
}
Avatars.Filter = AvatarFilter.Compile(string.Join(' ', FilterTokens));
Avatars.Filter = AvatarFilter.Compile(FilterTokens);
if (Selected is not null && Avatars.Contains(Selected))
{

View File

@@ -21,6 +21,7 @@ using Snap.Hutao.Service.User;
using Snap.Hutao.View.Dialog;
using Snap.Hutao.Web.Response;
using System.Collections.Frozen;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using CalculateAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta;
using CalculateClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient;
@@ -46,7 +47,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel, IWiki
private AdvancedCollectionView<Weapon>? weapons;
private Weapon? selected;
private List<string>? filterTokens;
private ObservableCollection<SearchToken>? filterTokens;
private string? filterToken;
private BaseValueInfo? baseValueInfo;
private Dictionary<Level, Dictionary<GrowCurveType, float>>? levelWeaponCurveMap;
@@ -80,7 +81,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel, IWiki
/// <summary>
/// 保存的筛选标志
/// </summary>
public List<string>? FilterTokens { get => filterTokens; set => SetProperty(ref filterTokens, value); }
public ObservableCollection<SearchToken>? FilterTokens { get => filterTokens; set => SetProperty(ref filterTokens, value); }
public string? FilterToken { get => filterToken; set => SetProperty(ref filterToken, value); }
@@ -90,6 +91,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel, IWiki
{
accessor.TokenizingTextBox.TextChanged += OnFilterSuggestionRequested;
accessor.TokenizingTextBox.QuerySubmitted += OnQuerySubmitted;
accessor.TokenizingTextBox.TokenItemAdding += OnTokenItemAdding;
accessor.TokenizingTextBox.TokenItemAdded += OnTokenItemModified;
accessor.TokenizingTextBox.TokenItemRemoved += OnTokenItemModified;
}
@@ -242,6 +244,27 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel, IWiki
ApplyFilter();
}
private void OnTokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
{
if (string.IsNullOrWhiteSpace(args.TokenText))
{
return;
}
if (Weapons is null)
{
return;
}
if (Weapons.SourceCollection.SingleOrDefault(w => w.Name == args.TokenText) is { } weapon)
{
args.Item = new SearchToken(weapon);
return;
}
args.Item = new SearchToken(args.TokenText);
}
private void OnTokenItemModified(TokenizingTextBox sender, object args)
{
ApplyFilter();
@@ -260,7 +283,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel, IWiki
return;
}
Weapons.Filter = WeaponFilter.Compile(string.Join(' ', FilterTokens));
Weapons.Filter = WeaponFilter.Compile(FilterTokens);
if (Selected is not null && Weapons.Contains(Selected))
{