mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
support or in wiki search
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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=}"
|
||||
SuggestedItemTemplate="{StaticResource SuggestionTemplate}"
|
||||
SuggestedItemsSource="{Binding AvailableQueries}"
|
||||
Text="{Binding FilterToken, Mode=TwoWay}"
|
||||
TokenItemTemplate="{StaticResource SuggestionTemplate}"/>
|
||||
TokenItemTemplate="{StaticResource TokenTemplate}"/>
|
||||
</AppBarElementContainer>
|
||||
<AppBarButton
|
||||
Command="{Binding CultivateCommand}"
|
||||
|
||||
@@ -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=}"
|
||||
SuggestedItemTemplate="{StaticResource SuggestionTemplate}"
|
||||
SuggestedItemsSource="{Binding AvailableQueries}"
|
||||
Text="{Binding FilterToken, Mode=TwoWay}"
|
||||
TokenItemTemplate="{StaticResource SuggestionTemplate}"/>
|
||||
TokenItemTemplate="{StaticResource TokenTemplate}"/>
|
||||
</AppBarElementContainer>
|
||||
<AppBarButton
|
||||
Command="{Binding CultivateCommand}"
|
||||
|
||||
@@ -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);
|
||||
|
||||
118
src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/SearchToken.cs
Normal file
118
src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/SearchToken.cs
Normal 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,
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user