mirror of
https://github.com/wangdage12/Snap.Hutao.git
synced 2026-07-31 04:10:25 +08:00
右键显示缺项带图片
This commit is contained in:
@@ -352,7 +352,7 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
entryByInnerId[e.InnerId] = e;
|
||||
}
|
||||
|
||||
Dictionary<uint, List<string>> unfinishedSegmentsByMaterial = [];
|
||||
Dictionary<uint, List<(string SortKey, StatisticsConsumerMenuLine Line)>> unfinishedRowsByMaterial = [];
|
||||
|
||||
foreach ((CultivateEntry entry, CultivateItem item) in pairs.AsSpan())
|
||||
{
|
||||
@@ -361,33 +361,75 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
continue;
|
||||
}
|
||||
|
||||
string name = FormatStatisticsConsumerEntryName(entry, context, entryByInnerId);
|
||||
string segment = $"{name}×{item.Count}";
|
||||
ref List<string>? list = ref CollectionsMarshal.GetValueRefOrAddDefault(unfinishedSegmentsByMaterial, item.ItemId, out _);
|
||||
string sortKey = $"{FormatStatisticsConsumerEntryName(entry, context, entryByInnerId)}×{item.Count}";
|
||||
StatisticsConsumerMenuLine line = CreateStatisticsConsumerMenuLine(entry, item, context, entryByInnerId);
|
||||
ref List<(string SortKey, StatisticsConsumerMenuLine Line)>? list = ref CollectionsMarshal.GetValueRefOrAddDefault(unfinishedRowsByMaterial, item.ItemId, out _);
|
||||
list ??= [];
|
||||
list.Add(segment);
|
||||
list.Add((sortKey, line));
|
||||
}
|
||||
|
||||
foreach ((uint materialId, StatisticsCultivateItem stat) in resultItems)
|
||||
{
|
||||
if (MaterialIds.IsExcludedFromStatisticsConsumerMenu(materialId))
|
||||
{
|
||||
stat.StatisticsConsumerMenuLines = ImmutableArray.Create(SH.ViewPageCultivationStatisticsConsumerMenuExcluded);
|
||||
stat.StatisticsConsumerMenuLines = ImmutableArray.Create(StatisticsConsumerMenuLine.Plain(SH.ViewPageCultivationStatisticsConsumerMenuExcluded));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unfinishedSegmentsByMaterial.TryGetValue(materialId, out List<string>? segments))
|
||||
if (unfinishedRowsByMaterial.TryGetValue(materialId, out List<(string SortKey, StatisticsConsumerMenuLine Line)>? rows))
|
||||
{
|
||||
segments.Sort(StringComparer.Ordinal);
|
||||
stat.StatisticsConsumerMenuLines = ImmutableArray.CreateRange(segments);
|
||||
rows.Sort(static (a, b) => StringComparer.Ordinal.Compare(a.SortKey, b.SortKey));
|
||||
stat.StatisticsConsumerMenuLines = ImmutableArray.CreateRange(rows.ConvertAll(static r => r.Line));
|
||||
}
|
||||
else
|
||||
{
|
||||
stat.StatisticsConsumerMenuLines = ImmutableArray.Create(SH.ViewPageCultivationStatisticsUnfinishedConsumersEmptyList);
|
||||
stat.StatisticsConsumerMenuLines = ImmutableArray.Create(StatisticsConsumerMenuLine.Plain(SH.ViewPageCultivationStatisticsUnfinishedConsumersEmptyList));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static StatisticsConsumerMenuLine CreateStatisticsConsumerMenuLine(
|
||||
CultivateEntry entry,
|
||||
CultivateItem item,
|
||||
ICultivationMetadataContext context,
|
||||
Dictionary<Guid, CultivateEntry> entryByInnerId)
|
||||
{
|
||||
string countSuffix = $"×{item.Count}";
|
||||
|
||||
switch (entry.Type)
|
||||
{
|
||||
case CultivateType.AvatarAndSkill:
|
||||
{
|
||||
ModelItem avatarItem = context.GetAvatarItem(entry.Id);
|
||||
return StatisticsConsumerMenuLine.SingleIcon(avatarItem.Icon, avatarItem.Quality, avatarItem.Name, countSuffix);
|
||||
}
|
||||
|
||||
case CultivateType.Weapon:
|
||||
{
|
||||
ModelItem weaponItem = context.GetWeaponItem(entry.Id);
|
||||
if (entry.RelatedEntryId is Guid relatedId
|
||||
&& entryByInnerId.TryGetValue(relatedId, out CultivateEntry? related)
|
||||
&& related.Type is CultivateType.AvatarAndSkill)
|
||||
{
|
||||
ModelItem avatarItem = context.GetAvatarItem(related.Id);
|
||||
return StatisticsConsumerMenuLine.AvatarAndWeapon(
|
||||
avatarItem.Icon,
|
||||
avatarItem.Quality,
|
||||
avatarItem.Name,
|
||||
weaponItem.Icon,
|
||||
weaponItem.Quality,
|
||||
weaponItem.Name,
|
||||
countSuffix);
|
||||
}
|
||||
|
||||
return StatisticsConsumerMenuLine.SingleIcon(weaponItem.Icon, weaponItem.Quality, weaponItem.Name, countSuffix);
|
||||
}
|
||||
|
||||
default:
|
||||
return StatisticsConsumerMenuLine.Plain($"{Material.Default.Name}{countSuffix}");
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatStatisticsConsumerEntryName(
|
||||
CultivateEntry entry,
|
||||
ICultivationMetadataContext context,
|
||||
|
||||
@@ -238,12 +238,52 @@
|
||||
<ScrollViewer MaxHeight="320" MinWidth="220" Padding="12,10">
|
||||
<ItemsControl ItemsSource="{Binding StatisticsConsumerMenuLines, Mode=OneWay}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="x:String">
|
||||
<TextBlock
|
||||
Margin="0,3"
|
||||
Style="{ThemeResource BodyTextBlockStyle}"
|
||||
Text="{x:Bind Mode=OneWay}"
|
||||
TextWrapping="WrapWholeWords"/>
|
||||
<DataTemplate x:DataType="shvcu:StatisticsConsumerMenuLine">
|
||||
<Grid Margin="0,3">
|
||||
<TextBlock
|
||||
Style="{ThemeResource BodyTextBlockStyle}"
|
||||
Text="{x:Bind PlainMessage, Mode=OneWay}"
|
||||
TextWrapping="WrapWholeWords"
|
||||
Visibility="{x:Bind IsPlainMessage, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}"/>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Spacing="4"
|
||||
VerticalAlignment="Center"
|
||||
Visibility="{x:Bind ShowRichRow, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<shuxc:ItemIcon
|
||||
Width="28"
|
||||
Height="28"
|
||||
Icon="{x:Bind LeadingIcon, Mode=OneWay}"
|
||||
Quality="{x:Bind LeadingQuality, Mode=OneWay}"/>
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Style="{ThemeResource BodyTextBlockStyle}"
|
||||
Text="{x:Bind FirstName, Mode=OneWay}"/>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Spacing="4"
|
||||
VerticalAlignment="Center"
|
||||
Visibility="{x:Bind HasSecondIcon, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Style="{ThemeResource BodyTextBlockStyle}"
|
||||
Text="{x:Bind BetweenSeparator, Mode=OneWay}"/>
|
||||
<shuxc:ItemIcon
|
||||
Width="28"
|
||||
Height="28"
|
||||
Icon="{x:Bind SecondIcon, Mode=OneWay}"
|
||||
Quality="{x:Bind SecondQuality, Mode=OneWay}"/>
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Style="{ThemeResource BodyTextBlockStyle}"
|
||||
Text="{x:Bind SecondName, Mode=OneWay}"/>
|
||||
</StackPanel>
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Style="{ThemeResource BodyTextBlockStyle}"
|
||||
Text="{x:Bind CountSuffix, Mode=OneWay}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
namespace Snap.Hutao.ViewModel.Cultivation;
|
||||
|
||||
/// <summary>
|
||||
/// 材料统计右键浮层中一行「未完成」养成条目:名称前展示角色/武器图标。
|
||||
/// </summary>
|
||||
internal sealed class StatisticsConsumerMenuLine
|
||||
{
|
||||
private StatisticsConsumerMenuLine()
|
||||
{
|
||||
}
|
||||
|
||||
public bool IsPlainMessage { get; private init; }
|
||||
|
||||
public string? PlainMessage { get; private init; }
|
||||
|
||||
public bool ShowRichRow => !IsPlainMessage;
|
||||
|
||||
public Uri LeadingIcon { get; private init; } = default!;
|
||||
|
||||
public QualityType LeadingQuality { get; private init; }
|
||||
|
||||
public bool HasSecondIcon { get; private init; }
|
||||
|
||||
public Uri SecondIcon { get; private init; } = default!;
|
||||
|
||||
public QualityType SecondQuality { get; private init; }
|
||||
|
||||
public string FirstName { get; private init; } = string.Empty;
|
||||
|
||||
/// <summary>双名称行中间分隔(与武器关联条目的「角色·武器」展示一致,使用间隔号)。</summary>
|
||||
public string BetweenSeparator { get; private init; } = "\u00B7";
|
||||
|
||||
public string? SecondName { get; private init; }
|
||||
|
||||
public string CountSuffix { get; private init; } = string.Empty;
|
||||
|
||||
public static StatisticsConsumerMenuLine Plain(string message)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
IsPlainMessage = true,
|
||||
PlainMessage = message,
|
||||
};
|
||||
}
|
||||
|
||||
public static StatisticsConsumerMenuLine SingleIcon(Uri icon, QualityType quality, string name, string countSuffix)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
LeadingIcon = icon,
|
||||
LeadingQuality = quality,
|
||||
FirstName = name,
|
||||
CountSuffix = countSuffix,
|
||||
HasSecondIcon = false,
|
||||
SecondIcon = default!,
|
||||
SecondQuality = QualityType.QUALITY_NONE,
|
||||
};
|
||||
}
|
||||
|
||||
public static StatisticsConsumerMenuLine AvatarAndWeapon(
|
||||
Uri avatarIcon,
|
||||
QualityType avatarQuality,
|
||||
string avatarName,
|
||||
Uri weaponIcon,
|
||||
QualityType weaponQuality,
|
||||
string weaponName,
|
||||
string countSuffix)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
LeadingIcon = avatarIcon,
|
||||
LeadingQuality = avatarQuality,
|
||||
FirstName = avatarName,
|
||||
HasSecondIcon = true,
|
||||
SecondIcon = weaponIcon,
|
||||
SecondQuality = weaponQuality,
|
||||
SecondName = weaponName,
|
||||
CountSuffix = countSuffix,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -68,9 +68,9 @@ internal sealed class StatisticsCultivateItem
|
||||
public bool IsToday { get => Inner.IsItemOfToday(offset, true); }
|
||||
|
||||
/// <summary>
|
||||
/// 材料统计右键浮层中按行展示的「未完成」养成条目(每人一行,<c>名×数量</c>)。
|
||||
/// 材料统计右键浮层中按行展示的「未完成」养成条目(每人一行,名称前为角色/武器图标,<c>名×数量</c>)。
|
||||
/// </summary>
|
||||
public ImmutableArray<string> StatisticsConsumerMenuLines { get; set; }
|
||||
public ImmutableArray<StatisticsConsumerMenuLine> StatisticsConsumerMenuLines { get; set; }
|
||||
|
||||
internal bool ExcludedFromPresentation { get; set; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user