using Microsoft.UI.Xaml.Automation;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Shapes;
using XFEEDToolBox.Core.Galaxy;
using Windows.UI;
using System.Windows.Input;
namespace XFEEDToolBox.Controls;
/// <summary>Shared view/filter controls for both galaxy map entry points.</summary>
public sealed partial class GalaxyFilterPanel:UserControl
{
public static readonly DependencyProperty FilterProperty=DependencyProperty.Register(nameof(Filter),typeof(GalaxyMapFilter),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty SystemsProperty=DependencyProperty.Register(nameof(Systems),typeof(IReadOnlyList<GalacticSystem>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty ShipJumpRangeProperty=DependencyProperty.Register(nameof(ShipJumpRange),typeof(double),typeof(GalaxyFilterPanel),new PropertyMetadata(0d,Changed));
public static readonly DependencyProperty InformationStatusProperty=DependencyProperty.Register(nameof(InformationStatus),typeof(string),typeof(GalaxyFilterPanel),new PropertyMetadata("",Changed));
public static readonly DependencyProperty ImportSettingsCommandProperty=DependencyProperty.Register(nameof(ImportSettingsCommand),typeof(ICommand),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty OriginalSettingsStatusProperty=DependencyProperty.Register(nameof(OriginalSettingsStatus),typeof(string),typeof(GalaxyFilterPanel),new PropertyMetadata("",Changed));
public static readonly DependencyProperty OriginalSettingsDetailsProperty=DependencyProperty.Register(nameof(OriginalSettingsDetails),typeof(string),typeof(GalaxyFilterPanel),new PropertyMetadata("",Changed));
public static readonly DependencyProperty OriginalOptionsProperty=DependencyProperty.Register(nameof(OriginalOptions),typeof(IReadOnlyDictionary<string,string>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty QueryChoicesProperty=DependencyProperty.Register(nameof(QueryChoices),typeof(IReadOnlyList<GalaxyFilterChoice>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty HistoricalTradeChoicesProperty=DependencyProperty.Register(nameof(HistoricalTradeChoices),typeof(IReadOnlyList<GalaxyFilterChoice>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty HistoricalTradeStatusProperty=DependencyProperty.Register(nameof(HistoricalTradeStatus),typeof(string),typeof(GalaxyFilterPanel),new PropertyMetadata("",Changed));
public static readonly DependencyProperty ThargoidCountsProperty=DependencyProperty.Register(nameof(ThargoidCounts),typeof(IReadOnlyList<GalaxyThargoidCountDisplay>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty ThargoidSignalsProperty=DependencyProperty.Register(nameof(ThargoidSignals),typeof(IReadOnlyList<GalaxyThargoidTarget>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty ThargoidRecommendationsProperty=DependencyProperty.Register(nameof(ThargoidRecommendations),typeof(IReadOnlyList<GalaxyThargoidTarget>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty ThargoidStatusProperty=DependencyProperty.Register(nameof(ThargoidStatus),typeof(string),typeof(GalaxyFilterPanel),new PropertyMetadata("",Changed));
public static readonly DependencyProperty SelectThargoidTargetCommandProperty=DependencyProperty.Register(nameof(SelectThargoidTargetCommand),typeof(ICommand),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty LocatePowerHeadquartersCommandProperty=DependencyProperty.Register(nameof(LocatePowerHeadquartersCommand),typeof(ICommand),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty CommunityGoalTargetsProperty=DependencyProperty.Register(nameof(CommunityGoalTargets),typeof(IReadOnlyList<GalaxyAccountMapTarget>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty CommunityGoalStatusProperty=DependencyProperty.Register(nameof(CommunityGoalStatus),typeof(string),typeof(GalaxyFilterPanel),new PropertyMetadata("",Changed));
public static readonly DependencyProperty SelectCommunityGoalCommandProperty=DependencyProperty.Register(nameof(SelectCommunityGoalCommand),typeof(ICommand),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public GalaxyMapFilter Filter {get=>(GalaxyMapFilter?)GetValue(FilterProperty)??GalaxyMapFilter.Default;set=>SetValue(FilterProperty,value);}
public IReadOnlyList<GalacticSystem> Systems {get=>(IReadOnlyList<GalacticSystem>?)GetValue(SystemsProperty)??[];set=>SetValue(SystemsProperty,value);}
public double ShipJumpRange {get=>(double)GetValue(ShipJumpRangeProperty);set=>SetValue(ShipJumpRangeProperty,value);}
public string InformationStatus {get=>(string)GetValue(InformationStatusProperty);set=>SetValue(InformationStatusProperty,value);}
public ICommand? ImportSettingsCommand {get=>(ICommand?)GetValue(ImportSettingsCommandProperty);set=>SetValue(ImportSettingsCommandProperty,value);}
public string OriginalSettingsStatus {get=>(string)GetValue(OriginalSettingsStatusProperty);set=>SetValue(OriginalSettingsStatusProperty,value);}
public string OriginalSettingsDetails {get=>(string)GetValue(OriginalSettingsDetailsProperty);set=>SetValue(OriginalSettingsDetailsProperty,value);}
public IReadOnlyDictionary<string,string>? OriginalOptions {get=>(IReadOnlyDictionary<string,string>?)GetValue(OriginalOptionsProperty);set=>SetValue(OriginalOptionsProperty,value);}
public IReadOnlyList<GalaxyFilterChoice>? QueryChoices {get=>(IReadOnlyList<GalaxyFilterChoice>?)GetValue(QueryChoicesProperty);set=>SetValue(QueryChoicesProperty,value);}
public IReadOnlyList<GalaxyFilterChoice> HistoricalTradeChoices {get=>(IReadOnlyList<GalaxyFilterChoice>?)GetValue(HistoricalTradeChoicesProperty)??[];set=>SetValue(HistoricalTradeChoicesProperty,value);}
public string HistoricalTradeStatus {get=>(string)GetValue(HistoricalTradeStatusProperty);set=>SetValue(HistoricalTradeStatusProperty,value);}
public IReadOnlyList<GalaxyThargoidCountDisplay> ThargoidCounts {get=>(IReadOnlyList<GalaxyThargoidCountDisplay>?)GetValue(ThargoidCountsProperty)??[];set=>SetValue(ThargoidCountsProperty,value);}
public IReadOnlyList<GalaxyThargoidTarget> ThargoidSignals {get=>(IReadOnlyList<GalaxyThargoidTarget>?)GetValue(ThargoidSignalsProperty)??[];set=>SetValue(ThargoidSignalsProperty,value);}
public IReadOnlyList<GalaxyThargoidTarget> ThargoidRecommendations {get=>(IReadOnlyList<GalaxyThargoidTarget>?)GetValue(ThargoidRecommendationsProperty)??[];set=>SetValue(ThargoidRecommendationsProperty,value);}
public string ThargoidStatus {get=>(string)GetValue(ThargoidStatusProperty);set=>SetValue(ThargoidStatusProperty,value);}
public ICommand? SelectThargoidTargetCommand {get=>(ICommand?)GetValue(SelectThargoidTargetCommandProperty);set=>SetValue(SelectThargoidTargetCommandProperty,value);}
public ICommand? LocatePowerHeadquartersCommand {get=>(ICommand?)GetValue(LocatePowerHeadquartersCommandProperty);set=>SetValue(LocatePowerHeadquartersCommandProperty,value);}
public IReadOnlyList<GalaxyAccountMapTarget> CommunityGoalTargets {get=>(IReadOnlyList<GalaxyAccountMapTarget>?)GetValue(CommunityGoalTargetsProperty)??[];set=>SetValue(CommunityGoalTargetsProperty,value);}
public string CommunityGoalStatus {get=>(string)GetValue(CommunityGoalStatusProperty);set=>SetValue(CommunityGoalStatusProperty,value);}
public ICommand? SelectCommunityGoalCommand {get=>(ICommand?)GetValue(SelectCommunityGoalCommandProperty);set=>SetValue(SelectCommunityGoalCommandProperty,value);}
private bool updating;
private readonly ComboBox mode=new(){Header="视图模式",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly ComboBox category=new(){Header="星系筛选",HorizontalAlignment=HorizontalAlignment.Stretch,DisplayMemberPath="Label",ItemsSource=GalaxyFilterDefinitions.All};
private readonly ComboBox size=new(){Header="恒星大小",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly ComboBox commodityDirection=new(){Header="商品流向",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly ComboBox powerplayView=new(){Header="势力博弈视图",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly ComboBox activityPower=new(){Header="活动势力范围",PlaceholderText="选择实际势力或全部势力",DisplayMemberPath="Label",HorizontalAlignment=HorizontalAlignment.Stretch};
private string activityChoicesSignature="";
private IReadOnlyList<GalaxyFilterChoice> activityChoices=[];
private readonly ComboBox activityType=new(){Header="活动类型",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly CheckBox useMinimumActivity=new(){Content="限制最低近期活动强度"};
private readonly CheckBox territoryActivityEnabled=new(){Content="启用领土活动显示与条件"};
private readonly Slider minimumActivity=new(){Minimum=0,Maximum=1,StepFrequency=.01,TickFrequency=.25,Header="最低近期活动强度",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly StackPanel activityControls=new(){Spacing=5};
private readonly TextBlock activityNote=new(){Text="领土附加条件:仅启用最低强度时按所选势力/活动的原近期计数过滤;计数0有效,缺缓存保持未知。",FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly Button powerHeadquarters=new(){Content="定位所选势力总部",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly Expander thargoidPanel=new(){Header="原游戏信号与推荐星系",HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch,IsExpanded=true};
private readonly StackPanel thargoidCounts=new(){Spacing=3},thargoidSignals=new(){Spacing=5},thargoidRecommendations=new(){Spacing=5};
private readonly TextBlock thargoidStatus=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private IReadOnlyList<GalaxyThargoidCountDisplay>? renderedThargoidCounts;
private IReadOnlyList<GalaxyThargoidTarget>? renderedThargoidSignals,renderedThargoidRecommendations;
private ICommand? renderedThargoidCommand;
private readonly StackPanel communityGoalTargets=new(){Spacing=5};
private readonly TextBlock communityGoalStatus=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private IReadOnlyList<GalaxyAccountMapTarget>? renderedCommunityGoals;
private ICommand? renderedCommunityGoalCommand;
private readonly StackPanel choices=new(){Spacing=0};
private readonly StackPanel commodityGroups=new(){Spacing=5};
private readonly Dictionary<string,CheckBox> commodityLeaves=new(StringComparer.Ordinal);
private readonly Dictionary<string,(CheckBox Check,GalaxyCommodityGroup Group)> commodityParents=new(StringComparer.Ordinal);
private readonly Dictionary<string,bool> commodityExpanded=new(StringComparer.Ordinal);
private string commodityTreeSignature="";
private IReadOnlyList<GalaxyCommodityGroup> currentCommodityGroups=[];
private readonly StackPanel selectionActions=new(){Orientation=Orientation.Horizontal,Spacing=8};
private readonly Expander powerStates=new(){Header="领土状态(与所选势力同时满足)",HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch};
private readonly Dictionary<string,CheckBox> powerStateChecks=new(StringComparer.Ordinal);
private readonly TextBox choiceSearch=new(){PlaceholderText="查找筛选值(中文或原名)"};
private readonly TextBlock coverage=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly StackPanel populationLegend=new(){Spacing=3};
private readonly TextBlock populationLegendText=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly TextBlock note=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly TextBlock information=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly CheckBox unknown=new(){Content="显示未知数据的星系"};
private readonly CheckBox grid=new(){Content="星图网格"},gridStalks=new(){Content="星系到网格的投影线(工具选项)"},labels=new(){Content="恒星名称"},links=new(){Content="显示跳跃连接线"},markers=new(){Content="地点与工程师标记"},route=new(){Content="导航标记与航线"},background=new(){Content="银河与星云背景"};
private readonly CheckBox reverseMouseCrane=new(){Content="反转鼠标升降(工具选项)"};
private readonly CheckBox regionNames=new(){Content="原游戏区域名称"},nebulaNames=new(){Content="星云名称"},constellations=new(){Content="星座连线"},galacticZones=new(){Content="银河扇区平面"},galacticZoneNames=new(){Content="银河扇区名称"};
private readonly CheckBox historicalTrade=new(){Content="显示缓存历史贸易流(非实时)"};
private readonly ComboBox historicalTradeSource=new(){Header="历史流量来源",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly TextBox historicalTradeSearch=new(){PlaceholderText="查找缓存中的商品"};
private readonly TextBlock historicalTradeStatus=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly StackPanel historicalTradeCommodities=new(){Spacing=0};
private readonly Dictionary<string,CheckBox> historicalCommodityChecks=new(StringComparer.Ordinal);
private string historicalCommoditySignature="";
private readonly NumberBox range=new(){Header="跳跃连接范围 / ly",Minimum=1,Maximum=1000,SmallChange=1,SpinButtonPlacementMode=NumberBoxSpinButtonPlacementMode.Compact};
private readonly NumberBox populationMin=new(){Header="人口下限(人)",Minimum=0,Maximum=9_000_000_000_000_000};
private readonly NumberBox populationMax=new(){Header="人口满值(人)",Minimum=0,Maximum=9_000_000_000_000_000};
private readonly Button shipRange=new(){Content="采用当前飞船最大跳距",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly Button scoop=new(){Content="仅可吸油 O B A F G K M"};
private readonly Button importSettings=new(){Content="读取游戏星图配置",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly TextBlock importStatus=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly StackPanel originalOptions=new(){Spacing=5};
private readonly TextBlock importDetails=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly CheckBox originalRouteFilter=new(){Content="将筛选应用于路线(游戏保存值)",IsEnabled=false,IsThreeState=true};
private IReadOnlyDictionary<string,string>? renderedOriginalOptions;
private readonly Dictionary<string,(CheckBox Row,TextBlock Text,Ellipse Swatch)> choiceRows=new(StringComparer.Ordinal);
private string renderedChoices="";
private IReadOnlyList<GalacticSystem>? countedSystems;
private GalaxyFilterCategory countedCategory=(GalaxyFilterCategory)(-1);
private bool countedOriginalState;
private GalaxyCommodityDirection countedCommodityDirection;
private readonly Dictionary<string,int> choiceCounts=new(StringComparer.Ordinal);
public GalaxyFilterPanel()
{
InstallStaticCheckBoxTemplate();
Width=300;HorizontalContentAlignment=HorizontalAlignment.Stretch;
var muted=(Brush)Application.Current.Resources["MutedBrush"];coverage.Foreground=note.Foreground=information.Foreground=muted;
constellations.Content="原游戏星座连线";
ToolTipService.SetToolTip(constellations,"原资源16组、206条实际连线;整组全部端点均真实已载入且通过当前查询才绘制完整组,缺任一端点整组等待。");
mode.Items.Add(new ComboBoxItem{Content="写实模式",Tag=GalaxyMapMode.Realistic});mode.Items.Add(new ComboBoxItem{Content="地图模式",Tag=GalaxyMapMode.Map});
foreach(var (name,value) in new[]{("势力博弈",GalaxyMapMode.Powerplay),("Thargoid 战争模式",GalaxyMapMode.ThargoidWar),("星系殖民模式",GalaxyMapMode.Colonisation)})
{var item=new ComboBoxItem{Content=name,Tag=value};mode.Items.Add(item);}
var root=new StackPanel{Spacing=8};root.Children.Add(new TextBlock{Text="银河系星图 · 视图",FontSize=18,FontWeight=Microsoft.UI.Text.FontWeights.SemiBold});
AutomationProperties.SetName(importSettings,"读取游戏星图配置");importStatus.Foreground=muted;
// Original3A3D910 publishes exactly these three radio UIDs/labels.
// Saved mode3 is accepted by the predicate but displays the first item.
commodityDirection.Items.Add(new ComboBoxItem{Content="进口/出口价格",Tag=GalaxyCommodityDirection.Both});commodityDirection.Items.Add(new ComboBoxItem{Content="进口价格",Tag=GalaxyCommodityDirection.Imports});commodityDirection.Items.Add(new ComboBoxItem{Content="出口价格",Tag=GalaxyCommodityDirection.Exports});
powerplayView.Items.Add(new ComboBoxItem{Content="领土:势力与状态",Tag=GalaxyPowerplayView.Territory});powerplayView.Items.Add(new ComboBoxItem{Content="活动:原范围与星系条件",Tag=GalaxyPowerplayView.Activities});
foreach(var entry in new[]{("暗中破坏",GalaxyPowerActivity.Undermining),("巩固中",GalaxyPowerActivity.Reinforcing),("正在获取",GalaxyPowerActivity.Acquiring),("抵抗破坏",GalaxyPowerActivity.BeingUndermined),("总计",GalaxyPowerActivity.Total)})activityType.Items.Add(new ComboBoxItem{Content=entry.Item1,Tag=entry.Item2});
activityControls.Children.Add(territoryActivityEnabled);activityControls.Children.Add(activityPower);activityControls.Children.Add(activityType);activityControls.Children.Add(useMinimumActivity);activityControls.Children.Add(minimumActivity);
activityNote.Foreground=muted;activityControls.Children.Add(activityNote);savedActivityPowerStatus.Foreground=muted;activityControls.Children.Add(savedActivityPowerStatus);
root.Children.Add(mode);root.Children.Add(powerplayView);root.Children.Add(category);root.Children.Add(commodityDirection);root.Children.Add(activityControls);root.Children.Add(powerHeadquarters);root.Children.Add(coverage);
AutomationProperties.SetName(powerHeadquarters,"定位所选势力总部");ToolTipService.SetToolTip(powerHeadquarters,"选择一个实际势力,读取其原定义中的总部地址并定位;全部势力或未知总部时不可用。");
var thargoidContent=new StackPanel{Spacing=7};thargoidContent.Children.Add(thargoidStatus);thargoidContent.Children.Add(thargoidCounts);
thargoidContent.Children.Add(new TextBlock{Text="原信号",FontWeight=Microsoft.UI.Text.FontWeights.SemiBold});thargoidContent.Children.Add(new ScrollViewer{Content=thargoidSignals,MaxHeight=220,VerticalScrollBarVisibility=ScrollBarVisibility.Auto});
thargoidContent.Children.Add(new TextBlock{Text="原推荐星系(每组至多5项)",FontWeight=Microsoft.UI.Text.FontWeights.SemiBold});thargoidContent.Children.Add(new ScrollViewer{Content=thargoidRecommendations,MaxHeight=300,VerticalScrollBarVisibility=ScrollBarVisibility.Auto});
thargoidContent.Children.Add(new TextBlock{Text="点击按真实地址解析并定位。仅显示原游戏已有缓存,不发起原网络更新;列表长度不代替战争状态总数。",FontSize=11,TextWrapping=TextWrapping.Wrap,Foreground=muted});
thargoidPanel.Content=thargoidContent;root.Children.Add(thargoidPanel);
var populationRamp=new LinearGradientBrush{StartPoint=new(0,.5),EndPoint=new(1,.5)};
var low=GalaxyMapFilter.PopulationLowColour;var high=GalaxyMapFilter.PopulationHighColour;
populationRamp.GradientStops.Add(new GradientStop{Offset=0,Color=Color.FromArgb(255,low.R,low.G,low.B)});populationRamp.GradientStops.Add(new GradientStop{Offset=1,Color=Color.FromArgb(255,high.R,high.G,high.B)});
populationLegend.Children.Add(new Rectangle{Height=6,Fill=populationRamp,HorizontalAlignment=HorizontalAlignment.Stretch});populationLegend.Children.Add(populationLegendText);root.Children.Add(populationLegend);
var all=new Button{Content="全选",FontSize=12};var none=new Button{Content="清除",FontSize=12};
scoop.Content="仅可吸油";scoop.FontSize=12;ToolTipService.SetToolTip(scoop,"仅显示可吸油的 O、B、A、F、G、K、M 型恒星");
selectionActions.Children.Add(all);selectionActions.Children.Add(none);selectionActions.Children.Add(scoop);root.Children.Add(selectionActions);
root.Children.Add(choiceSearch);root.Children.Add(new ScrollViewer{Content=choices,MaxHeight=238,VerticalScrollBarVisibility=ScrollBarVisibility.Auto,HorizontalScrollBarVisibility=ScrollBarVisibility.Disabled});root.Children.Add(unknown);
root.Children.Add(commodityGroups);
var powerStateContent=new StackPanel{Spacing=2};var stateAll=new Button{Content="全部状态",HorizontalAlignment=HorizontalAlignment.Stretch};powerStateContent.Children.Add(stateAll);stateAll.Click+=(_,_)=>Filter=Filter with{TerritoryStateMask=null,PowerStateValues=null};
foreach(var state in GalaxyFilterDefinitions.All.First(d=>d.Category==GalaxyFilterCategory.PowerState).Choices)
{
var check=new CheckBox{Content=state.Label,IsThreeState=true};powerStateChecks[state.Key]=check;
void SelectState(object sender,RoutedEventArgs e)
{if(updating)return;Filter=Filter with{TerritoryStateMask=GalaxyOriginalPowerFilters.SetStateGroup(Filter.TerritoryStateMask,state.Key,check.IsChecked==true),PowerStateValues=null};}
check.Checked+=SelectState;check.Unchecked+=SelectState;check.Indeterminate+=SelectState;powerStateContent.Children.Add(check);
}
powerStates.Content=powerStateContent;root.Children.Add(powerStates);
var dataNotes=new StackPanel{Spacing=6};dataNotes.Children.Add(note);dataNotes.Children.Add(information);dataNotes.Children.Add(new TextBlock{Text="移动或缩放后按当前视野重新查询;未匹配星系隐藏。",FontSize=11,TextWrapping=TextWrapping.Wrap,Foreground=muted});
root.Children.Add(new Expander{Header="资料与覆盖说明",Content=dataNotes,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch});
InitializeSizeOptions();
var display=new StackPanel{Spacing=2};display.Children.Add(size);display.Children.Add(populationMin);display.Children.Add(populationMax);foreach(var toggle in new[]{grid,gridStalks,labels,regionNames,nebulaNames,constellations,galacticZones,galacticZoneNames,links,markers,route,background,reverseMouseCrane})display.Children.Add(toggle);display.Children.Add(range);display.Children.Add(shipRange);
display.Children.Add(new TextBlock{Text="名称、星座和扇区图层独立叠加;不会把区域或星云当作恒星筛选。各图层仅绘制已核验的原资源。",FontSize=11,TextWrapping=TextWrapping.Wrap,Foreground=muted});
display.Children.Add(new TextBlock{Text="连线以选中或当前星系为起点,受实际光年距离限制。范围为显示参数,不等于已规划航路。",FontSize=11,TextWrapping=TextWrapping.Wrap,Foreground=muted});
root.Children.Add(new Expander{Header="显示控制",Content=display,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch});
root.Children.Add(BuildCurrentTradeControls());
foreach(var entry in new[]{("全部缓存流量",GalaxyTradeFlowSource.All),("玩家缓存流量",GalaxyTradeFlowSource.Players),("NPC 缓存流量",GalaxyTradeFlowSource.Npc)})historicalTradeSource.Items.Add(new ComboBoxItem{Content=entry.Item1,Tag=entry.Item2});
var history=new StackPanel{Spacing=5};history.Children.Add(historicalTrade);history.Children.Add(historicalTradeSource);history.Children.Add(historicalTradeSearch);
var historyAll=new Button{Content="全部商品"};var historyNone=new Button{Content="清除商品"};var historyActions=new StackPanel{Orientation=Orientation.Horizontal,Spacing=7};historyActions.Children.Add(historyAll);historyActions.Children.Add(historyNone);history.Children.Add(historyActions);
history.Children.Add(new ScrollViewer{Content=historicalTradeCommodities,MaxHeight=180,VerticalScrollBarVisibility=ScrollBarVisibility.Auto});history.Children.Add(historicalTradeStatus);
history.Children.Add(new TextBlock{Text="按原贸易流13类目录分组,仅列缓存实际商品;原目录以外的商品分类保持未知。父类选择作用于整类,查找仅改变列表。",FontSize=11,TextWrapping=TextWrapping.Wrap,Foreground=muted});
history.Children.Add(new TextBlock{Text="来自当前账号原TRD缓存的带时间贸易记录;不会代替游戏实时有效期。只绘制真实地址和坐标已确认的端点。",FontSize=11,TextWrapping=TextWrapping.Wrap,Foreground=muted});
root.Children.Add(new Expander{Header="缓存历史贸易流",Content=history,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch});
historicalTrade.Checked+=(_,_)=>{if(!updating)Filter=Filter with{ShowHistoricalTradeFlows=true};};historicalTrade.Unchecked+=(_,_)=>{if(!updating)Filter=Filter with{ShowHistoricalTradeFlows=false};};
historicalTradeSource.SelectionChanged+=(_,_)=>{if(!updating&&historicalTradeSource.SelectedItem is ComboBoxItem{Tag:GalaxyTradeFlowSource source})Filter=Filter with{HistoricalTradeSource=source};};
historyAll.Click+=(_,_)=>Filter=Filter with{HistoricalTradeCommodities=null};historyNone.Click+=(_,_)=>Filter=Filter with{HistoricalTradeCommodities=new HashSet<string>()};historicalTradeSearch.TextChanged+=(_,_)=>Refresh();
root.Children.Add(importSettings);root.Children.Add(importStatus);
root.Children.Add(BuildFleetControls());
root.Children.Add(BuildMissionControls());
var layers=new StackPanel{Spacing=3};layers.Children.Add(new TextBlock{Text="选择标记后查询当前视野;资料说明会列出实际来源与待确认的覆盖。",FontSize=11,Foreground=muted,TextWrapping=TextWrapping.Wrap});
foreach(var entry in GalaxyFilterDefinitions.All.First(d=>d.Category==GalaxyFilterCategory.Markers).Choices)
{
var button=new Button{Content=entry.Label,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Left};
AutomationProperties.SetName(button,"查询"+entry.Label);button.Click+=(_,_)=>Filter=Filter.WithCategory(GalaxyFilterCategory.Markers) with{Values=new HashSet<string>{entry.Key},IncludeUnknown=false};layers.Children.Add(button);
}
root.Children.Add(new Expander{Header="原游戏标记查询",Content=new ScrollViewer{Content=layers,MaxHeight=260,VerticalScrollBarVisibility=ScrollBarVisibility.Auto},HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch});
var goals=new StackPanel{Spacing=6};goals.Children.Add(communityGoalStatus);goals.Children.Add(new ScrollViewer{Content=communityGoalTargets,MaxHeight=250,VerticalScrollBarVisibility=ScrollBarVisibility.Auto});
root.Children.Add(new Expander{Header="社区活动 · 原发布缓存",Content=goals,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch});
ToolTipService.SetToolTip(originalRouteFilter,"显示原游戏保存的此类别开关。恒星类别的路线设置可在航行偏好中更改,长按目标规划时应用。");root.Children.Add(originalRouteFilter);
var originalContent=new StackPanel{Spacing=7};originalContent.Children.Add(importDetails);originalContent.Children.Add(originalOptions);
root.Children.Add(new Expander{Header="原游戏配置值与导入说明(只读)",Content=new ScrollViewer{Content=originalContent,MaxHeight=220,HorizontalScrollBarVisibility=ScrollBarVisibility.Disabled,VerticalScrollBarVisibility=ScrollBarVisibility.Auto},HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch});
root.Children.Add(new TextBlock{Text="筛选即时作用于本工具星图;游戏规划的航路使用游戏内筛选设置。",Foreground=muted,FontSize=11,TextWrapping=TextWrapping.Wrap});
var reset=new Button{Content="恢复默认视图",HorizontalAlignment=HorizontalAlignment.Stretch};root.Children.Add(reset);Content=root;
mode.SelectionChanged+=(_,_)=>{if(!updating&&mode.SelectedItem is ComboBoxItem{Tag:GalaxyMapMode selected})Filter=selected switch{GalaxyMapMode.Powerplay=>Filter.WithCategory(GalaxyFilterCategory.Power) with{Mode=selected,Values=null,PowerStateValues=null},GalaxyMapMode.ThargoidWar=>Filter.WithCategory(GalaxyFilterCategory.ThargoidWar) with{Mode=selected},GalaxyMapMode.Colonisation=>Filter.WithCategory(GalaxyFilterCategory.Colonisation) with{Mode=selected},_=>Filter with{Mode=selected}};};
category.SelectionChanged+=(_,_)=>{if(!updating&&category.SelectedItem is GalaxyFilterDefinition selected)Filter=selected.Category==GalaxyFilterCategory.PowerActivityEligibility?Filter.WithCategory(selected.Category) with{Mode=GalaxyMapMode.Powerplay,PowerplayView=GalaxyPowerplayView.Territory}:Filter.WithCategory(selected.Category);};
powerplayView.SelectionChanged+=(_,_)=>{if(!updating&&powerplayView.SelectedItem is ComboBoxItem{Tag:GalaxyPowerplayView selected})Filter=Filter.WithCategory(GalaxyFilterCategory.Power) with{Mode=GalaxyMapMode.Powerplay,PowerplayView=selected,Values=null,PowerStateValues=null};};
activityPower.SelectionChanged+=(_,_)=>{if(!updating&&activityPower.SelectedItem is GalaxyFilterChoice selected)Filter=Filter with{ActivityAllPowers=selected.Key=="__allpowers",ActivityPowerName=selected.Key=="__allpowers"?null:selected.Label,ActivityInternalPowerId=selected.Key=="__allpowers"?null:Filter.OriginalPowerOptions.FirstOrDefault(p=>GalaxyPowerActivities.ChoiceKey(p.InternalPowerId)==selected.Key)?.InternalPowerId,OriginalActivityPowerIndex=null};};
activityType.SelectionChanged+=(_,_)=>{if(!updating&&activityType.SelectedItem is ComboBoxItem{Tag:GalaxyPowerActivity selected})Filter=Filter with{Activity=selected};};
useMinimumActivity.Checked+=(_,_)=>{if(!updating)Filter=Filter with{UseMinimumActivity=true};};useMinimumActivity.Unchecked+=(_,_)=>{if(!updating)Filter=Filter with{UseMinimumActivity=false};};
territoryActivityEnabled.Checked+=(_,_)=>{if(!updating)Filter=Filter with{TerritoryActivityFilterEnabled=true};};territoryActivityEnabled.Unchecked+=(_,_)=>{if(!updating)Filter=Filter with{TerritoryActivityFilterEnabled=false};};
minimumActivity.ValueChanged+=(_,e)=>{if(!updating)Filter=Filter with{MinimumActivity=e.NewValue};};
size.SelectionChanged+=(_,_)=>{if(!updating&&size.SelectedItem is ComboBoxItem{Tag:GalaxyFilterCategory selected})Filter=Filter with{SizeCategory=selected};};
commodityDirection.SelectionChanged+=(_,_)=>{if(!updating&&commodityDirection.SelectedItem is ComboBoxItem{Tag:GalaxyCommodityDirection selected})Filter=Filter with{CommodityDirection=selected,OriginalCommodityPriceMode=commodityDirection.SelectedIndex};};
void PopulationRangeChanged(NumberBox sender,NumberBoxValueChangedEventArgs args)
{if(!updating&&double.IsFinite(populationMin.Value)&&double.IsFinite(populationMax.Value)&&populationMin.Value>=0&&populationMax.Value>=populationMin.Value)Filter=Filter with{PopulationMinimum=populationMin.Value,PopulationMaximum=populationMax.Value};}
populationMin.ValueChanged+=PopulationRangeChanged;populationMax.ValueChanged+=PopulationRangeChanged;
all.Click+=(_,_)=>Filter=Filter with{Mode=Filter.Mode==GalaxyMapMode.Realistic?GalaxyMapMode.Map:Filter.Mode,TerritoryPowerMask=Filter.UsesPowerTerritory?null:Filter.TerritoryPowerMask,OriginalCommodityUid=Filter.Category==GalaxyFilterCategory.Commodity?null:Filter.OriginalCommodityUid,Values=null,IncludeUnknown=false};
none.Click+=(_,_)=>Filter=Filter with{Mode=Filter.Mode==GalaxyMapMode.Realistic?GalaxyMapMode.Map:Filter.Mode,TerritoryPowerMask=Filter.UsesPowerTerritory?0:Filter.TerritoryPowerMask,OriginalCommodityUid=Filter.Category==GalaxyFilterCategory.Commodity?null:Filter.OriginalCommodityUid,Values=Filter.UsesPowerTerritory?null:new HashSet<string>(),IncludeUnknown=false};
scoop.Click+=(_,_)=>Filter=Filter.WithCategory(GalaxyFilterCategory.StarClass) with{Values=new HashSet<string>(GalaxyFilterDefinitions.ScoopableStarClasses),IncludeUnknown=false};
foreach(var toggle in new[]{grid,gridStalks,labels,regionNames,nebulaNames,constellations,galacticZones,galacticZoneNames,links,markers,route,background,reverseMouseCrane,unknown}){toggle.Checked+=ToggleChanged;toggle.Unchecked+=ToggleChanged;}
range.ValueChanged+=(_,e)=>{if(!updating&&double.IsFinite(e.NewValue)&&e.NewValue is >=1 and <=1000)Filter=Filter with{JumpRange=e.NewValue};};
shipRange.Click+=(_,_)=>{if(ShipJumpRange is >=1 and <=1000)Filter=Filter with{JumpRange=ShipJumpRange};};
reset.Click+=(_,_)=>Filter=GalaxyMapFilter.Default;
choiceSearch.TextChanged+=(_,_)=>Refresh();
root.Children.Add(BuildMarkerVisibilityControls());
Loaded+=(_,_)=>Refresh();Refresh();
}
private static void Changed(DependencyObject sender,DependencyPropertyChangedEventArgs e)=>((GalaxyFilterPanel)sender).Refresh();
private void ToggleChanged(object sender,RoutedEventArgs e)
{
if(updating)return;Filter=Filter with{IncludeUnknown=unknown.IsChecked==true,ShowGrid=grid.IsChecked==true,ShowGridStalks=gridStalks.IsChecked==true,ReverseMouseCrane=reverseMouseCrane.IsChecked==true,ShowLinks=links.IsChecked==true,ShowLabels=labels.IsChecked==true,ShowRegionNames=regionNames.IsChecked==true,ShowNebulaNames=nebulaNames.IsChecked==true,ShowConstellations=constellations.IsChecked==true,ShowGalacticZonesPlane=galacticZones.IsChecked==true,ShowGalacticZoneNames=galacticZoneNames.IsChecked==true,ShowMarkers=markers.IsChecked==true,ShowRoute=route.IsChecked==true,ShowBackground=background.IsChecked==true};
}
private void Refresh()
{
if(updating)return;updating=true;
try
{
var f=Filter;mode.SelectedIndex=(int)f.Mode;category.SelectedItem=GalaxyFilterDefinitions.All.First(x=>x.Category==f.Category);
populationLegend.Visibility=f.Mode!=GalaxyMapMode.Realistic&&f.Category==GalaxyFilterCategory.Population?Visibility.Visible:Visibility.Collapsed;
populationLegendText.Text=$"{f.PopulationMinimum:N0} — {f.PopulationMaximum:N0} 人 · 线性配色";
powerStates.Visibility=f.Mode==GalaxyMapMode.Powerplay&&!f.UsesPowerOuter&&f.Category==GalaxyFilterCategory.Power?Visibility.Visible:Visibility.Collapsed;
foreach(var check in powerStateChecks)check.Value.IsChecked=f.UsesPowerTerritory?GalaxyOriginalPowerFilters.StateSelected(f.TerritoryStateMask,check.Key):f.PowerStateValues is null||f.PowerStateValues.Contains(check.Key);
RefreshSizeSelection(f);
commodityDirection.Visibility=f.Category==GalaxyFilterCategory.Commodity||f.SizeCategory==GalaxyFilterCategory.Commodity?Visibility.Visible:Visibility.Collapsed;commodityDirection.SelectedIndex=f.CommodityPriceMode is 1 or 2?f.CommodityPriceMode:0;
powerplayView.Visibility=f.Mode==GalaxyMapMode.Powerplay?Visibility.Visible:Visibility.Collapsed;powerplayView.SelectedIndex=(int)f.PowerplayView;
activityControls.Visibility=f.Mode==GalaxyMapMode.Powerplay&&f.Category is GalaxyFilterCategory.Power or GalaxyFilterCategory.PowerActivityEligibility?Visibility.Visible:Visibility.Collapsed;
activityPower.Header=f.UsesPowerOuter?"势力(总部定位)":"活动势力范围";
activityType.Visibility=useMinimumActivity.Visibility=minimumActivity.Visibility=activityNote.Visibility=f.UsesPowerOuter?Visibility.Collapsed:Visibility.Visible;
territoryActivityEnabled.Visibility=f.UsesPowerOuter||f.Category==GalaxyFilterCategory.PowerActivityEligibility?Visibility.Collapsed:Visibility.Visible;territoryActivityEnabled.IsChecked=f.TerritoryActivityFilterEnabled;
activityType.IsEnabled=useMinimumActivity.IsEnabled=f.UsesPowerActivityCounter;
category.Visibility=f.UsesPowerOuter?Visibility.Collapsed:Visibility.Visible;
powerHeadquarters.Visibility=f.Mode==GalaxyMapMode.Powerplay?Visibility.Visible:Visibility.Collapsed;powerHeadquarters.Command=LocatePowerHeadquartersCommand;
thargoidPanel.Visibility=f.Mode==GalaxyMapMode.ThargoidWar||f.Category==GalaxyFilterCategory.ThargoidWar?Visibility.Visible:Visibility.Collapsed;
RefreshThargoid();
RefreshCommunityGoals();
RefreshFleetControls();
RefreshMissionControls();
selectionActions.Visibility=choices.Visibility=choiceSearch.Visibility=f.UsesPowerOuter||f.Category==GalaxyFilterCategory.PowerActivityEligibility?Visibility.Collapsed:Visibility.Visible;
commodityGroups.Visibility=f.Category==GalaxyFilterCategory.Commodity?Visibility.Visible:Visibility.Collapsed;
if(f.Category==GalaxyFilterCategory.Commodity){choices.Visibility=Visibility.Collapsed;RefreshCommodityTree();}
var powerChoices=new[]{new GalaxyFilterChoice("__allpowers","全部势力(原活动选项)")}.Concat(f.OriginalPowerOptions.Select(p=>new GalaxyFilterChoice(GalaxyPowerActivities.ChoiceKey(p.InternalPowerId),p.Name))).ToArray();
var powerSignature=string.Join('|',powerChoices.Select(p=>p.Key+":"+p.Label));if(activityChoicesSignature!=powerSignature){activityChoicesSignature=powerSignature;activityChoices=powerChoices;activityPower.ItemsSource=activityChoices;}
activityPower.SelectedItem=activityChoices.FirstOrDefault(p=>f.ActivityAllPowers?p.Key=="__allpowers":f.ActivityInternalPowerId is {} powerId?p.Key==GalaxyPowerActivities.ChoiceKey(powerId):string.Equals(p.Label,f.ActivityPowerName,StringComparison.OrdinalIgnoreCase));activityType.SelectedIndex=(int)f.Activity;
RefreshSavedActivityPower();
useMinimumActivity.IsChecked=f.UseMinimumActivity;minimumActivity.Value=f.MinimumActivity;minimumActivity.IsEnabled=f.UsesPowerActivityCounter&&f.UseMinimumActivity;minimumActivity.Header=$"最低近期活动强度 {f.MinimumActivity:P0}";
populationMin.Value=f.PopulationMinimum;populationMax.Value=f.PopulationMaximum;populationMin.IsEnabled=populationMax.IsEnabled=f.Mode!=GalaxyMapMode.Realistic&&(f.Category==GalaxyFilterCategory.Population||f.SizeCategory==GalaxyFilterCategory.Population);
var definition=(GalaxyFilterDefinition)category.SelectedItem;
var originalState=f.Category==GalaxyFilterCategory.State&&f.RequireOriginalStateFacts;
var counts=choiceCounts;var recount=!ReferenceEquals(countedSystems,Systems)||countedCategory!=f.Category||countedCommodityDirection!=f.CommodityDirection||countedOriginalState!=originalState;
if(recount){countedSystems=Systems;countedCategory=f.Category;countedCommodityDirection=f.CommodityDirection;countedOriginalState=originalState;counts.Clear();}
var missing=0;var matching=0;
foreach(var s in Systems)
{var match=f.Evaluate(s);if(match.Unknown)missing++;if(recount&&match.Keys is {} keys)foreach(var key in keys.Distinct())counts[key]=counts.GetValueOrDefault(key)+1;if(match.Matches)matching++;}
coverage.Text=$"{matching:N0} / {Systems.Count:N0} 星系 · {missing:N0} 个未知";
note.Text=f.Mode==GalaxyMapMode.Realistic?"写实模式依恒星光谱显示。选择筛选类别会切换至地图模式。":f.Category==GalaxyFilterCategory.Markers?GalaxyMarkerSources.Describe(f.Values):definition.Availability.Length>0?definition.Availability:"图例颜色对应已记录类别;灰色表示未知数据。";
if(f.UsesPowerTerritory)note.Text="势力与状态按原掩码同时筛选,须有当前游戏元数据。"+(f.OriginalPowerOptions.Count==0?"等待游戏实际势力表;不会用公开列表顺序代替原索引。":"清除势力仍保留原规则允许的无主、无势力旗标星系;要隐藏它们请取消“未占领”状态。")+"半选状态表示原保存值只含组内部分阶段。";
if(f.Mode!=GalaxyMapMode.Realistic&&f.Category==GalaxyFilterCategory.Services)note.Text="前10项为原版服务选项,后续为工具扩展服务。"+note.Text;
if(f.Category==GalaxyFilterCategory.Commodity&&f.OriginalCommodityOptions.Count>0)note.Text=$"原定义源 {f.OriginalCommodityOptions.Count:N0} 项;库存不满足 {f.OriginalCommodityOptions.Count(o=>o.PublisherEligible==false):N0},待确认 {f.OriginalCommodityOptions.Count(o=>o.PublisherEligible is null):N0}。"+(f.UsesOriginalCommodity?"\n单项使用原进口/出口价格分类及无有效价格分支;未知隐藏。":"\n多项或非原定义使用公共站点供需查询;不冒充原区域余额。");
if(f.RequiresOriginalCommodity&&f.CommodityRequest is null)note.Text="原商品选择尚未与实际UID定义绑定;保留原选择,资料未知。不会改查之前的公共商品。";
information.Text=InformationStatus;
importSettings.Command=ImportSettingsCommand;importStatus.Text=OriginalSettingsStatus;
importDetails.Text=OriginalSettingsDetails;
originalRouteFilter.IsChecked=f.OriginalRouteFilters.TryGetValue(f.Category,out var affects)?affects:null;
if(!ReferenceEquals(renderedOriginalOptions,OriginalOptions))
{renderedOriginalOptions=OriginalOptions;originalOptions.Children.Clear();foreach(var entry in OriginalOptions??new Dictionary<string,string>())originalOptions.Children.Add(new TextBlock{Text=entry.Key+" = "+entry.Value,FontSize=10,TextWrapping=TextWrapping.Wrap});}
var extras=counts.Keys.Concat(f.OriginalOptionKeys.GetValueOrDefault(f.Category)??new HashSet<string>()).Concat(f.Values??new HashSet<string>());
var valueChoices=f.Category==GalaxyFilterCategory.PowerActivityEligibility?Array.Empty<GalaxyFilterChoice>():QueryChoices??[];
var entries=definition.Choices.Concat(valueChoices).DistinctBy(x=>x.Key).Concat(extras.Distinct().Where(k=>!definition.Choices.Any(c=>c.Key==k)&&!valueChoices.Any(c=>c.Key==k)).Order().Select(k=>new GalaxyFilterChoice(k,k)));
if(GalaxyStateFilterChoices.FromOriginal(f,counts.Keys) is {} originalStates)entries=originalStates;
if(f.UsesPowerTerritory)entries=f.OriginalPowerOptions.Where(p=>p.Index is >=0 and <128).OrderBy(p=>p.Index).Select(p=>new GalaxyFilterChoice(GalaxyMapFilter.Normalize(p.Name),p.Name));
if(f.Category==GalaxyFilterCategory.Services)
{var order=GalaxyOriginalServiceOptions.Keys.Select((key,index)=>(key,index)).ToDictionary(p=>p.key,p=>p.index);entries=entries.OrderBy(c=>order.GetValueOrDefault(c.Key,int.MaxValue));}
var search=choiceSearch.Text.Trim();var visibleEntries=f.Category==GalaxyFilterCategory.Commodity?[]:entries.Where(x=>search.Length==0||x.Label.Contains(search,StringComparison.OrdinalIgnoreCase)||x.Key.Contains(search,StringComparison.OrdinalIgnoreCase)).Take(160).ToArray();
var signature=f.Category+"|"+string.Join("|",visibleEntries.Select(e=>e.Key+":"+e.Label));
if(signature!=renderedChoices){renderedChoices=signature;choices.Children.Clear();choiceRows.Clear();}
foreach(var entry in visibleEntries)
{
var count=counts.GetValueOrDefault(entry.Key);var selected=f.UsesPowerTerritory?f.TerritoryPowerMask is null||f.OriginalPowerOptions.FirstOrDefault(p=>GalaxyMapFilter.Normalize(p.Name)==entry.Key) is {Index:>=0 and <128} option&&(f.TerritoryPowerMask.Value&(1u<<(option.Index&31)))!=0:f.Values is null||f.Values.Contains(entry.Key);
if(!choiceRows.TryGetValue(entry.Key,out var controls))
{
var row=new CheckBox{HorizontalAlignment=HorizontalAlignment.Stretch};var line=new StackPanel{Orientation=Orientation.Horizontal,Spacing=7};
var text=new TextBlock{FontSize=12,VerticalAlignment=VerticalAlignment.Center};var swatch=new Ellipse{Width=8,Height=8,VerticalAlignment=VerticalAlignment.Center};
line.Children.Add(swatch);line.Children.Add(text);row.Content=line;row.Checked+=(_,_)=>Select(entry.Key,true);row.Unchecked+=(_,_)=>Select(entry.Key,false);
choiceRows[entry.Key]=controls=(row,text,swatch);choices.Children.Add(row);
}
controls.Row.IsChecked=selected;controls.Row.IsEnabled=f.Mode!=GalaxyMapMode.Realistic;controls.Text.Text=entry.Label+" "+count.ToString("N0");
controls.Swatch.Visibility=f.Category is GalaxyFilterCategory.Population or GalaxyFilterCategory.Markers?Visibility.Collapsed:Visibility.Visible;
var colour=f.ColourForValue(entry.Key);
if(controls.Swatch.Fill is SolidColorBrush oldBrush)oldBrush.Color=Color.FromArgb(255,colour.R,colour.G,colour.B);else controls.Swatch.Fill=new SolidColorBrush(Color.FromArgb(255,colour.R,colour.G,colour.B));
AutomationProperties.SetName(controls.Row,entry.Label+","+count+" 个星系");
}
scoop.Visibility=f.Category==GalaxyFilterCategory.StarClass?Visibility.Visible:Visibility.Collapsed;
unknown.IsChecked=f.IncludeUnknown;unknown.IsEnabled=f.Mode!=GalaxyMapMode.Realistic;
grid.IsChecked=f.ShowGrid;gridStalks.IsChecked=f.ShowGridStalks;gridStalks.IsEnabled=f.ShowGrid;reverseMouseCrane.IsChecked=f.ReverseMouseCrane;labels.IsChecked=f.ShowLabels;links.IsChecked=f.ShowLinks;markers.IsChecked=f.ShowMarkers;route.IsChecked=f.ShowRoute;background.IsChecked=f.ShowBackground;
regionNames.IsChecked=f.ShowRegionNames;nebulaNames.IsChecked=f.ShowNebulaNames;constellations.IsChecked=f.ShowConstellations;galacticZones.IsChecked=f.ShowGalacticZonesPlane;galacticZoneNames.IsChecked=f.ShowGalacticZoneNames;
RefreshMarkerVisibilityControls(f);
RefreshCurrentTradeControls(f);
historicalTrade.IsChecked=f.ShowHistoricalTradeFlows;historicalTradeSource.SelectedIndex=(int)f.HistoricalTradeSource;historicalTradeSource.IsEnabled=f.ShowHistoricalTradeFlows;historicalTradeStatus.Text=HistoricalTradeStatus;
RefreshHistoricalCommodityTree(f);
range.Value=f.JumpRange;range.IsEnabled=f.ShowLinks;shipRange.IsEnabled=f.ShowLinks&&ShipJumpRange is >=1 and <=1000;
}
finally{updating=false;}
}
private void RefreshCommodityTree()
{
var available=(QueryChoices??[]).Concat(Filter.Values?.Select(k=>new GalaxyFilterChoice(k,k))??[]).Concat(Systems.SelectMany(s=>GalaxyMapFilter.Keys(s,GalaxyFilterCategory.Commodity,Filter.CommodityDirection)??[]).Select(k=>new GalaxyFilterChoice(k,k)));
currentCommodityGroups=GalaxyCommodityTree.Build(available,originalOptions:Filter.OriginalCommodityOptions);
var search=choiceSearch.Text.Trim();
bool Contains(string value)=>value.Contains(search,StringComparison.OrdinalIgnoreCase);
var shown=currentCommodityGroups.Select(g=>search.Length==0||Contains(g.Label)||Contains(g.Key)?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(commodityTreeSignature!=signature)
{
commodityTreeSignature=signature;commodityGroups.Children.Clear();commodityParents.Clear();commodityLeaves.Clear();
commodityGroups.Children.Add(new TextBlock{Text="按原商品类别分组;分类未读取的商品仍可单独选择。父类选择作用于整类,查找仅改变列表。",FontSize=11,TextWrapping=TextWrapping.Wrap});
foreach(var group in shown)
{
var complete=currentCommodityGroups.First(g=>g.Key==group.Key);
var content=new StackPanel{Spacing=1};var parent=new CheckBox{Content="全选此类",IsThreeState=true};content.Children.Add(parent);commodityParents[group.Key]=(parent,complete);
parent.Click+=(_,_)=>
{
if(updating)return;
Filter=Filter with{OriginalCommodityUid=null,Values=GalaxyCommodityTree.SelectGroup(Filter.Values,currentCommodityGroups.SelectMany(g=>g.Children).Select(c=>c.Key),complete.Children.Select(c=>c.Key),GalaxyCommodityTree.Selected(Filter.Values,complete.Children)!=true)};
};
foreach(var child in group.Children)
{
var check=new CheckBox{Content=child.Label,HorizontalAlignment=HorizontalAlignment.Stretch};commodityLeaves[child.Key]=check;content.Children.Add(check);
check.Checked+=(_,_)=>Select(child.Key,true);check.Unchecked+=(_,_)=>Select(child.Key,false);
}
var expander=new Expander{Header=group.Label+" · "+complete.Children.Count,Content=content,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch,IsExpanded=search.Length>0||commodityExpanded.GetValueOrDefault(group.Key)};
expander.Expanding+=(_,_)=>commodityExpanded[group.Key]=true;expander.Collapsed+=(_,_)=>commodityExpanded[group.Key]=false;commodityGroups.Children.Add(expander);
}
}
foreach(var entry in commodityLeaves)entry.Value.IsChecked=Filter.Values is null||Filter.Values.Contains(entry.Key);
foreach(var entry in commodityParents)entry.Value.Check.IsChecked=GalaxyCommodityTree.Selected(Filter.Values,entry.Value.Group.Children);
}
private void RefreshCommunityGoals()
{
communityGoalStatus.Text=CommunityGoalStatus;
if(ReferenceEquals(renderedCommunityGoals,CommunityGoalTargets)&&ReferenceEquals(renderedCommunityGoalCommand,SelectCommunityGoalCommand))return;
renderedCommunityGoals=CommunityGoalTargets;renderedCommunityGoalCommand=SelectCommunityGoalCommand;communityGoalTargets.Children.Clear();
foreach(var target in CommunityGoalTargets)
{
var content=new StackPanel{Spacing=2};content.Children.Add(new TextBlock{Text=target.Label,FontSize=12,TextWrapping=TextWrapping.Wrap});content.Children.Add(new TextBlock{Text=target.Source+" · 地址 "+target.SystemAddress,FontSize=10,TextWrapping=TextWrapping.Wrap});
var button=new Button{Content=content,Command=SelectCommunityGoalCommand,CommandParameter=target,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch};AutomationProperties.SetName(button,"定位社区活动 "+target.Label);communityGoalTargets.Children.Add(button);
}
}
private void RefreshThargoid()
{
thargoidStatus.Text=ThargoidStatus;
if(!ReferenceEquals(renderedThargoidCounts,ThargoidCounts))
{
renderedThargoidCounts=ThargoidCounts;thargoidCounts.Children.Clear();
foreach(var count in ThargoidCounts.Where(c=>c.State!=38))thargoidCounts.Children.Add(new TextBlock{Text=count.Text,FontSize=12,TextWrapping=TextWrapping.Wrap});
if(ThargoidCounts.FirstOrDefault(c=>c.State==38) is {} pseudo)ToolTipService.SetToolTip(thargoidCounts,"原缓存状态38 · "+pseudo.Text);
}
var commandChanged=!ReferenceEquals(renderedThargoidCommand,SelectThargoidTargetCommand);renderedThargoidCommand=SelectThargoidTargetCommand;
if(commandChanged||!ReferenceEquals(renderedThargoidSignals,ThargoidSignals))
{renderedThargoidSignals=ThargoidSignals;Targets(thargoidSignals,ThargoidSignals);}
if(commandChanged||!ReferenceEquals(renderedThargoidRecommendations,ThargoidRecommendations))
{renderedThargoidRecommendations=ThargoidRecommendations;Targets(thargoidRecommendations,ThargoidRecommendations);}
void Targets(StackPanel panel,IReadOnlyList<GalaxyThargoidTarget> targets)
{
panel.Children.Clear();
if(targets.Count==0){panel.Children.Add(new TextBlock{Text="缓存暂无条目;不代表已确认不存在。",FontSize=11,TextWrapping=TextWrapping.Wrap});return;}
foreach(var target in targets)
{
var label=new StackPanel{Spacing=3};label.Children.Add(new TextBlock{Text=target.Title,TextWrapping=TextWrapping.Wrap,FontSize=12});label.Children.Add(new TextBlock{Text=target.Detail,TextWrapping=TextWrapping.Wrap,FontSize=10});
var button=new Button{Content=label,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch,Command=SelectThargoidTargetCommand,CommandParameter=target};
AutomationProperties.SetName(button,"定位 "+target.Title);panel.Children.Add(button);
}
}
}
private void Select(string key,bool selected)
{
if(updating)return;
if(Filter.UsesPowerTerritory)
{
if(Filter.OriginalPowerOptions.FirstOrDefault(p=>GalaxyMapFilter.Normalize(p.Name)==key) is not {Index:>=0 and <128} power)return;
var mask=Filter.TerritoryPowerMask??Filter.OriginalPowerOptions.Where(p=>p.Index is >=0 and <128).Aggregate(0u,(bits,p)=>bits|(1u<<(p.Index&31)));
var bit=1u<<(power.Index&31);Filter=Filter with{TerritoryPowerMask=selected?mask|bit:mask&~bit,Values=null};return;
}
if(GalaxyStateFilterChoices.FromOriginal(Filter,choiceCounts.Keys) is {} originalStates)
{Filter=Filter with{Values=GalaxyStateFilterChoices.Select(Filter.Values,originalStates,key,selected)};return;}
var definition=GalaxyFilterDefinitions.All.First(x=>x.Category==Filter.Category);
var values=Filter.Values is {} existing?new HashSet<string>(existing):definition.Choices.Select(x=>x.Key).Concat((QueryChoices??[]).Select(x=>x.Key)).Concat(Filter.Category==GalaxyFilterCategory.Commodity?currentCommodityGroups.SelectMany(g=>g.Children).Select(c=>c.Key):[]).Concat(Systems.SelectMany(s=>GalaxyMapFilter.Keys(s,Filter.Category,Filter.CommodityDirection)??[])).ToHashSet();
if(selected)values.Add(key);else values.Remove(key);Filter=Filter with{Values=values,OriginalCommodityUid=Filter.Category==GalaxyFilterCategory.Commodity?null:Filter.OriginalCommodityUid};
}
}
using Microsoft.UI.Xaml.Automation;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Shapes;
using XFEEDToolBox.Core.Galaxy;
using Windows.UI;
using System.Windows.Input;
namespace XFEEDToolBox.Controls;
/// <summary>Shared view/filter controls for both galaxy map entry points.</summary>
public sealed partial class GalaxyFilterPanel:UserControl
{
public static readonly DependencyProperty FilterProperty=DependencyProperty.Register(nameof(Filter),typeof(GalaxyMapFilter),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty SystemsProperty=DependencyProperty.Register(nameof(Systems),typeof(IReadOnlyList<GalacticSystem>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty ShipJumpRangeProperty=DependencyProperty.Register(nameof(ShipJumpRange),typeof(double),typeof(GalaxyFilterPanel),new PropertyMetadata(0d,Changed));
public static readonly DependencyProperty InformationStatusProperty=DependencyProperty.Register(nameof(InformationStatus),typeof(string),typeof(GalaxyFilterPanel),new PropertyMetadata("",Changed));
public static readonly DependencyProperty ImportSettingsCommandProperty=DependencyProperty.Register(nameof(ImportSettingsCommand),typeof(ICommand),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty OriginalSettingsStatusProperty=DependencyProperty.Register(nameof(OriginalSettingsStatus),typeof(string),typeof(GalaxyFilterPanel),new PropertyMetadata("",Changed));
public static readonly DependencyProperty OriginalSettingsDetailsProperty=DependencyProperty.Register(nameof(OriginalSettingsDetails),typeof(string),typeof(GalaxyFilterPanel),new PropertyMetadata("",Changed));
public static readonly DependencyProperty OriginalOptionsProperty=DependencyProperty.Register(nameof(OriginalOptions),typeof(IReadOnlyDictionary<string,string>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty QueryChoicesProperty=DependencyProperty.Register(nameof(QueryChoices),typeof(IReadOnlyList<GalaxyFilterChoice>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty HistoricalTradeChoicesProperty=DependencyProperty.Register(nameof(HistoricalTradeChoices),typeof(IReadOnlyList<GalaxyFilterChoice>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty HistoricalTradeStatusProperty=DependencyProperty.Register(nameof(HistoricalTradeStatus),typeof(string),typeof(GalaxyFilterPanel),new PropertyMetadata("",Changed));
public static readonly DependencyProperty ThargoidCountsProperty=DependencyProperty.Register(nameof(ThargoidCounts),typeof(IReadOnlyList<GalaxyThargoidCountDisplay>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty ThargoidSignalsProperty=DependencyProperty.Register(nameof(ThargoidSignals),typeof(IReadOnlyList<GalaxyThargoidTarget>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty ThargoidRecommendationsProperty=DependencyProperty.Register(nameof(ThargoidRecommendations),typeof(IReadOnlyList<GalaxyThargoidTarget>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty ThargoidStatusProperty=DependencyProperty.Register(nameof(ThargoidStatus),typeof(string),typeof(GalaxyFilterPanel),new PropertyMetadata("",Changed));
public static readonly DependencyProperty SelectThargoidTargetCommandProperty=DependencyProperty.Register(nameof(SelectThargoidTargetCommand),typeof(ICommand),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty LocatePowerHeadquartersCommandProperty=DependencyProperty.Register(nameof(LocatePowerHeadquartersCommand),typeof(ICommand),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty CommunityGoalTargetsProperty=DependencyProperty.Register(nameof(CommunityGoalTargets),typeof(IReadOnlyList<GalaxyAccountMapTarget>),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public static readonly DependencyProperty CommunityGoalStatusProperty=DependencyProperty.Register(nameof(CommunityGoalStatus),typeof(string),typeof(GalaxyFilterPanel),new PropertyMetadata("",Changed));
public static readonly DependencyProperty SelectCommunityGoalCommandProperty=DependencyProperty.Register(nameof(SelectCommunityGoalCommand),typeof(ICommand),typeof(GalaxyFilterPanel),new PropertyMetadata(null,Changed));
public GalaxyMapFilter Filter {get=>(GalaxyMapFilter?)GetValue(FilterProperty)??GalaxyMapFilter.Default;set=>SetValue(FilterProperty,value);}
public IReadOnlyList<GalacticSystem> Systems {get=>(IReadOnlyList<GalacticSystem>?)GetValue(SystemsProperty)??[];set=>SetValue(SystemsProperty,value);}
public double ShipJumpRange {get=>(double)GetValue(ShipJumpRangeProperty);set=>SetValue(ShipJumpRangeProperty,value);}
public string InformationStatus {get=>(string)GetValue(InformationStatusProperty);set=>SetValue(InformationStatusProperty,value);}
public ICommand? ImportSettingsCommand {get=>(ICommand?)GetValue(ImportSettingsCommandProperty);set=>SetValue(ImportSettingsCommandProperty,value);}
public string OriginalSettingsStatus {get=>(string)GetValue(OriginalSettingsStatusProperty);set=>SetValue(OriginalSettingsStatusProperty,value);}
public string OriginalSettingsDetails {get=>(string)GetValue(OriginalSettingsDetailsProperty);set=>SetValue(OriginalSettingsDetailsProperty,value);}
public IReadOnlyDictionary<string,string>? OriginalOptions {get=>(IReadOnlyDictionary<string,string>?)GetValue(OriginalOptionsProperty);set=>SetValue(OriginalOptionsProperty,value);}
public IReadOnlyList<GalaxyFilterChoice>? QueryChoices {get=>(IReadOnlyList<GalaxyFilterChoice>?)GetValue(QueryChoicesProperty);set=>SetValue(QueryChoicesProperty,value);}
public IReadOnlyList<GalaxyFilterChoice> HistoricalTradeChoices {get=>(IReadOnlyList<GalaxyFilterChoice>?)GetValue(HistoricalTradeChoicesProperty)??[];set=>SetValue(HistoricalTradeChoicesProperty,value);}
public string HistoricalTradeStatus {get=>(string)GetValue(HistoricalTradeStatusProperty);set=>SetValue(HistoricalTradeStatusProperty,value);}
public IReadOnlyList<GalaxyThargoidCountDisplay> ThargoidCounts {get=>(IReadOnlyList<GalaxyThargoidCountDisplay>?)GetValue(ThargoidCountsProperty)??[];set=>SetValue(ThargoidCountsProperty,value);}
public IReadOnlyList<GalaxyThargoidTarget> ThargoidSignals {get=>(IReadOnlyList<GalaxyThargoidTarget>?)GetValue(ThargoidSignalsProperty)??[];set=>SetValue(ThargoidSignalsProperty,value);}
public IReadOnlyList<GalaxyThargoidTarget> ThargoidRecommendations {get=>(IReadOnlyList<GalaxyThargoidTarget>?)GetValue(ThargoidRecommendationsProperty)??[];set=>SetValue(ThargoidRecommendationsProperty,value);}
public string ThargoidStatus {get=>(string)GetValue(ThargoidStatusProperty);set=>SetValue(ThargoidStatusProperty,value);}
public ICommand? SelectThargoidTargetCommand {get=>(ICommand?)GetValue(SelectThargoidTargetCommandProperty);set=>SetValue(SelectThargoidTargetCommandProperty,value);}
public ICommand? LocatePowerHeadquartersCommand {get=>(ICommand?)GetValue(LocatePowerHeadquartersCommandProperty);set=>SetValue(LocatePowerHeadquartersCommandProperty,value);}
public IReadOnlyList<GalaxyAccountMapTarget> CommunityGoalTargets {get=>(IReadOnlyList<GalaxyAccountMapTarget>?)GetValue(CommunityGoalTargetsProperty)??[];set=>SetValue(CommunityGoalTargetsProperty,value);}
public string CommunityGoalStatus {get=>(string)GetValue(CommunityGoalStatusProperty);set=>SetValue(CommunityGoalStatusProperty,value);}
public ICommand? SelectCommunityGoalCommand {get=>(ICommand?)GetValue(SelectCommunityGoalCommandProperty);set=>SetValue(SelectCommunityGoalCommandProperty,value);}
private bool updating;
private readonly ComboBox mode=new(){Header="视图模式",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly ComboBox category=new(){Header="星系筛选",HorizontalAlignment=HorizontalAlignment.Stretch,DisplayMemberPath="Label",ItemsSource=GalaxyFilterDefinitions.All};
private readonly ComboBox size=new(){Header="恒星大小",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly ComboBox commodityDirection=new(){Header="商品流向",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly ComboBox powerplayView=new(){Header="势力博弈视图",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly ComboBox activityPower=new(){Header="活动势力范围",PlaceholderText="选择实际势力或全部势力",DisplayMemberPath="Label",HorizontalAlignment=HorizontalAlignment.Stretch};
private string activityChoicesSignature="";
private IReadOnlyList<GalaxyFilterChoice> activityChoices=[];
private readonly ComboBox activityType=new(){Header="活动类型",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly CheckBox useMinimumActivity=new(){Content="限制最低近期活动强度"};
private readonly CheckBox territoryActivityEnabled=new(){Content="启用领土活动显示与条件"};
private readonly Slider minimumActivity=new(){Minimum=0,Maximum=1,StepFrequency=.01,TickFrequency=.25,Header="最低近期活动强度",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly StackPanel activityControls=new(){Spacing=5};
private readonly TextBlock activityNote=new(){Text="领土附加条件:仅启用最低强度时按所选势力/活动的原近期计数过滤;计数0有效,缺缓存保持未知。",FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly Button powerHeadquarters=new(){Content="定位所选势力总部",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly Expander thargoidPanel=new(){Header="原游戏信号与推荐星系",HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch,IsExpanded=true};
private readonly StackPanel thargoidCounts=new(){Spacing=3},thargoidSignals=new(){Spacing=5},thargoidRecommendations=new(){Spacing=5};
private readonly TextBlock thargoidStatus=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private IReadOnlyList<GalaxyThargoidCountDisplay>? renderedThargoidCounts;
private IReadOnlyList<GalaxyThargoidTarget>? renderedThargoidSignals,renderedThargoidRecommendations;
private ICommand? renderedThargoidCommand;
private readonly StackPanel communityGoalTargets=new(){Spacing=5};
private readonly TextBlock communityGoalStatus=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private IReadOnlyList<GalaxyAccountMapTarget>? renderedCommunityGoals;
private ICommand? renderedCommunityGoalCommand;
private readonly StackPanel choices=new(){Spacing=0};
private readonly StackPanel commodityGroups=new(){Spacing=5};
private readonly Dictionary<string,CheckBox> commodityLeaves=new(StringComparer.Ordinal);
private readonly Dictionary<string,(CheckBox Check,GalaxyCommodityGroup Group)> commodityParents=new(StringComparer.Ordinal);
private readonly Dictionary<string,bool> commodityExpanded=new(StringComparer.Ordinal);
private string commodityTreeSignature="";
private IReadOnlyList<GalaxyCommodityGroup> currentCommodityGroups=[];
private readonly StackPanel selectionActions=new(){Orientation=Orientation.Horizontal,Spacing=8};
private readonly Expander powerStates=new(){Header="领土状态(与所选势力同时满足)",HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch};
private readonly Dictionary<string,CheckBox> powerStateChecks=new(StringComparer.Ordinal);
private readonly TextBox choiceSearch=new(){PlaceholderText="查找筛选值(中文或原名)"};
private readonly TextBlock coverage=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly StackPanel populationLegend=new(){Spacing=3};
private readonly TextBlock populationLegendText=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly TextBlock note=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly TextBlock information=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly CheckBox unknown=new(){Content="显示未知数据的星系"};
private readonly CheckBox grid=new(){Content="星图网格"},gridStalks=new(){Content="星系到网格的投影线(工具选项)"},labels=new(){Content="恒星名称"},links=new(){Content="显示跳跃连接线"},markers=new(){Content="地点与工程师标记"},route=new(){Content="导航标记与航线"},background=new(){Content="银河与星云背景"};
private readonly CheckBox reverseMouseCrane=new(){Content="反转鼠标升降(工具选项)"};
private readonly CheckBox regionNames=new(){Content="原游戏区域名称"},nebulaNames=new(){Content="星云名称"},constellations=new(){Content="星座连线"},galacticZones=new(){Content="银河扇区平面"},galacticZoneNames=new(){Content="银河扇区名称"};
private readonly CheckBox historicalTrade=new(){Content="显示缓存历史贸易流(非实时)"};
private readonly ComboBox historicalTradeSource=new(){Header="历史流量来源",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly TextBox historicalTradeSearch=new(){PlaceholderText="查找缓存中的商品"};
private readonly TextBlock historicalTradeStatus=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly StackPanel historicalTradeCommodities=new(){Spacing=0};
private readonly Dictionary<string,CheckBox> historicalCommodityChecks=new(StringComparer.Ordinal);
private string historicalCommoditySignature="";
private readonly NumberBox range=new(){Header="跳跃连接范围 / ly",Minimum=1,Maximum=1000,SmallChange=1,SpinButtonPlacementMode=NumberBoxSpinButtonPlacementMode.Compact};
private readonly NumberBox populationMin=new(){Header="人口下限(人)",Minimum=0,Maximum=9_000_000_000_000_000};
private readonly NumberBox populationMax=new(){Header="人口满值(人)",Minimum=0,Maximum=9_000_000_000_000_000};
private readonly Button shipRange=new(){Content="采用当前飞船最大跳距",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly Button scoop=new(){Content="仅可吸油 O B A F G K M"};
private readonly Button importSettings=new(){Content="读取游戏星图配置",HorizontalAlignment=HorizontalAlignment.Stretch};
private readonly TextBlock importStatus=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly StackPanel originalOptions=new(){Spacing=5};
private readonly TextBlock importDetails=new(){FontSize=11,TextWrapping=TextWrapping.Wrap};
private readonly CheckBox originalRouteFilter=new(){Content="将筛选应用于路线(游戏保存值)",IsEnabled=false,IsThreeState=true};
private IReadOnlyDictionary<string,string>? renderedOriginalOptions;
private readonly Dictionary<string,(CheckBox Row,TextBlock Text,Ellipse Swatch)> choiceRows=new(StringComparer.Ordinal);
private string renderedChoices="";
private IReadOnlyList<GalacticSystem>? countedSystems;
private GalaxyFilterCategory countedCategory=(GalaxyFilterCategory)(-1);
private bool countedOriginalState;
private GalaxyCommodityDirection countedCommodityDirection;
private readonly Dictionary<string,int> choiceCounts=new(StringComparer.Ordinal);
public GalaxyFilterPanel()
{
InstallStaticCheckBoxTemplate();
Width=300;HorizontalContentAlignment=HorizontalAlignment.Stretch;
var muted=(Brush)Application.Current.Resources["MutedBrush"];coverage.Foreground=note.Foreground=information.Foreground=muted;
constellations.Content="原游戏星座连线";
ToolTipService.SetToolTip(constellations,"原资源16组、206条实际连线;整组全部端点均真实已载入且通过当前查询才绘制完整组,缺任一端点整组等待。");
mode.Items.Add(new ComboBoxItem{Content="写实模式",Tag=GalaxyMapMode.Realistic});mode.Items.Add(new ComboBoxItem{Content="地图模式",Tag=GalaxyMapMode.Map});
foreach(var (name,value) in new[]{("势力博弈",GalaxyMapMode.Powerplay),("Thargoid 战争模式",GalaxyMapMode.ThargoidWar),("星系殖民模式",GalaxyMapMode.Colonisation)})
{var item=new ComboBoxItem{Content=name,Tag=value};mode.Items.Add(item);}
var root=new StackPanel{Spacing=8};root.Children.Add(new TextBlock{Text="银河系星图 · 视图",FontSize=18,FontWeight=Microsoft.UI.Text.FontWeights.SemiBold});
AutomationProperties.SetName(importSettings,"读取游戏星图配置");importStatus.Foreground=muted;
// Original3A3D910 publishes exactly these three radio UIDs/labels.
// Saved mode3 is accepted by the predicate but displays the first item.
commodityDirection.Items.Add(new ComboBoxItem{Content="进口/出口价格",Tag=GalaxyCommodityDirection.Both});commodityDirection.Items.Add(new ComboBoxItem{Content="进口价格",Tag=GalaxyCommodityDirection.Imports});commodityDirection.Items.Add(new ComboBoxItem{Content="出口价格",Tag=GalaxyCommodityDirection.Exports});
powerplayView.Items.Add(new ComboBoxItem{Content="领土:势力与状态",Tag=GalaxyPowerplayView.Territory});powerplayView.Items.Add(new ComboBoxItem{Content="活动:原范围与星系条件",Tag=GalaxyPowerplayView.Activities});
foreach(var entry in new[]{("暗中破坏",GalaxyPowerActivity.Undermining),("巩固中",GalaxyPowerActivity.Reinforcing),("正在获取",GalaxyPowerActivity.Acquiring),("抵抗破坏",GalaxyPowerActivity.BeingUndermined),("总计",GalaxyPowerActivity.Total)})activityType.Items.Add(new ComboBoxItem{Content=entry.Item1,Tag=entry.Item2});
activityControls.Children.Add(territoryActivityEnabled);activityControls.Children.Add(activityPower);activityControls.Children.Add(activityType);activityControls.Children.Add(useMinimumActivity);activityControls.Children.Add(minimumActivity);
activityNote.Foreground=muted;activityControls.Children.Add(activityNote);savedActivityPowerStatus.Foreground=muted;activityControls.Children.Add(savedActivityPowerStatus);
root.Children.Add(mode);root.Children.Add(powerplayView);root.Children.Add(category);root.Children.Add(commodityDirection);root.Children.Add(activityControls);root.Children.Add(powerHeadquarters);root.Children.Add(coverage);
AutomationProperties.SetName(powerHeadquarters,"定位所选势力总部");ToolTipService.SetToolTip(powerHeadquarters,"选择一个实际势力,读取其原定义中的总部地址并定位;全部势力或未知总部时不可用。");
var thargoidContent=new StackPanel{Spacing=7};thargoidContent.Children.Add(thargoidStatus);thargoidContent.Children.Add(thargoidCounts);
thargoidContent.Children.Add(new TextBlock{Text="原信号",FontWeight=Microsoft.UI.Text.FontWeights.SemiBold});thargoidContent.Children.Add(new ScrollViewer{Content=thargoidSignals,MaxHeight=220,VerticalScrollBarVisibility=ScrollBarVisibility.Auto});
thargoidContent.Children.Add(new TextBlock{Text="原推荐星系(每组至多5项)",FontWeight=Microsoft.UI.Text.FontWeights.SemiBold});thargoidContent.Children.Add(new ScrollViewer{Content=thargoidRecommendations,MaxHeight=300,VerticalScrollBarVisibility=ScrollBarVisibility.Auto});
thargoidContent.Children.Add(new TextBlock{Text="点击按真实地址解析并定位。仅显示原游戏已有缓存,不发起原网络更新;列表长度不代替战争状态总数。",FontSize=11,TextWrapping=TextWrapping.Wrap,Foreground=muted});
thargoidPanel.Content=thargoidContent;root.Children.Add(thargoidPanel);
var populationRamp=new LinearGradientBrush{StartPoint=new(0,.5),EndPoint=new(1,.5)};
var low=GalaxyMapFilter.PopulationLowColour;var high=GalaxyMapFilter.PopulationHighColour;
populationRamp.GradientStops.Add(new GradientStop{Offset=0,Color=Color.FromArgb(255,low.R,low.G,low.B)});populationRamp.GradientStops.Add(new GradientStop{Offset=1,Color=Color.FromArgb(255,high.R,high.G,high.B)});
populationLegend.Children.Add(new Rectangle{Height=6,Fill=populationRamp,HorizontalAlignment=HorizontalAlignment.Stretch});populationLegend.Children.Add(populationLegendText);root.Children.Add(populationLegend);
var all=new Button{Content="全选",FontSize=12};var none=new Button{Content="清除",FontSize=12};
scoop.Content="仅可吸油";scoop.FontSize=12;ToolTipService.SetToolTip(scoop,"仅显示可吸油的 O、B、A、F、G、K、M 型恒星");
selectionActions.Children.Add(all);selectionActions.Children.Add(none);selectionActions.Children.Add(scoop);root.Children.Add(selectionActions);
root.Children.Add(choiceSearch);root.Children.Add(new ScrollViewer{Content=choices,MaxHeight=238,VerticalScrollBarVisibility=ScrollBarVisibility.Auto,HorizontalScrollBarVisibility=ScrollBarVisibility.Disabled});root.Children.Add(unknown);
root.Children.Add(commodityGroups);
var powerStateContent=new StackPanel{Spacing=2};var stateAll=new Button{Content="全部状态",HorizontalAlignment=HorizontalAlignment.Stretch};powerStateContent.Children.Add(stateAll);stateAll.Click+=(_,_)=>Filter=Filter with{TerritoryStateMask=null,PowerStateValues=null};
foreach(var state in GalaxyFilterDefinitions.All.First(d=>d.Category==GalaxyFilterCategory.PowerState).Choices)
{
var check=new CheckBox{Content=state.Label,IsThreeState=true};powerStateChecks[state.Key]=check;
void SelectState(object sender,RoutedEventArgs e)
{if(updating)return;Filter=Filter with{TerritoryStateMask=GalaxyOriginalPowerFilters.SetStateGroup(Filter.TerritoryStateMask,state.Key,check.IsChecked==true),PowerStateValues=null};}
check.Checked+=SelectState;check.Unchecked+=SelectState;check.Indeterminate+=SelectState;powerStateContent.Children.Add(check);
}
powerStates.Content=powerStateContent;root.Children.Add(powerStates);
var dataNotes=new StackPanel{Spacing=6};dataNotes.Children.Add(note);dataNotes.Children.Add(information);dataNotes.Children.Add(new TextBlock{Text="移动或缩放后按当前视野重新查询;未匹配星系隐藏。",FontSize=11,TextWrapping=TextWrapping.Wrap,Foreground=muted});
root.Children.Add(new Expander{Header="资料与覆盖说明",Content=dataNotes,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch});
InitializeSizeOptions();
var display=new StackPanel{Spacing=2};display.Children.Add(size);display.Children.Add(populationMin);display.Children.Add(populationMax);foreach(var toggle in new[]{grid,gridStalks,labels,regionNames,nebulaNames,constellations,galacticZones,galacticZoneNames,links,markers,route,background,reverseMouseCrane})display.Children.Add(toggle);display.Children.Add(range);display.Children.Add(shipRange);
display.Children.Add(new TextBlock{Text="名称、星座和扇区图层独立叠加;不会把区域或星云当作恒星筛选。各图层仅绘制已核验的原资源。",FontSize=11,TextWrapping=TextWrapping.Wrap,Foreground=muted});
display.Children.Add(new TextBlock{Text="连线以选中或当前星系为起点,受实际光年距离限制。范围为显示参数,不等于已规划航路。",FontSize=11,TextWrapping=TextWrapping.Wrap,Foreground=muted});
root.Children.Add(new Expander{Header="显示控制",Content=display,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch});
root.Children.Add(BuildCurrentTradeControls());
foreach(var entry in new[]{("全部缓存流量",GalaxyTradeFlowSource.All),("玩家缓存流量",GalaxyTradeFlowSource.Players),("NPC 缓存流量",GalaxyTradeFlowSource.Npc)})historicalTradeSource.Items.Add(new ComboBoxItem{Content=entry.Item1,Tag=entry.Item2});
var history=new StackPanel{Spacing=5};history.Children.Add(historicalTrade);history.Children.Add(historicalTradeSource);history.Children.Add(historicalTradeSearch);
var historyAll=new Button{Content="全部商品"};var historyNone=new Button{Content="清除商品"};var historyActions=new StackPanel{Orientation=Orientation.Horizontal,Spacing=7};historyActions.Children.Add(historyAll);historyActions.Children.Add(historyNone);history.Children.Add(historyActions);
history.Children.Add(new ScrollViewer{Content=historicalTradeCommodities,MaxHeight=180,VerticalScrollBarVisibility=ScrollBarVisibility.Auto});history.Children.Add(historicalTradeStatus);
history.Children.Add(new TextBlock{Text="按原贸易流13类目录分组,仅列缓存实际商品;原目录以外的商品分类保持未知。父类选择作用于整类,查找仅改变列表。",FontSize=11,TextWrapping=TextWrapping.Wrap,Foreground=muted});
history.Children.Add(new TextBlock{Text="来自当前账号原TRD缓存的带时间贸易记录;不会代替游戏实时有效期。只绘制真实地址和坐标已确认的端点。",FontSize=11,TextWrapping=TextWrapping.Wrap,Foreground=muted});
root.Children.Add(new Expander{Header="缓存历史贸易流",Content=history,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch});
historicalTrade.Checked+=(_,_)=>{if(!updating)Filter=Filter with{ShowHistoricalTradeFlows=true};};historicalTrade.Unchecked+=(_,_)=>{if(!updating)Filter=Filter with{ShowHistoricalTradeFlows=false};};
historicalTradeSource.SelectionChanged+=(_,_)=>{if(!updating&&historicalTradeSource.SelectedItem is ComboBoxItem{Tag:GalaxyTradeFlowSource source})Filter=Filter with{HistoricalTradeSource=source};};
historyAll.Click+=(_,_)=>Filter=Filter with{HistoricalTradeCommodities=null};historyNone.Click+=(_,_)=>Filter=Filter with{HistoricalTradeCommodities=new HashSet<string>()};historicalTradeSearch.TextChanged+=(_,_)=>Refresh();
root.Children.Add(importSettings);root.Children.Add(importStatus);
root.Children.Add(BuildFleetControls());
root.Children.Add(BuildMissionControls());
var layers=new StackPanel{Spacing=3};layers.Children.Add(new TextBlock{Text="选择标记后查询当前视野;资料说明会列出实际来源与待确认的覆盖。",FontSize=11,Foreground=muted,TextWrapping=TextWrapping.Wrap});
foreach(var entry in GalaxyFilterDefinitions.All.First(d=>d.Category==GalaxyFilterCategory.Markers).Choices)
{
var button=new Button{Content=entry.Label,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Left};
AutomationProperties.SetName(button,"查询"+entry.Label);button.Click+=(_,_)=>Filter=Filter.WithCategory(GalaxyFilterCategory.Markers) with{Values=new HashSet<string>{entry.Key},IncludeUnknown=false};layers.Children.Add(button);
}
root.Children.Add(new Expander{Header="原游戏标记查询",Content=new ScrollViewer{Content=layers,MaxHeight=260,VerticalScrollBarVisibility=ScrollBarVisibility.Auto},HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch});
var goals=new StackPanel{Spacing=6};goals.Children.Add(communityGoalStatus);goals.Children.Add(new ScrollViewer{Content=communityGoalTargets,MaxHeight=250,VerticalScrollBarVisibility=ScrollBarVisibility.Auto});
root.Children.Add(new Expander{Header="社区活动 · 原发布缓存",Content=goals,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch});
ToolTipService.SetToolTip(originalRouteFilter,"显示原游戏保存的此类别开关。恒星类别的路线设置可在航行偏好中更改,长按目标规划时应用。");root.Children.Add(originalRouteFilter);
var originalContent=new StackPanel{Spacing=7};originalContent.Children.Add(importDetails);originalContent.Children.Add(originalOptions);
root.Children.Add(new Expander{Header="原游戏配置值与导入说明(只读)",Content=new ScrollViewer{Content=originalContent,MaxHeight=220,HorizontalScrollBarVisibility=ScrollBarVisibility.Disabled,VerticalScrollBarVisibility=ScrollBarVisibility.Auto},HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch});
root.Children.Add(new TextBlock{Text="筛选即时作用于本工具星图;游戏规划的航路使用游戏内筛选设置。",Foreground=muted,FontSize=11,TextWrapping=TextWrapping.Wrap});
var reset=new Button{Content="恢复默认视图",HorizontalAlignment=HorizontalAlignment.Stretch};root.Children.Add(reset);Content=root;
mode.SelectionChanged+=(_,_)=>{if(!updating&&mode.SelectedItem is ComboBoxItem{Tag:GalaxyMapMode selected})Filter=selected switch{GalaxyMapMode.Powerplay=>Filter.WithCategory(GalaxyFilterCategory.Power) with{Mode=selected,Values=null,PowerStateValues=null},GalaxyMapMode.ThargoidWar=>Filter.WithCategory(GalaxyFilterCategory.ThargoidWar) with{Mode=selected},GalaxyMapMode.Colonisation=>Filter.WithCategory(GalaxyFilterCategory.Colonisation) with{Mode=selected},_=>Filter with{Mode=selected}};};
category.SelectionChanged+=(_,_)=>{if(!updating&&category.SelectedItem is GalaxyFilterDefinition selected)Filter=selected.Category==GalaxyFilterCategory.PowerActivityEligibility?Filter.WithCategory(selected.Category) with{Mode=GalaxyMapMode.Powerplay,PowerplayView=GalaxyPowerplayView.Territory}:Filter.WithCategory(selected.Category);};
powerplayView.SelectionChanged+=(_,_)=>{if(!updating&&powerplayView.SelectedItem is ComboBoxItem{Tag:GalaxyPowerplayView selected})Filter=Filter.WithCategory(GalaxyFilterCategory.Power) with{Mode=GalaxyMapMode.Powerplay,PowerplayView=selected,Values=null,PowerStateValues=null};};
activityPower.SelectionChanged+=(_,_)=>{if(!updating&&activityPower.SelectedItem is GalaxyFilterChoice selected)Filter=Filter with{ActivityAllPowers=selected.Key=="__allpowers",ActivityPowerName=selected.Key=="__allpowers"?null:selected.Label,ActivityInternalPowerId=selected.Key=="__allpowers"?null:Filter.OriginalPowerOptions.FirstOrDefault(p=>GalaxyPowerActivities.ChoiceKey(p.InternalPowerId)==selected.Key)?.InternalPowerId,OriginalActivityPowerIndex=null};};
activityType.SelectionChanged+=(_,_)=>{if(!updating&&activityType.SelectedItem is ComboBoxItem{Tag:GalaxyPowerActivity selected})Filter=Filter with{Activity=selected};};
useMinimumActivity.Checked+=(_,_)=>{if(!updating)Filter=Filter with{UseMinimumActivity=true};};useMinimumActivity.Unchecked+=(_,_)=>{if(!updating)Filter=Filter with{UseMinimumActivity=false};};
territoryActivityEnabled.Checked+=(_,_)=>{if(!updating)Filter=Filter with{TerritoryActivityFilterEnabled=true};};territoryActivityEnabled.Unchecked+=(_,_)=>{if(!updating)Filter=Filter with{TerritoryActivityFilterEnabled=false};};
minimumActivity.ValueChanged+=(_,e)=>{if(!updating)Filter=Filter with{MinimumActivity=e.NewValue};};
size.SelectionChanged+=(_,_)=>{if(!updating&&size.SelectedItem is ComboBoxItem{Tag:GalaxyFilterCategory selected})Filter=Filter with{SizeCategory=selected};};
commodityDirection.SelectionChanged+=(_,_)=>{if(!updating&&commodityDirection.SelectedItem is ComboBoxItem{Tag:GalaxyCommodityDirection selected})Filter=Filter with{CommodityDirection=selected,OriginalCommodityPriceMode=commodityDirection.SelectedIndex};};
void PopulationRangeChanged(NumberBox sender,NumberBoxValueChangedEventArgs args)
{if(!updating&&double.IsFinite(populationMin.Value)&&double.IsFinite(populationMax.Value)&&populationMin.Value>=0&&populationMax.Value>=populationMin.Value)Filter=Filter with{PopulationMinimum=populationMin.Value,PopulationMaximum=populationMax.Value};}
populationMin.ValueChanged+=PopulationRangeChanged;populationMax.ValueChanged+=PopulationRangeChanged;
all.Click+=(_,_)=>Filter=Filter with{Mode=Filter.Mode==GalaxyMapMode.Realistic?GalaxyMapMode.Map:Filter.Mode,TerritoryPowerMask=Filter.UsesPowerTerritory?null:Filter.TerritoryPowerMask,OriginalCommodityUid=Filter.Category==GalaxyFilterCategory.Commodity?null:Filter.OriginalCommodityUid,Values=null,IncludeUnknown=false};
none.Click+=(_,_)=>Filter=Filter with{Mode=Filter.Mode==GalaxyMapMode.Realistic?GalaxyMapMode.Map:Filter.Mode,TerritoryPowerMask=Filter.UsesPowerTerritory?0:Filter.TerritoryPowerMask,OriginalCommodityUid=Filter.Category==GalaxyFilterCategory.Commodity?null:Filter.OriginalCommodityUid,Values=Filter.UsesPowerTerritory?null:new HashSet<string>(),IncludeUnknown=false};
scoop.Click+=(_,_)=>Filter=Filter.WithCategory(GalaxyFilterCategory.StarClass) with{Values=new HashSet<string>(GalaxyFilterDefinitions.ScoopableStarClasses),IncludeUnknown=false};
foreach(var toggle in new[]{grid,gridStalks,labels,regionNames,nebulaNames,constellations,galacticZones,galacticZoneNames,links,markers,route,background,reverseMouseCrane,unknown}){toggle.Checked+=ToggleChanged;toggle.Unchecked+=ToggleChanged;}
range.ValueChanged+=(_,e)=>{if(!updating&&double.IsFinite(e.NewValue)&&e.NewValue is >=1 and <=1000)Filter=Filter with{JumpRange=e.NewValue};};
shipRange.Click+=(_,_)=>{if(ShipJumpRange is >=1 and <=1000)Filter=Filter with{JumpRange=ShipJumpRange};};
reset.Click+=(_,_)=>Filter=GalaxyMapFilter.Default;
choiceSearch.TextChanged+=(_,_)=>Refresh();
root.Children.Add(BuildMarkerVisibilityControls());
Loaded+=(_,_)=>Refresh();Refresh();
}
private static void Changed(DependencyObject sender,DependencyPropertyChangedEventArgs e)=>((GalaxyFilterPanel)sender).Refresh();
private void ToggleChanged(object sender,RoutedEventArgs e)
{
if(updating)return;Filter=Filter with{IncludeUnknown=unknown.IsChecked==true,ShowGrid=grid.IsChecked==true,ShowGridStalks=gridStalks.IsChecked==true,ReverseMouseCrane=reverseMouseCrane.IsChecked==true,ShowLinks=links.IsChecked==true,ShowLabels=labels.IsChecked==true,ShowRegionNames=regionNames.IsChecked==true,ShowNebulaNames=nebulaNames.IsChecked==true,ShowConstellations=constellations.IsChecked==true,ShowGalacticZonesPlane=galacticZones.IsChecked==true,ShowGalacticZoneNames=galacticZoneNames.IsChecked==true,ShowMarkers=markers.IsChecked==true,ShowRoute=route.IsChecked==true,ShowBackground=background.IsChecked==true};
}
private void Refresh()
{
if(updating)return;updating=true;
try
{
var f=Filter;mode.SelectedIndex=(int)f.Mode;category.SelectedItem=GalaxyFilterDefinitions.All.First(x=>x.Category==f.Category);
populationLegend.Visibility=f.Mode!=GalaxyMapMode.Realistic&&f.Category==GalaxyFilterCategory.Population?Visibility.Visible:Visibility.Collapsed;
populationLegendText.Text=$"{f.PopulationMinimum:N0} — {f.PopulationMaximum:N0} 人 · 线性配色";
powerStates.Visibility=f.Mode==GalaxyMapMode.Powerplay&&!f.UsesPowerOuter&&f.Category==GalaxyFilterCategory.Power?Visibility.Visible:Visibility.Collapsed;
foreach(var check in powerStateChecks)check.Value.IsChecked=f.UsesPowerTerritory?GalaxyOriginalPowerFilters.StateSelected(f.TerritoryStateMask,check.Key):f.PowerStateValues is null||f.PowerStateValues.Contains(check.Key);
RefreshSizeSelection(f);
commodityDirection.Visibility=f.Category==GalaxyFilterCategory.Commodity||f.SizeCategory==GalaxyFilterCategory.Commodity?Visibility.Visible:Visibility.Collapsed;commodityDirection.SelectedIndex=f.CommodityPriceMode is 1 or 2?f.CommodityPriceMode:0;
powerplayView.Visibility=f.Mode==GalaxyMapMode.Powerplay?Visibility.Visible:Visibility.Collapsed;powerplayView.SelectedIndex=(int)f.PowerplayView;
activityControls.Visibility=f.Mode==GalaxyMapMode.Powerplay&&f.Category is GalaxyFilterCategory.Power or GalaxyFilterCategory.PowerActivityEligibility?Visibility.Visible:Visibility.Collapsed;
activityPower.Header=f.UsesPowerOuter?"势力(总部定位)":"活动势力范围";
activityType.Visibility=useMinimumActivity.Visibility=minimumActivity.Visibility=activityNote.Visibility=f.UsesPowerOuter?Visibility.Collapsed:Visibility.Visible;
territoryActivityEnabled.Visibility=f.UsesPowerOuter||f.Category==GalaxyFilterCategory.PowerActivityEligibility?Visibility.Collapsed:Visibility.Visible;territoryActivityEnabled.IsChecked=f.TerritoryActivityFilterEnabled;
activityType.IsEnabled=useMinimumActivity.IsEnabled=f.UsesPowerActivityCounter;
category.Visibility=f.UsesPowerOuter?Visibility.Collapsed:Visibility.Visible;
powerHeadquarters.Visibility=f.Mode==GalaxyMapMode.Powerplay?Visibility.Visible:Visibility.Collapsed;powerHeadquarters.Command=LocatePowerHeadquartersCommand;
thargoidPanel.Visibility=f.Mode==GalaxyMapMode.ThargoidWar||f.Category==GalaxyFilterCategory.ThargoidWar?Visibility.Visible:Visibility.Collapsed;
RefreshThargoid();
RefreshCommunityGoals();
RefreshFleetControls();
RefreshMissionControls();
selectionActions.Visibility=choices.Visibility=choiceSearch.Visibility=f.UsesPowerOuter||f.Category==GalaxyFilterCategory.PowerActivityEligibility?Visibility.Collapsed:Visibility.Visible;
commodityGroups.Visibility=f.Category==GalaxyFilterCategory.Commodity?Visibility.Visible:Visibility.Collapsed;
if(f.Category==GalaxyFilterCategory.Commodity){choices.Visibility=Visibility.Collapsed;RefreshCommodityTree();}
var powerChoices=new[]{new GalaxyFilterChoice("__allpowers","全部势力(原活动选项)")}.Concat(f.OriginalPowerOptions.Select(p=>new GalaxyFilterChoice(GalaxyPowerActivities.ChoiceKey(p.InternalPowerId),p.Name))).ToArray();
var powerSignature=string.Join('|',powerChoices.Select(p=>p.Key+":"+p.Label));if(activityChoicesSignature!=powerSignature){activityChoicesSignature=powerSignature;activityChoices=powerChoices;activityPower.ItemsSource=activityChoices;}
activityPower.SelectedItem=activityChoices.FirstOrDefault(p=>f.ActivityAllPowers?p.Key=="__allpowers":f.ActivityInternalPowerId is {} powerId?p.Key==GalaxyPowerActivities.ChoiceKey(powerId):string.Equals(p.Label,f.ActivityPowerName,StringComparison.OrdinalIgnoreCase));activityType.SelectedIndex=(int)f.Activity;
RefreshSavedActivityPower();
useMinimumActivity.IsChecked=f.UseMinimumActivity;minimumActivity.Value=f.MinimumActivity;minimumActivity.IsEnabled=f.UsesPowerActivityCounter&&f.UseMinimumActivity;minimumActivity.Header=$"最低近期活动强度 {f.MinimumActivity:P0}";
populationMin.Value=f.PopulationMinimum;populationMax.Value=f.PopulationMaximum;populationMin.IsEnabled=populationMax.IsEnabled=f.Mode!=GalaxyMapMode.Realistic&&(f.Category==GalaxyFilterCategory.Population||f.SizeCategory==GalaxyFilterCategory.Population);
var definition=(GalaxyFilterDefinition)category.SelectedItem;
var originalState=f.Category==GalaxyFilterCategory.State&&f.RequireOriginalStateFacts;
var counts=choiceCounts;var recount=!ReferenceEquals(countedSystems,Systems)||countedCategory!=f.Category||countedCommodityDirection!=f.CommodityDirection||countedOriginalState!=originalState;
if(recount){countedSystems=Systems;countedCategory=f.Category;countedCommodityDirection=f.CommodityDirection;countedOriginalState=originalState;counts.Clear();}
var missing=0;var matching=0;
foreach(var s in Systems)
{var match=f.Evaluate(s);if(match.Unknown)missing++;if(recount&&match.Keys is {} keys)foreach(var key in keys.Distinct())counts[key]=counts.GetValueOrDefault(key)+1;if(match.Matches)matching++;}
coverage.Text=$"{matching:N0} / {Systems.Count:N0} 星系 · {missing:N0} 个未知";
note.Text=f.Mode==GalaxyMapMode.Realistic?"写实模式依恒星光谱显示。选择筛选类别会切换至地图模式。":f.Category==GalaxyFilterCategory.Markers?GalaxyMarkerSources.Describe(f.Values):definition.Availability.Length>0?definition.Availability:"图例颜色对应已记录类别;灰色表示未知数据。";
if(f.UsesPowerTerritory)note.Text="势力与状态按原掩码同时筛选,须有当前游戏元数据。"+(f.OriginalPowerOptions.Count==0?"等待游戏实际势力表;不会用公开列表顺序代替原索引。":"清除势力仍保留原规则允许的无主、无势力旗标星系;要隐藏它们请取消“未占领”状态。")+"半选状态表示原保存值只含组内部分阶段。";
if(f.Mode!=GalaxyMapMode.Realistic&&f.Category==GalaxyFilterCategory.Services)note.Text="前10项为原版服务选项,后续为工具扩展服务。"+note.Text;
if(f.Category==GalaxyFilterCategory.Commodity&&f.OriginalCommodityOptions.Count>0)note.Text=$"原定义源 {f.OriginalCommodityOptions.Count:N0} 项;库存不满足 {f.OriginalCommodityOptions.Count(o=>o.PublisherEligible==false):N0},待确认 {f.OriginalCommodityOptions.Count(o=>o.PublisherEligible is null):N0}。"+(f.UsesOriginalCommodity?"\n单项使用原进口/出口价格分类及无有效价格分支;未知隐藏。":"\n多项或非原定义使用公共站点供需查询;不冒充原区域余额。");
if(f.RequiresOriginalCommodity&&f.CommodityRequest is null)note.Text="原商品选择尚未与实际UID定义绑定;保留原选择,资料未知。不会改查之前的公共商品。";
information.Text=InformationStatus;
importSettings.Command=ImportSettingsCommand;importStatus.Text=OriginalSettingsStatus;
importDetails.Text=OriginalSettingsDetails;
originalRouteFilter.IsChecked=f.OriginalRouteFilters.TryGetValue(f.Category,out var affects)?affects:null;
if(!ReferenceEquals(renderedOriginalOptions,OriginalOptions))
{renderedOriginalOptions=OriginalOptions;originalOptions.Children.Clear();foreach(var entry in OriginalOptions??new Dictionary<string,string>())originalOptions.Children.Add(new TextBlock{Text=entry.Key+" = "+entry.Value,FontSize=10,TextWrapping=TextWrapping.Wrap});}
var extras=counts.Keys.Concat(f.OriginalOptionKeys.GetValueOrDefault(f.Category)??new HashSet<string>()).Concat(f.Values??new HashSet<string>());
var valueChoices=f.Category==GalaxyFilterCategory.PowerActivityEligibility?Array.Empty<GalaxyFilterChoice>():QueryChoices??[];
var entries=definition.Choices.Concat(valueChoices).DistinctBy(x=>x.Key).Concat(extras.Distinct().Where(k=>!definition.Choices.Any(c=>c.Key==k)&&!valueChoices.Any(c=>c.Key==k)).Order().Select(k=>new GalaxyFilterChoice(k,k)));
if(GalaxyStateFilterChoices.FromOriginal(f,counts.Keys) is {} originalStates)entries=originalStates;
if(f.UsesPowerTerritory)entries=f.OriginalPowerOptions.Where(p=>p.Index is >=0 and <128).OrderBy(p=>p.Index).Select(p=>new GalaxyFilterChoice(GalaxyMapFilter.Normalize(p.Name),p.Name));
if(f.Category==GalaxyFilterCategory.Services)
{var order=GalaxyOriginalServiceOptions.Keys.Select((key,index)=>(key,index)).ToDictionary(p=>p.key,p=>p.index);entries=entries.OrderBy(c=>order.GetValueOrDefault(c.Key,int.MaxValue));}
var search=choiceSearch.Text.Trim();var visibleEntries=f.Category==GalaxyFilterCategory.Commodity?[]:entries.Where(x=>search.Length==0||x.Label.Contains(search,StringComparison.OrdinalIgnoreCase)||x.Key.Contains(search,StringComparison.OrdinalIgnoreCase)).Take(160).ToArray();
var signature=f.Category+"|"+string.Join("|",visibleEntries.Select(e=>e.Key+":"+e.Label));
if(signature!=renderedChoices){renderedChoices=signature;choices.Children.Clear();choiceRows.Clear();}
foreach(var entry in visibleEntries)
{
var count=counts.GetValueOrDefault(entry.Key);var selected=f.UsesPowerTerritory?f.TerritoryPowerMask is null||f.OriginalPowerOptions.FirstOrDefault(p=>GalaxyMapFilter.Normalize(p.Name)==entry.Key) is {Index:>=0 and <128} option&&(f.TerritoryPowerMask.Value&(1u<<(option.Index&31)))!=0:f.Values is null||f.Values.Contains(entry.Key);
if(!choiceRows.TryGetValue(entry.Key,out var controls))
{
var row=new CheckBox{HorizontalAlignment=HorizontalAlignment.Stretch};var line=new StackPanel{Orientation=Orientation.Horizontal,Spacing=7};
var text=new TextBlock{FontSize=12,VerticalAlignment=VerticalAlignment.Center};var swatch=new Ellipse{Width=8,Height=8,VerticalAlignment=VerticalAlignment.Center};
line.Children.Add(swatch);line.Children.Add(text);row.Content=line;row.Checked+=(_,_)=>Select(entry.Key,true);row.Unchecked+=(_,_)=>Select(entry.Key,false);
choiceRows[entry.Key]=controls=(row,text,swatch);choices.Children.Add(row);
}
controls.Row.IsChecked=selected;controls.Row.IsEnabled=f.Mode!=GalaxyMapMode.Realistic;controls.Text.Text=entry.Label+" "+count.ToString("N0");
controls.Swatch.Visibility=f.Category is GalaxyFilterCategory.Population or GalaxyFilterCategory.Markers?Visibility.Collapsed:Visibility.Visible;
var colour=f.ColourForValue(entry.Key);
if(controls.Swatch.Fill is SolidColorBrush oldBrush)oldBrush.Color=Color.FromArgb(255,colour.R,colour.G,colour.B);else controls.Swatch.Fill=new SolidColorBrush(Color.FromArgb(255,colour.R,colour.G,colour.B));
AutomationProperties.SetName(controls.Row,entry.Label+","+count+" 个星系");
}
scoop.Visibility=f.Category==GalaxyFilterCategory.StarClass?Visibility.Visible:Visibility.Collapsed;
unknown.IsChecked=f.IncludeUnknown;unknown.IsEnabled=f.Mode!=GalaxyMapMode.Realistic;
grid.IsChecked=f.ShowGrid;gridStalks.IsChecked=f.ShowGridStalks;gridStalks.IsEnabled=f.ShowGrid;reverseMouseCrane.IsChecked=f.ReverseMouseCrane;labels.IsChecked=f.ShowLabels;links.IsChecked=f.ShowLinks;markers.IsChecked=f.ShowMarkers;route.IsChecked=f.ShowRoute;background.IsChecked=f.ShowBackground;
regionNames.IsChecked=f.ShowRegionNames;nebulaNames.IsChecked=f.ShowNebulaNames;constellations.IsChecked=f.ShowConstellations;galacticZones.IsChecked=f.ShowGalacticZonesPlane;galacticZoneNames.IsChecked=f.ShowGalacticZoneNames;
RefreshMarkerVisibilityControls(f);
RefreshCurrentTradeControls(f);
historicalTrade.IsChecked=f.ShowHistoricalTradeFlows;historicalTradeSource.SelectedIndex=(int)f.HistoricalTradeSource;historicalTradeSource.IsEnabled=f.ShowHistoricalTradeFlows;historicalTradeStatus.Text=HistoricalTradeStatus;
RefreshHistoricalCommodityTree(f);
range.Value=f.JumpRange;range.IsEnabled=f.ShowLinks;shipRange.IsEnabled=f.ShowLinks&&ShipJumpRange is >=1 and <=1000;
}
finally{updating=false;}
}
private void RefreshCommodityTree()
{
var available=(QueryChoices??[]).Concat(Filter.Values?.Select(k=>new GalaxyFilterChoice(k,k))??[]).Concat(Systems.SelectMany(s=>GalaxyMapFilter.Keys(s,GalaxyFilterCategory.Commodity,Filter.CommodityDirection)??[]).Select(k=>new GalaxyFilterChoice(k,k)));
currentCommodityGroups=GalaxyCommodityTree.Build(available,originalOptions:Filter.OriginalCommodityOptions);
var search=choiceSearch.Text.Trim();
bool Contains(string value)=>value.Contains(search,StringComparison.OrdinalIgnoreCase);
var shown=currentCommodityGroups.Select(g=>search.Length==0||Contains(g.Label)||Contains(g.Key)?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(commodityTreeSignature!=signature)
{
commodityTreeSignature=signature;commodityGroups.Children.Clear();commodityParents.Clear();commodityLeaves.Clear();
commodityGroups.Children.Add(new TextBlock{Text="按原商品类别分组;分类未读取的商品仍可单独选择。父类选择作用于整类,查找仅改变列表。",FontSize=11,TextWrapping=TextWrapping.Wrap});
foreach(var group in shown)
{
var complete=currentCommodityGroups.First(g=>g.Key==group.Key);
var content=new StackPanel{Spacing=1};var parent=new CheckBox{Content="全选此类",IsThreeState=true};content.Children.Add(parent);commodityParents[group.Key]=(parent,complete);
parent.Click+=(_,_)=>
{
if(updating)return;
Filter=Filter with{OriginalCommodityUid=null,Values=GalaxyCommodityTree.SelectGroup(Filter.Values,currentCommodityGroups.SelectMany(g=>g.Children).Select(c=>c.Key),complete.Children.Select(c=>c.Key),GalaxyCommodityTree.Selected(Filter.Values,complete.Children)!=true)};
};
foreach(var child in group.Children)
{
var check=new CheckBox{Content=child.Label,HorizontalAlignment=HorizontalAlignment.Stretch};commodityLeaves[child.Key]=check;content.Children.Add(check);
check.Checked+=(_,_)=>Select(child.Key,true);check.Unchecked+=(_,_)=>Select(child.Key,false);
}
var expander=new Expander{Header=group.Label+" · "+complete.Children.Count,Content=content,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch,IsExpanded=search.Length>0||commodityExpanded.GetValueOrDefault(group.Key)};
expander.Expanding+=(_,_)=>commodityExpanded[group.Key]=true;expander.Collapsed+=(_,_)=>commodityExpanded[group.Key]=false;commodityGroups.Children.Add(expander);
}
}
foreach(var entry in commodityLeaves)entry.Value.IsChecked=Filter.Values is null||Filter.Values.Contains(entry.Key);
foreach(var entry in commodityParents)entry.Value.Check.IsChecked=GalaxyCommodityTree.Selected(Filter.Values,entry.Value.Group.Children);
}
private void RefreshCommunityGoals()
{
communityGoalStatus.Text=CommunityGoalStatus;
if(ReferenceEquals(renderedCommunityGoals,CommunityGoalTargets)&&ReferenceEquals(renderedCommunityGoalCommand,SelectCommunityGoalCommand))return;
renderedCommunityGoals=CommunityGoalTargets;renderedCommunityGoalCommand=SelectCommunityGoalCommand;communityGoalTargets.Children.Clear();
foreach(var target in CommunityGoalTargets)
{
var content=new StackPanel{Spacing=2};content.Children.Add(new TextBlock{Text=target.Label,FontSize=12,TextWrapping=TextWrapping.Wrap});content.Children.Add(new TextBlock{Text=target.Source+" · 地址 "+target.SystemAddress,FontSize=10,TextWrapping=TextWrapping.Wrap});
var button=new Button{Content=content,Command=SelectCommunityGoalCommand,CommandParameter=target,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch};AutomationProperties.SetName(button,"定位社区活动 "+target.Label);communityGoalTargets.Children.Add(button);
}
}
private void RefreshThargoid()
{
thargoidStatus.Text=ThargoidStatus;
if(!ReferenceEquals(renderedThargoidCounts,ThargoidCounts))
{
renderedThargoidCounts=ThargoidCounts;thargoidCounts.Children.Clear();
foreach(var count in ThargoidCounts.Where(c=>c.State!=38))thargoidCounts.Children.Add(new TextBlock{Text=count.Text,FontSize=12,TextWrapping=TextWrapping.Wrap});
if(ThargoidCounts.FirstOrDefault(c=>c.State==38) is {} pseudo)ToolTipService.SetToolTip(thargoidCounts,"原缓存状态38 · "+pseudo.Text);
}
var commandChanged=!ReferenceEquals(renderedThargoidCommand,SelectThargoidTargetCommand);renderedThargoidCommand=SelectThargoidTargetCommand;
if(commandChanged||!ReferenceEquals(renderedThargoidSignals,ThargoidSignals))
{renderedThargoidSignals=ThargoidSignals;Targets(thargoidSignals,ThargoidSignals);}
if(commandChanged||!ReferenceEquals(renderedThargoidRecommendations,ThargoidRecommendations))
{renderedThargoidRecommendations=ThargoidRecommendations;Targets(thargoidRecommendations,ThargoidRecommendations);}
void Targets(StackPanel panel,IReadOnlyList<GalaxyThargoidTarget> targets)
{
panel.Children.Clear();
if(targets.Count==0){panel.Children.Add(new TextBlock{Text="缓存暂无条目;不代表已确认不存在。",FontSize=11,TextWrapping=TextWrapping.Wrap});return;}
foreach(var target in targets)
{
var label=new StackPanel{Spacing=3};label.Children.Add(new TextBlock{Text=target.Title,TextWrapping=TextWrapping.Wrap,FontSize=12});label.Children.Add(new TextBlock{Text=target.Detail,TextWrapping=TextWrapping.Wrap,FontSize=10});
var button=new Button{Content=label,HorizontalAlignment=HorizontalAlignment.Stretch,HorizontalContentAlignment=HorizontalAlignment.Stretch,Command=SelectThargoidTargetCommand,CommandParameter=target};
AutomationProperties.SetName(button,"定位 "+target.Title);panel.Children.Add(button);
}
}
}
private void Select(string key,bool selected)
{
if(updating)return;
if(Filter.UsesPowerTerritory)
{
if(Filter.OriginalPowerOptions.FirstOrDefault(p=>GalaxyMapFilter.Normalize(p.Name)==key) is not {Index:>=0 and <128} power)return;
var mask=Filter.TerritoryPowerMask??Filter.OriginalPowerOptions.Where(p=>p.Index is >=0 and <128).Aggregate(0u,(bits,p)=>bits|(1u<<(p.Index&31)));
var bit=1u<<(power.Index&31);Filter=Filter with{TerritoryPowerMask=selected?mask|bit:mask&~bit,Values=null};return;
}
if(GalaxyStateFilterChoices.FromOriginal(Filter,choiceCounts.Keys) is {} originalStates)
{Filter=Filter with{Values=GalaxyStateFilterChoices.Select(Filter.Values,originalStates,key,selected)};return;}
var definition=GalaxyFilterDefinitions.All.First(x=>x.Category==Filter.Category);
var values=Filter.Values is {} existing?new HashSet<string>(existing):definition.Choices.Select(x=>x.Key).Concat((QueryChoices??[]).Select(x=>x.Key)).Concat(Filter.Category==GalaxyFilterCategory.Commodity?currentCommodityGroups.SelectMany(g=>g.Children).Select(c=>c.Key):[]).Concat(Systems.SelectMany(s=>GalaxyMapFilter.Keys(s,Filter.Category,Filter.CommodityDirection)??[])).ToHashSet();
if(selected)values.Add(key);else values.Remove(key);Filter=Filter with{Values=values,OriginalCommodityUid=Filter.Category==GalaxyFilterCategory.Commodity?null:Filter.OriginalCommodityUid};
}
}