using XFEEDToolBox.Core.Galaxy;
namespace XFEEDToolBox.Controls;
public sealed partial class GalaxyFilterPanel
{
public static readonly DependencyProperty CurrentTradeChoicesProperty=DependencyProperty.Register(nameof(CurrentTradeChoices),typeof(IReadOnlyList<GalaxyCommodityGroup>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty CurrentTradeStatusProperty=DependencyProperty.Register(nameof(CurrentTradeStatus),typeof(string),typeof(GalaxyFilterPanel),new PropertyMetadata("",Changed));
public IReadOnlyList<GalaxyCommodityGroup> CurrentTradeChoices {get=>(IReadOnlyList<GalaxyCommodityGroup>?)GetValue(CurrentTradeChoicesProperty)??[];set=>SetValue(CurrentTradeChoicesProperty,value);}
public string CurrentTradeStatus {get=>(string)GetValue(CurrentTradeStatusProperty);set=>SetValue(CurrentTradeStatusProperty,value);}
private readonly CheckBox currentTrade=new(){Content="显示原当前有效贸易流"};
private readonly ComboBox currentTradeSource=new(){Header="交易者来源",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly TextBox currentTradeSearch=new(){PlaceholderText="查找原缓存中的商品"};
private readonly TextBlock currentTradeStatus=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly StackPanel currentTradeCommodities=new(){Spacing=0};
private readonly Dictionary<string,CheckBox> currentTradeLeaves=[];
private readonly Dictionary<string,(CheckBox Check,GalaxyCommodityGroup Group)> currentTradeParents=[];
private readonly Dictionary<int,CheckBox> currentTradeCategoryVisibility=[];
private readonly Dictionary<string,bool> currentTradeExpanded=[];
private string currentTradeSignature="";
private Expander BuildCurrentTradeControls()
{
foreach(var entry in new[]{("NPC",GalaxyTradeFlowSource.Npc),("玩家",GalaxyTradeFlowSource.Players),("全部",GalaxyTradeFlowSource.All)})
currentTradeSource.Items.Add(new ComboBoxItem{Content=entry.Item1,Tag=entry.Item2});
var content=new StackPanel{Spacing=5};content.Children.Add(currentTrade);content.Children.Add(currentTradeSource);content.Children.Add(currentTradeSearch);
var all=new Button{Content="全部商品"};var none=new Button{Content="清除商品"};var actions=new StackPanel{Orientation=Orientation.Horizontal,Spacing=7};actions.Children.Add(all);actions.Children.Add(none);content.Children.Add(actions);
content.Children.Add(new ScrollViewer{Content=currentTradeCommodities,MaxHeight=180,VerticalScrollBarVisibility=ScrollBarVisibility.Auto});content.Children.Add(currentTradeStatus);
content.Children.Add(new TextBlock{Text="只读取原游戏现有贸易缓存,按原时钟有效期、交易者与商品分类筛选。商品名、端点或坐标未确认时不绘制;不以历史记录补齐。",FontSize=11,TextWrapping=TextWrapping.Wrap});
currentTrade.Checked+=(_,_)=>{if(!updating)Filter=Filter with{ShowCurrentTradeFlows=true};};
currentTrade.Unchecked+=(_,_)=>{if(!updating)Filter=Filter with{ShowCurrentTradeFlows=false};};
currentTradeSource.SelectionChanged+=(_,_)=>{if(!updating&¤tTradeSource.SelectedItem is ComboBoxItem{Tag:GalaxyTradeFlowSource source})Filter=Filter with{CurrentTradeSource=source};};
all.Click+=(_,_)=>Filter=Filter with{CurrentTradeCategories=null,CurrentTradeCommodities=null};none.Click+=(_,_)=>Filter=Filter with{CurrentTradeCategories=new HashSet<int>(),CurrentTradeCommodities=new HashSet<string>()};currentTradeSearch.TextChanged+=(_,_)=>Refresh();
return new(){Header="原当前贸易流",Content=content,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch};
}
private void RefreshCurrentTradeControls(GalaxyMapFilter filter)
{
currentTrade.IsChecked=filter.ShowCurrentTradeFlows;currentTradeSource.SelectedItem=currentTradeSource.Items.OfType<ComboBoxItem>().FirstOrDefault(i=>i.Tag is GalaxyTradeFlowSource source&&source==filter.CurrentTradeSource);
currentTradeSource.IsEnabled=filter.ShowCurrentTradeFlows;currentTradeStatus.Text=CurrentTradeStatus;
var groups=CurrentTradeChoices;var search=currentTradeSearch.Text.Trim();bool Contains(string text)=>text.Contains(search,StringComparison.OrdinalIgnoreCase);
var shown=groups.Select(g=>search.Length==0||Contains(g.Label)?g:g with{Children=g.Children.Where(c=>Contains(c.Label)||Contains(c.Key)).ToArray()}).Where(g=>g.Children.Count>0).ToArray();
var signature=string.Join('|',shown.Select(g=>g.Key+":"+string.Join(',',g.Children.Select(c=>c.Key+"="+c.Label))));
if(signature!=currentTradeSignature)
{
currentTradeSignature=signature;currentTradeCommodities.Children.Clear();currentTradeLeaves.Clear();currentTradeParents.Clear();currentTradeCategoryVisibility.Clear();
foreach(var group in shown)
{
var complete=groups.First(g=>g.Key==group.Key);var children=new StackPanel{Spacing=1};var parent=new CheckBox{Content="全选此类",IsThreeState=true};children.Children.Add(parent);currentTradeParents[group.Key]=(parent,complete);
parent.Click+=(_,_)=>
{
if(updating)return;
var originalCategory=GalaxyOriginalTradeCategories.Categories.FirstOrDefault(c=>group.Key=="originaltrade:"+c.Id);
var targets=originalCategory is null?complete.Children.Select(c=>c.Key):GalaxyOriginalTradeCategories.Commodities.Where(c=>c.CategoryId==originalCategory.Id).Select(c=>GalaxyMapFilter.Normalize(c.Name));
Filter=Filter with{CurrentTradeCommodities=GalaxyCommodityTree.SelectGroup(Filter.CurrentTradeCommodities,CurrentTradeUniverse(),targets,GalaxyCommodityTree.Selected(Filter.CurrentTradeCommodities,complete.Children)!=true)};
};
foreach(var child in group.Children)
{
var checkbox=new CheckBox{Content=child.Label};children.Children.Add(checkbox);currentTradeLeaves[child.Key]=checkbox;
void Select(object sender,RoutedEventArgs args)
{
if(updating)return;
var selected=Filter.CurrentTradeCommodities is {} old?new HashSet<string>(old):CurrentTradeUniverse().ToHashSet(StringComparer.Ordinal);
if(checkbox.IsChecked==true)selected.Add(child.Key);else selected.Remove(child.Key);
Filter=Filter with{CurrentTradeCommodities=selected};
}
checkbox.Checked+=Select;checkbox.Unchecked+=Select;
}
object header=group.Label+" · "+complete.Children.Count;
if(GalaxyOriginalTradeCategories.Categories.FirstOrDefault(c=>group.Key=="originaltrade:"+c.Id) is {} definition)
{
var categorySwitch=new CheckBox{Content=header};header=categorySwitch;currentTradeCategoryVisibility[definition.Id]=categorySwitch;
categorySwitch.Click+=(_,_)=>
{
if(updating)return;
var selection=Filter.CurrentTradeCategories is {} old?new HashSet<int>(old):GalaxyOriginalTradeCategories.Categories.Select(c=>c.Id).ToHashSet();
if(categorySwitch.IsChecked==true)selection.Add(definition.Id);else selection.Remove(definition.Id);
// Original parent visibility is independent of each
// leaf's saved checkbox; switching it off preserves leaves.
Filter=Filter with{CurrentTradeCategories=selection};
};
}
var expanded=new Expander{Header=header,Content=children,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch,IsExpanded=search.Length>0||currentTradeExpanded.GetValueOrDefault(group.Key)};
expanded.Expanding+=(_,_)=>currentTradeExpanded[group.Key]=true;expanded.Collapsed+=(_,_)=>currentTradeExpanded[group.Key]=false;currentTradeCommodities.Children.Add(expanded);
}
}
foreach(var row in currentTradeLeaves){row.Value.IsChecked=filter.CurrentTradeCommodities is null||filter.CurrentTradeCommodities.Contains(row.Key);row.Value.IsEnabled=filter.ShowCurrentTradeFlows;}
foreach(var row in currentTradeParents){row.Value.Check.IsChecked=GalaxyCommodityTree.Selected(filter.CurrentTradeCommodities,row.Value.Group.Children);row.Value.Check.IsEnabled=filter.ShowCurrentTradeFlows;}
foreach(var row in currentTradeCategoryVisibility){row.Value.IsChecked=filter.CurrentTradeCategories is null||filter.CurrentTradeCategories.Contains(row.Key);row.Value.IsEnabled=filter.ShowCurrentTradeFlows;}
}
private IEnumerable<string> CurrentTradeUniverse()=>GalaxyOriginalTradeCategories.Commodities.Select(c=>GalaxyMapFilter.Normalize(c.Name)).Concat(CurrentTradeChoices.SelectMany(g=>g.Children).Select(c=>c.Key)).Distinct(StringComparer.Ordinal);
}
using XFEEDToolBox.Core.Galaxy;
namespace XFEEDToolBox.Controls;
public sealed partial class GalaxyFilterPanel
{
public static readonly DependencyProperty CurrentTradeChoicesProperty=DependencyProperty.Register(nameof(CurrentTradeChoices),typeof(IReadOnlyList<GalaxyCommodityGroup>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty CurrentTradeStatusProperty=DependencyProperty.Register(nameof(CurrentTradeStatus),typeof(string),typeof(GalaxyFilterPanel),new PropertyMetadata("",Changed));
public IReadOnlyList<GalaxyCommodityGroup> CurrentTradeChoices {get=>(IReadOnlyList<GalaxyCommodityGroup>?)GetValue(CurrentTradeChoicesProperty)??[];set=>SetValue(CurrentTradeChoicesProperty,value);}
public string CurrentTradeStatus {get=>(string)GetValue(CurrentTradeStatusProperty);set=>SetValue(CurrentTradeStatusProperty,value);}
private readonly CheckBox currentTrade=new(){Content="显示原当前有效贸易流"};
private readonly ComboBox currentTradeSource=new(){Header="交易者来源",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly TextBox currentTradeSearch=new(){PlaceholderText="查找原缓存中的商品"};
private readonly TextBlock currentTradeStatus=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly StackPanel currentTradeCommodities=new(){Spacing=0};
private readonly Dictionary<string,CheckBox> currentTradeLeaves=[];
private readonly Dictionary<string,(CheckBox Check,GalaxyCommodityGroup Group)> currentTradeParents=[];
private readonly Dictionary<int,CheckBox> currentTradeCategoryVisibility=[];
private readonly Dictionary<string,bool> currentTradeExpanded=[];
private string currentTradeSignature="";
private Expander BuildCurrentTradeControls()
{
foreach(var entry in new[]{("NPC",GalaxyTradeFlowSource.Npc),("玩家",GalaxyTradeFlowSource.Players),("全部",GalaxyTradeFlowSource.All)})
currentTradeSource.Items.Add(new ComboBoxItem{Content=entry.Item1,Tag=entry.Item2});
var content=new StackPanel{Spacing=5};content.Children.Add(currentTrade);content.Children.Add(currentTradeSource);content.Children.Add(currentTradeSearch);
var all=new Button{Content="全部商品"};var none=new Button{Content="清除商品"};var actions=new StackPanel{Orientation=Orientation.Horizontal,Spacing=7};actions.Children.Add(all);actions.Children.Add(none);content.Children.Add(actions);
content.Children.Add(new ScrollViewer{Content=currentTradeCommodities,MaxHeight=180,VerticalScrollBarVisibility=ScrollBarVisibility.Auto});content.Children.Add(currentTradeStatus);
content.Children.Add(new TextBlock{Text="只读取原游戏现有贸易缓存,按原时钟有效期、交易者与商品分类筛选。商品名、端点或坐标未确认时不绘制;不以历史记录补齐。",FontSize=11,TextWrapping=TextWrapping.Wrap});
currentTrade.Checked+=(_,_)=>{if(!updating)Filter=Filter with{ShowCurrentTradeFlows=true};};
currentTrade.Unchecked+=(_,_)=>{if(!updating)Filter=Filter with{ShowCurrentTradeFlows=false};};
currentTradeSource.SelectionChanged+=(_,_)=>{if(!updating&¤tTradeSource.SelectedItem is ComboBoxItem{Tag:GalaxyTradeFlowSource source})Filter=Filter with{CurrentTradeSource=source};};
all.Click+=(_,_)=>Filter=Filter with{CurrentTradeCategories=null,CurrentTradeCommodities=null};none.Click+=(_,_)=>Filter=Filter with{CurrentTradeCategories=new HashSet<int>(),CurrentTradeCommodities=new HashSet<string>()};currentTradeSearch.TextChanged+=(_,_)=>Refresh();
return new(){Header="原当前贸易流",Content=content,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch};
}
private void RefreshCurrentTradeControls(GalaxyMapFilter filter)
{
currentTrade.IsChecked=filter.ShowCurrentTradeFlows;currentTradeSource.SelectedItem=currentTradeSource.Items.OfType<ComboBoxItem>().FirstOrDefault(i=>i.Tag is GalaxyTradeFlowSource source&&source==filter.CurrentTradeSource);
currentTradeSource.IsEnabled=filter.ShowCurrentTradeFlows;currentTradeStatus.Text=CurrentTradeStatus;
var groups=CurrentTradeChoices;var search=currentTradeSearch.Text.Trim();bool Contains(string text)=>text.Contains(search,StringComparison.OrdinalIgnoreCase);
var shown=groups.Select(g=>search.Length==0||Contains(g.Label)?g:g with{Children=g.Children.Where(c=>Contains(c.Label)||Contains(c.Key)).ToArray()}).Where(g=>g.Children.Count>0).ToArray();
var signature=string.Join('|',shown.Select(g=>g.Key+":"+string.Join(',',g.Children.Select(c=>c.Key+"="+c.Label))));
if(signature!=currentTradeSignature)
{
currentTradeSignature=signature;currentTradeCommodities.Children.Clear();currentTradeLeaves.Clear();currentTradeParents.Clear();currentTradeCategoryVisibility.Clear();
foreach(var group in shown)
{
var complete=groups.First(g=>g.Key==group.Key);var children=new StackPanel{Spacing=1};var parent=new CheckBox{Content="全选此类",IsThreeState=true};children.Children.Add(parent);currentTradeParents[group.Key]=(parent,complete);
parent.Click+=(_,_)=>
{
if(updating)return;
var originalCategory=GalaxyOriginalTradeCategories.Categories.FirstOrDefault(c=>group.Key=="originaltrade:"+c.Id);
var targets=originalCategory is null?complete.Children.Select(c=>c.Key):GalaxyOriginalTradeCategories.Commodities.Where(c=>c.CategoryId==originalCategory.Id).Select(c=>GalaxyMapFilter.Normalize(c.Name));
Filter=Filter with{CurrentTradeCommodities=GalaxyCommodityTree.SelectGroup(Filter.CurrentTradeCommodities,CurrentTradeUniverse(),targets,GalaxyCommodityTree.Selected(Filter.CurrentTradeCommodities,complete.Children)!=true)};
};
foreach(var child in group.Children)
{
var checkbox=new CheckBox{Content=child.Label};children.Children.Add(checkbox);currentTradeLeaves[child.Key]=checkbox;
void Select(object sender,RoutedEventArgs args)
{
if(updating)return;
var selected=Filter.CurrentTradeCommodities is {} old?new HashSet<string>(old):CurrentTradeUniverse().ToHashSet(StringComparer.Ordinal);
if(checkbox.IsChecked==true)selected.Add(child.Key);else selected.Remove(child.Key);
Filter=Filter with{CurrentTradeCommodities=selected};
}
checkbox.Checked+=Select;checkbox.Unchecked+=Select;
}
object header=group.Label+" · "+complete.Children.Count;
if(GalaxyOriginalTradeCategories.Categories.FirstOrDefault(c=>group.Key=="originaltrade:"+c.Id) is {} definition)
{
var categorySwitch=new CheckBox{Content=header};header=categorySwitch;currentTradeCategoryVisibility[definition.Id]=categorySwitch;
categorySwitch.Click+=(_,_)=>
{
if(updating)return;
var selection=Filter.CurrentTradeCategories is {} old?new HashSet<int>(old):GalaxyOriginalTradeCategories.Categories.Select(c=>c.Id).ToHashSet();
if(categorySwitch.IsChecked==true)selection.Add(definition.Id);else selection.Remove(definition.Id);
// Original parent visibility is independent of each
// leaf's saved checkbox; switching it off preserves leaves.
Filter=Filter with{CurrentTradeCategories=selection};
};
}
var expanded=new Expander{Header=header,Content=children,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch,IsExpanded=search.Length>0||currentTradeExpanded.GetValueOrDefault(group.Key)};
expanded.Expanding+=(_,_)=>currentTradeExpanded[group.Key]=true;expanded.Collapsed+=(_,_)=>currentTradeExpanded[group.Key]=false;currentTradeCommodities.Children.Add(expanded);
}
}
foreach(var row in currentTradeLeaves){row.Value.IsChecked=filter.CurrentTradeCommodities is null||filter.CurrentTradeCommodities.Contains(row.Key);row.Value.IsEnabled=filter.ShowCurrentTradeFlows;}
foreach(var row in currentTradeParents){row.Value.Check.IsChecked=GalaxyCommodityTree.Selected(filter.CurrentTradeCommodities,row.Value.Group.Children);row.Value.Check.IsEnabled=filter.ShowCurrentTradeFlows;}
foreach(var row in currentTradeCategoryVisibility){row.Value.IsChecked=filter.CurrentTradeCategories is null||filter.CurrentTradeCategories.Contains(row.Key);row.Value.IsEnabled=filter.ShowCurrentTradeFlows;}
}
private IEnumerable<string> CurrentTradeUniverse()=>GalaxyOriginalTradeCategories.Commodities.Select(c=>GalaxyMapFilter.Normalize(c.Name)).Concat(CurrentTradeChoices.SelectMany(g=>g.Children).Select(c=>c.Key)).Distinct(StringComparer.Ordinal);
}