using XFEEDToolBox.ViewModels;
using XFEEDToolBox.Core.Galaxy;
namespace XFEEDToolBox.Views;
public sealed partial class NavigationPage : Page
{
private bool initialMapFramed;
private CancellationTokenSource? suggestionStop;
private CancellationTokenSource? regionStop;
private bool galaxyPageActive;
private CancellationTokenSource? placeSuggestionStop;
private CancellationTokenSource? focusStop;
private bool choosingPlace;
private string selectedPlaceQuery="";
public ToolboxViewModel ViewModel=>App.ViewModel;
public NavigationPage()
{
InitializeComponent();NavigationCacheMode=Microsoft.UI.Xaml.Navigation.NavigationCacheMode.Required;
Map.SystemSelected+=(_,s)=>ViewModel.GalaxySelection=s;
Map.SelectionCleared+=()=>ViewModel.GalaxySelection=null;
Map.TargetChosen+=ChooseTarget;SizeChanged+=(_,_)=>ResizePanels();
Map.ViewportSettled+=ViewportSettled;
Loaded+=(_,_)=>UpdatePlacePanels();
PlacesExpander.RegisterPropertyChangedCallback(Controls.HorizontalPanel.IsExpandedProperty,(_,_)=>UpdateSelectionOverlay());
FiltersExpander.RegisterPropertyChangedCallback(Controls.HorizontalPanel.IsExpandedProperty,(_,_)=>
{
if(FiltersExpander.IsExpanded)
{SidebarExpander.IsExpanded=false;PlacesExpander.IsExpanded=false;SidebarScroll.ChangeView(null,0,null);}
ResizePanels();
});
SidebarExpander.RegisterPropertyChangedCallback(Controls.HorizontalPanel.IsExpandedProperty,(_,_)=>{if(SidebarExpander.IsExpanded)FiltersExpander.IsExpanded=false;});
PlacesExpander.RegisterPropertyChangedCallback(Controls.HorizontalPanel.IsExpandedProperty,(_,_)=>{if(PlacesExpander.IsExpanded)FiltersExpander.IsExpanded=false;});
}
internal IDisposable SuspendRouteSubmissionForProbe()
{
if(!Environment.GetCommandLineArgs().Contains("--smoke-workspace"))throw new InvalidOperationException("仅限独立界面验收。");
Map.TargetChosen-=ChooseTarget;return new ProbeRouteScope(()=>Map.TargetChosen+=ChooseTarget);
}
private sealed class ProbeRouteScope(Action restore):IDisposable {public void Dispose()=>restore();}
protected override void OnNavigatedTo(Microsoft.UI.Xaml.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if(regionStop is null||regionStop.IsCancellationRequested){regionStop?.Dispose();regionStop=new();}
galaxyPageActive=true;Map.ResumePage();ViewModel.NavigationPlanned+=OnPlanned;ResizePanels();
ViewModel.GalaxyDisplayReady+=RefreshGalaxyDisplay;
}
protected override void OnNavigatedFrom(Microsoft.UI.Xaml.Navigation.NavigationEventArgs e)
{
galaxyPageActive=false;ViewModel.SuspendGalaxyStreaming();
ViewModel.NavigationPlanned-=OnPlanned;Map.SuspendPage();base.OnNavigatedFrom(e);
ViewModel.GalaxyDisplayReady-=RefreshGalaxyDisplay;
suggestionStop?.Cancel();DestinationInput.IsSuggestionListOpen=false;
regionStop?.Cancel();regionStop?.Dispose();regionStop=null;
placeSuggestionStop?.Cancel();
ViewModel.SearchGalacticPlacesCommand.Cancel();
focusStop?.Cancel();
}
private async void ChooseTarget(object? sender,GalacticSystem target)
{
if(ViewModel.IsPlottedDestination(target)){await ViewModel.CancelPlottedRouteCommand.ExecuteAsync(null);return;}
if(ViewModel.NavigationTargetLocked)return;
focusStop?.Cancel();suggestionStop?.Cancel();DestinationInput.IsSuggestionListOpen=false;
RouteExpander.IsExpanded=true;await ViewModel.ChooseNavigationTargetAsync(target);
await ViewModel.PlanNavigationCommand.ExecuteAsync(null);
}
private async void DestinationSubmitted(AutoSuggestBox sender,AutoSuggestBoxQuerySubmittedEventArgs e)
{
await FocusSearchAsync(e.ChosenSuggestion is GalacticSystem system?system.Name:sender.Text);
}
private async void DestinationChosen(AutoSuggestBox sender,AutoSuggestBoxSuggestionChosenEventArgs e)
{
if(e.SelectedItem is not GalacticSystem system)return;
await FocusSearchAsync(system.Name);
}
private async Task FocusSearchAsync(string name)
{
if(ViewModel.NavigationTargetLocked)return;
focusStop?.Cancel();focusStop?.Dispose();focusStop=new();var stop=focusStop;
try
{
var system=await ViewModel.ResolveGalacticSelectionAsync(name,stop.Token);stop.Token.ThrowIfCancellationRequested();
await ViewModel.ChooseNavigationTargetAsync(system);Map.FocusSystem(system);
}
catch(OperationCanceledException){ }
catch(Exception error){ViewModel.GalaxyStatus="星系查找未完成:"+error.Message;}
}
private async void DestinationChanged(AutoSuggestBox sender,AutoSuggestBoxTextChangedEventArgs e)
{
if(e.Reason!=AutoSuggestionBoxTextChangeReason.UserInput)return;
suggestionStop?.Cancel();suggestionStop?.Dispose();suggestionStop=new();var stop=suggestionStop;
var query=sender.Text.Trim();
if(query.Length<2){sender.ItemsSource=null;sender.IsSuggestionListOpen=false;return;}
try
{
await Task.Delay(250,stop.Token);
await PopulateSuggestionsAsync(query,stop.Token);
}
catch(OperationCanceledException){ }
catch(Exception error){if(!stop.IsCancellationRequested)App.Runtime.Log("星系补全",error.Message);}
}
private async Task PopulateSuggestionsAsync(string query,CancellationToken token)
{
var found=await ViewModel.SuggestGalacticSystemsAsync(query,token);
if(token.IsCancellationRequested||DestinationInput.Text.Trim()!=query)return;
DestinationInput.ItemsSource=found;DestinationInput.IsSuggestionListOpen=found.Count>0;
}
internal async Task<(int Count,bool Open,bool AllNative)> ProbeSuggestionsAsync(string query)
{
DestinationInput.Text=query;await PopulateSuggestionsAsync(query,CancellationToken.None);
var rows=DestinationInput.ItemsSource as IReadOnlyList<GalacticSystem>??[];
return(rows.Count,DestinationInput.IsSuggestionListOpen,rows.All(s=>s.Source=="游戏银河目录"));
}
private void OnPlanned()=>DispatcherQueue.TryEnqueue(()=>{RouteExpander.IsExpanded=true;Map.FitRoute(NavigationOverlay.ActualWidth+28);});
private void MapSizeChanged(object sender,SizeChangedEventArgs e)
{
ResizePanels();
if(!initialMapFramed&&ViewModel.NavigationMapRoute.Count>1&&Map.ActualWidth>0)
{initialMapFramed=true;DispatcherQueue.TryEnqueue(()=>Map.FitRoute(NavigationOverlay.ActualWidth+28));}
}
private void ResizePanels()
{
if(MapRegion is null||NavigationOverlay is null||NavigationLane is null||MapCommands is null)return;
NavigationOverlay.Width=NavigationLane.Width=Math.Clamp(MapRegion.ActualWidth*.28,260,344);
SidebarScroll.MaxWidth=Math.Max(240,MapRegion.ActualWidth-NavigationOverlay.Width-56);
SidebarExpander.ContentWidthLimit=PlacesExpander.ContentWidthLimit=FiltersExpander.ContentWidthLimit=Math.Max(128,SidebarScroll.MaxWidth-152);
SidebarScroll.MaxHeight=Math.Max(160,MapRegion.ActualHeight-112);
NavigationLane.MaxHeight=Math.Max(160,MapRegion.ActualHeight-32);
MapCommands.MaxWidth=Math.Max(180,MapRegion.ActualWidth-NavigationOverlay.Width-56);
RouteScroll.MaxHeight=Math.Max(140,MapRegion.ActualHeight-150);
}
private void ZoomIn(object sender,RoutedEventArgs e)=>Map.Zoom(1.5);
private void ZoomOut(object sender,RoutedEventArgs e)=>Map.Zoom(1/1.5);
private void ResetCamera(object sender,RoutedEventArgs e)=>Map.ResetCamera();
private void FitRoute(object sender,RoutedEventArgs e)=>Map.FitRoute(NavigationOverlay.ActualWidth+28);
private void ShowGalaxy(object sender,RoutedEventArgs e)=>Map.ShowGalaxyOverview();
private void ClearSelection(object sender,RoutedEventArgs e)=>Map.ClearSelection();
private void CancelRoutePlanning(object sender,RoutedEventArgs e)=>ViewModel.PlanNavigationCommand.Cancel();
private void RefreshGalaxyDisplay()=>Map.RefreshDisplay();
private async void PlaceKindChanged(object sender,SelectionChangedEventArgs e)
{
if(PlacesResultPane is null)return;
ViewModel.GalaxyPlaceKindIndex=PlaceKindInput.SelectedIndex;
selectedPlaceQuery="";
UpdatePlacePanels();
if(ViewModel.PlacesNeedItem)await PopulatePlaceChoicesAsync("");
}
private void UpdatePlacePanels()
{
if(PlacesResultPane is null)return;
PlacesResultPane.Visibility=Visibility.Collapsed;GatheringMaterialPane.Visibility=Visibility.Collapsed;
UpdateSelectionOverlay();
PlaceChildPanel.Visibility=ViewModel.GalaxyPlaceKindIndex==1?Visibility.Collapsed:Visibility.Visible;
if(ViewModel.GalaxyPlaceKindIndex==1)OpenPlaceSearch("最近的高科技星系");
}
private async void PlaceQueryChanged(object sender,TextChangedEventArgs e)
{
if(choosingPlace||PlaceChoiceList is null||!ViewModel.PlacesNeedItem||PlaceQueryInput.Text==selectedPlaceQuery)return;
selectedPlaceQuery="";
PlacesResultPane.Visibility=Visibility.Collapsed;UpdateSelectionOverlay();
await PopulatePlaceChoicesAsync(PlaceQueryInput.Text.Trim());
}
private async Task PopulatePlaceChoicesAsync(string query)
{
placeSuggestionStop?.Cancel();placeSuggestionStop?.Dispose();placeSuggestionStop=new();var stop=placeSuggestionStop;
var kind=ViewModel.GalaxyPlaceKindIndex;PlaceChoiceHint.Text="正在载入名称…";
try
{
await Task.Delay(150,stop.Token);var values=await ViewModel.SuggestPlacesAsync(query,stop.Token);
if(stop.IsCancellationRequested||kind!=ViewModel.GalaxyPlaceKindIndex)return;
PlaceChoiceList.ItemsSource=values;PlaceChoiceHint.Text=values.Count>0?$"{values.Count} 项 · 点击后向右展开":"没有匹配的名称";
}
catch(OperationCanceledException){ }
catch(Exception error){if(!stop.IsCancellationRequested){PlaceChoiceHint.Text="名称载入失败,请重新选择功能重试。";App.Runtime.Log("地点补全",error.Message);}}
}
private void PlaceLeafClicked(object sender,ItemClickEventArgs e)=>SelectPlaceLeaf((ListView)sender,e.ClickedItem);
private void PlaceLeafSelectionChanged(object sender,SelectionChangedEventArgs e)
{
if(sender is ListView{IsLoaded:true} list&&e.AddedItems.Count>0)SelectPlaceLeaf(list,e.AddedItems[0]);
}
private void SelectPlaceLeaf(ListView list,object item)
{
if(list==TraderCategoryInput&&ViewModel.PlacesNeedTrader)
{
ViewModel.GalaxyTraderIndex=list.Items.IndexOf(item);OpenPlaceSearch(item+"贸易商");
}
else if(list==GatheringCategoryInput&&ViewModel.PlacesNeedGathering)
{
ViewModel.GalaxyGatheringCategoryIndex=list.Items.IndexOf(item);PlacesResultPane.Visibility=Visibility.Collapsed;UpdateSelectionOverlay();
GatheringMaterialPane.Visibility=ViewModel.PlacesNeedGatheringMaterial?Visibility.Visible:Visibility.Collapsed;
if(!ViewModel.PlacesNeedGatheringMaterial)OpenPlaceSearch("数据 · Jameson 坠毁点");else RevealPlacePane();
}
else if(list==GatheringMaterialInput&&GatheringMaterialPane.Visibility==Visibility.Visible&&item is GatheringMaterial material)
{ViewModel.GalaxyGatheringMaterial=material;OpenPlaceSearch(material.DisplayName);}
else if(list==EngineerLocationInput&&ViewModel.PlacesNeedEngineer&&item is EngineerLocation engineer)
{ViewModel.GalaxyEngineer=engineer;OpenPlaceSearch(engineer.DisplayName);}
else if(list==PlaceChoiceList&&ViewModel.PlacesNeedItem&&item is GalacticPlaceChoice choice)
{
selectedPlaceQuery=choice.Name;choosingPlace=true;ViewModel.GalaxyPlaceQuery=choice.Name;PlaceQueryInput.Text=choice.Name;choosingPlace=false;
OpenPlaceSearch(choice.DisplayName);
}
}
internal void OpenPlaceSearch(string title)
{
SelectedPlaceTitle.Text=title;PlacesResultPane.Visibility=Visibility.Visible;UpdateSelectionOverlay();RevealPlacePane();
}
private void UpdateSelectionOverlay()
{
if(FindName("SelectedStarOverlay") is not FrameworkElement overlay)return;
var show=!PlacesExpander.IsExpanded||PlacesResultPane.Visibility!=Visibility.Visible;
overlay.Opacity=show?1:0;overlay.IsHitTestVisible=show;
}
private void RevealPlacePane()
{
DispatcherQueue.TryEnqueue(()=>{PlacesExpander.RevealRight();PlacesExpander.StartBringIntoView(new(){AnimationDesired=true,VerticalAlignmentRatio=0});});
}
private void PlaceClicked(object sender,ItemClickEventArgs e)
{if(e.ClickedItem is GalacticPlace place)ViewModel.ShowGalacticPlace(place);}
private async void UsePlaceDestination(object sender,RoutedEventArgs e)
{
if(ViewModel.NavigationTargetLocked||sender is not Button{Tag:GalacticPlace place})return;
ViewModel.ShowGalacticPlace(place);await ViewModel.UseGalaxyDestinationCommand.ExecuteAsync(null);
}
private async void ViewportSettled(GalaxyViewport viewport)
{
// Session supersession preserves an overlapping in-flight sector so
// continuous mouse movement cannot repeatedly cancel every first page.
var stop=regionStop;if(!galaxyPageActive||stop is null||stop.IsCancellationRequested)return;
try{await ViewModel.StreamGalaxyRegionAsync(viewport,stop.Token);}
catch(OperationCanceledException){ }
catch(Exception error){if(!stop.IsCancellationRequested)App.Runtime.Log("星图区域",error.Message);}
}
}
using XFEEDToolBox.ViewModels;
using XFEEDToolBox.Core.Galaxy;
namespace XFEEDToolBox.Views;
public sealed partial class NavigationPage : Page
{
private bool initialMapFramed;
private CancellationTokenSource? suggestionStop;
private CancellationTokenSource? regionStop;
private bool galaxyPageActive;
private CancellationTokenSource? placeSuggestionStop;
private CancellationTokenSource? focusStop;
private bool choosingPlace;
private string selectedPlaceQuery="";
public ToolboxViewModel ViewModel=>App.ViewModel;
public NavigationPage()
{
InitializeComponent();NavigationCacheMode=Microsoft.UI.Xaml.Navigation.NavigationCacheMode.Required;
Map.SystemSelected+=(_,s)=>ViewModel.GalaxySelection=s;
Map.SelectionCleared+=()=>ViewModel.GalaxySelection=null;
Map.TargetChosen+=ChooseTarget;SizeChanged+=(_,_)=>ResizePanels();
Map.ViewportSettled+=ViewportSettled;
Loaded+=(_,_)=>UpdatePlacePanels();
PlacesExpander.RegisterPropertyChangedCallback(Controls.HorizontalPanel.IsExpandedProperty,(_,_)=>UpdateSelectionOverlay());
FiltersExpander.RegisterPropertyChangedCallback(Controls.HorizontalPanel.IsExpandedProperty,(_,_)=>
{
if(FiltersExpander.IsExpanded)
{SidebarExpander.IsExpanded=false;PlacesExpander.IsExpanded=false;SidebarScroll.ChangeView(null,0,null);}
ResizePanels();
});
SidebarExpander.RegisterPropertyChangedCallback(Controls.HorizontalPanel.IsExpandedProperty,(_,_)=>{if(SidebarExpander.IsExpanded)FiltersExpander.IsExpanded=false;});
PlacesExpander.RegisterPropertyChangedCallback(Controls.HorizontalPanel.IsExpandedProperty,(_,_)=>{if(PlacesExpander.IsExpanded)FiltersExpander.IsExpanded=false;});
}
internal IDisposable SuspendRouteSubmissionForProbe()
{
if(!Environment.GetCommandLineArgs().Contains("--smoke-workspace"))throw new InvalidOperationException("仅限独立界面验收。");
Map.TargetChosen-=ChooseTarget;return new ProbeRouteScope(()=>Map.TargetChosen+=ChooseTarget);
}
private sealed class ProbeRouteScope(Action restore):IDisposable {public void Dispose()=>restore();}
protected override void OnNavigatedTo(Microsoft.UI.Xaml.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if(regionStop is null||regionStop.IsCancellationRequested){regionStop?.Dispose();regionStop=new();}
galaxyPageActive=true;Map.ResumePage();ViewModel.NavigationPlanned+=OnPlanned;ResizePanels();
ViewModel.GalaxyDisplayReady+=RefreshGalaxyDisplay;
}
protected override void OnNavigatedFrom(Microsoft.UI.Xaml.Navigation.NavigationEventArgs e)
{
galaxyPageActive=false;ViewModel.SuspendGalaxyStreaming();
ViewModel.NavigationPlanned-=OnPlanned;Map.SuspendPage();base.OnNavigatedFrom(e);
ViewModel.GalaxyDisplayReady-=RefreshGalaxyDisplay;
suggestionStop?.Cancel();DestinationInput.IsSuggestionListOpen=false;
regionStop?.Cancel();regionStop?.Dispose();regionStop=null;
placeSuggestionStop?.Cancel();
ViewModel.SearchGalacticPlacesCommand.Cancel();
focusStop?.Cancel();
}
private async void ChooseTarget(object? sender,GalacticSystem target)
{
if(ViewModel.IsPlottedDestination(target)){await ViewModel.CancelPlottedRouteCommand.ExecuteAsync(null);return;}
if(ViewModel.NavigationTargetLocked)return;
focusStop?.Cancel();suggestionStop?.Cancel();DestinationInput.IsSuggestionListOpen=false;
RouteExpander.IsExpanded=true;await ViewModel.ChooseNavigationTargetAsync(target);
await ViewModel.PlanNavigationCommand.ExecuteAsync(null);
}
private async void DestinationSubmitted(AutoSuggestBox sender,AutoSuggestBoxQuerySubmittedEventArgs e)
{
await FocusSearchAsync(e.ChosenSuggestion is GalacticSystem system?system.Name:sender.Text);
}
private async void DestinationChosen(AutoSuggestBox sender,AutoSuggestBoxSuggestionChosenEventArgs e)
{
if(e.SelectedItem is not GalacticSystem system)return;
await FocusSearchAsync(system.Name);
}
private async Task FocusSearchAsync(string name)
{
if(ViewModel.NavigationTargetLocked)return;
focusStop?.Cancel();focusStop?.Dispose();focusStop=new();var stop=focusStop;
try
{
var system=await ViewModel.ResolveGalacticSelectionAsync(name,stop.Token);stop.Token.ThrowIfCancellationRequested();
await ViewModel.ChooseNavigationTargetAsync(system);Map.FocusSystem(system);
}
catch(OperationCanceledException){ }
catch(Exception error){ViewModel.GalaxyStatus="星系查找未完成:"+error.Message;}
}
private async void DestinationChanged(AutoSuggestBox sender,AutoSuggestBoxTextChangedEventArgs e)
{
if(e.Reason!=AutoSuggestionBoxTextChangeReason.UserInput)return;
suggestionStop?.Cancel();suggestionStop?.Dispose();suggestionStop=new();var stop=suggestionStop;
var query=sender.Text.Trim();
if(query.Length<2){sender.ItemsSource=null;sender.IsSuggestionListOpen=false;return;}
try
{
await Task.Delay(250,stop.Token);
await PopulateSuggestionsAsync(query,stop.Token);
}
catch(OperationCanceledException){ }
catch(Exception error){if(!stop.IsCancellationRequested)App.Runtime.Log("星系补全",error.Message);}
}
private async Task PopulateSuggestionsAsync(string query,CancellationToken token)
{
var found=await ViewModel.SuggestGalacticSystemsAsync(query,token);
if(token.IsCancellationRequested||DestinationInput.Text.Trim()!=query)return;
DestinationInput.ItemsSource=found;DestinationInput.IsSuggestionListOpen=found.Count>0;
}
internal async Task<(int Count,bool Open,bool AllNative)> ProbeSuggestionsAsync(string query)
{
DestinationInput.Text=query;await PopulateSuggestionsAsync(query,CancellationToken.None);
var rows=DestinationInput.ItemsSource as IReadOnlyList<GalacticSystem>??[];
return(rows.Count,DestinationInput.IsSuggestionListOpen,rows.All(s=>s.Source=="游戏银河目录"));
}
private void OnPlanned()=>DispatcherQueue.TryEnqueue(()=>{RouteExpander.IsExpanded=true;Map.FitRoute(NavigationOverlay.ActualWidth+28);});
private void MapSizeChanged(object sender,SizeChangedEventArgs e)
{
ResizePanels();
if(!initialMapFramed&&ViewModel.NavigationMapRoute.Count>1&&Map.ActualWidth>0)
{initialMapFramed=true;DispatcherQueue.TryEnqueue(()=>Map.FitRoute(NavigationOverlay.ActualWidth+28));}
}
private void ResizePanels()
{
if(MapRegion is null||NavigationOverlay is null||NavigationLane is null||MapCommands is null)return;
NavigationOverlay.Width=NavigationLane.Width=Math.Clamp(MapRegion.ActualWidth*.28,260,344);
SidebarScroll.MaxWidth=Math.Max(240,MapRegion.ActualWidth-NavigationOverlay.Width-56);
SidebarExpander.ContentWidthLimit=PlacesExpander.ContentWidthLimit=FiltersExpander.ContentWidthLimit=Math.Max(128,SidebarScroll.MaxWidth-152);
SidebarScroll.MaxHeight=Math.Max(160,MapRegion.ActualHeight-112);
NavigationLane.MaxHeight=Math.Max(160,MapRegion.ActualHeight-32);
MapCommands.MaxWidth=Math.Max(180,MapRegion.ActualWidth-NavigationOverlay.Width-56);
RouteScroll.MaxHeight=Math.Max(140,MapRegion.ActualHeight-150);
}
private void ZoomIn(object sender,RoutedEventArgs e)=>Map.Zoom(1.5);
private void ZoomOut(object sender,RoutedEventArgs e)=>Map.Zoom(1/1.5);
private void ResetCamera(object sender,RoutedEventArgs e)=>Map.ResetCamera();
private void FitRoute(object sender,RoutedEventArgs e)=>Map.FitRoute(NavigationOverlay.ActualWidth+28);
private void ShowGalaxy(object sender,RoutedEventArgs e)=>Map.ShowGalaxyOverview();
private void ClearSelection(object sender,RoutedEventArgs e)=>Map.ClearSelection();
private void CancelRoutePlanning(object sender,RoutedEventArgs e)=>ViewModel.PlanNavigationCommand.Cancel();
private void RefreshGalaxyDisplay()=>Map.RefreshDisplay();
private async void PlaceKindChanged(object sender,SelectionChangedEventArgs e)
{
if(PlacesResultPane is null)return;
ViewModel.GalaxyPlaceKindIndex=PlaceKindInput.SelectedIndex;
selectedPlaceQuery="";
UpdatePlacePanels();
if(ViewModel.PlacesNeedItem)await PopulatePlaceChoicesAsync("");
}
private void UpdatePlacePanels()
{
if(PlacesResultPane is null)return;
PlacesResultPane.Visibility=Visibility.Collapsed;GatheringMaterialPane.Visibility=Visibility.Collapsed;
UpdateSelectionOverlay();
PlaceChildPanel.Visibility=ViewModel.GalaxyPlaceKindIndex==1?Visibility.Collapsed:Visibility.Visible;
if(ViewModel.GalaxyPlaceKindIndex==1)OpenPlaceSearch("最近的高科技星系");
}
private async void PlaceQueryChanged(object sender,TextChangedEventArgs e)
{
if(choosingPlace||PlaceChoiceList is null||!ViewModel.PlacesNeedItem||PlaceQueryInput.Text==selectedPlaceQuery)return;
selectedPlaceQuery="";
PlacesResultPane.Visibility=Visibility.Collapsed;UpdateSelectionOverlay();
await PopulatePlaceChoicesAsync(PlaceQueryInput.Text.Trim());
}
private async Task PopulatePlaceChoicesAsync(string query)
{
placeSuggestionStop?.Cancel();placeSuggestionStop?.Dispose();placeSuggestionStop=new();var stop=placeSuggestionStop;
var kind=ViewModel.GalaxyPlaceKindIndex;PlaceChoiceHint.Text="正在载入名称…";
try
{
await Task.Delay(150,stop.Token);var values=await ViewModel.SuggestPlacesAsync(query,stop.Token);
if(stop.IsCancellationRequested||kind!=ViewModel.GalaxyPlaceKindIndex)return;
PlaceChoiceList.ItemsSource=values;PlaceChoiceHint.Text=values.Count>0?$"{values.Count} 项 · 点击后向右展开":"没有匹配的名称";
}
catch(OperationCanceledException){ }
catch(Exception error){if(!stop.IsCancellationRequested){PlaceChoiceHint.Text="名称载入失败,请重新选择功能重试。";App.Runtime.Log("地点补全",error.Message);}}
}
private void PlaceLeafClicked(object sender,ItemClickEventArgs e)=>SelectPlaceLeaf((ListView)sender,e.ClickedItem);
private void PlaceLeafSelectionChanged(object sender,SelectionChangedEventArgs e)
{
if(sender is ListView{IsLoaded:true} list&&e.AddedItems.Count>0)SelectPlaceLeaf(list,e.AddedItems[0]);
}
private void SelectPlaceLeaf(ListView list,object item)
{
if(list==TraderCategoryInput&&ViewModel.PlacesNeedTrader)
{
ViewModel.GalaxyTraderIndex=list.Items.IndexOf(item);OpenPlaceSearch(item+"贸易商");
}
else if(list==GatheringCategoryInput&&ViewModel.PlacesNeedGathering)
{
ViewModel.GalaxyGatheringCategoryIndex=list.Items.IndexOf(item);PlacesResultPane.Visibility=Visibility.Collapsed;UpdateSelectionOverlay();
GatheringMaterialPane.Visibility=ViewModel.PlacesNeedGatheringMaterial?Visibility.Visible:Visibility.Collapsed;
if(!ViewModel.PlacesNeedGatheringMaterial)OpenPlaceSearch("数据 · Jameson 坠毁点");else RevealPlacePane();
}
else if(list==GatheringMaterialInput&&GatheringMaterialPane.Visibility==Visibility.Visible&&item is GatheringMaterial material)
{ViewModel.GalaxyGatheringMaterial=material;OpenPlaceSearch(material.DisplayName);}
else if(list==EngineerLocationInput&&ViewModel.PlacesNeedEngineer&&item is EngineerLocation engineer)
{ViewModel.GalaxyEngineer=engineer;OpenPlaceSearch(engineer.DisplayName);}
else if(list==PlaceChoiceList&&ViewModel.PlacesNeedItem&&item is GalacticPlaceChoice choice)
{
selectedPlaceQuery=choice.Name;choosingPlace=true;ViewModel.GalaxyPlaceQuery=choice.Name;PlaceQueryInput.Text=choice.Name;choosingPlace=false;
OpenPlaceSearch(choice.DisplayName);
}
}
internal void OpenPlaceSearch(string title)
{
SelectedPlaceTitle.Text=title;PlacesResultPane.Visibility=Visibility.Visible;UpdateSelectionOverlay();RevealPlacePane();
}
private void UpdateSelectionOverlay()
{
if(FindName("SelectedStarOverlay") is not FrameworkElement overlay)return;
var show=!PlacesExpander.IsExpanded||PlacesResultPane.Visibility!=Visibility.Visible;
overlay.Opacity=show?1:0;overlay.IsHitTestVisible=show;
}
private void RevealPlacePane()
{
DispatcherQueue.TryEnqueue(()=>{PlacesExpander.RevealRight();PlacesExpander.StartBringIntoView(new(){AnimationDesired=true,VerticalAlignmentRatio=0});});
}
private void PlaceClicked(object sender,ItemClickEventArgs e)
{if(e.ClickedItem is GalacticPlace place)ViewModel.ShowGalacticPlace(place);}
private async void UsePlaceDestination(object sender,RoutedEventArgs e)
{
if(ViewModel.NavigationTargetLocked||sender is not Button{Tag:GalacticPlace place})return;
ViewModel.ShowGalacticPlace(place);await ViewModel.UseGalaxyDestinationCommand.ExecuteAsync(null);
}
private async void ViewportSettled(GalaxyViewport viewport)
{
// Session supersession preserves an overlapping in-flight sector so
// continuous mouse movement cannot repeatedly cancel every first page.
var stop=regionStop;if(!galaxyPageActive||stop is null||stop.IsCancellationRequested)return;
try{await ViewModel.StreamGalaxyRegionAsync(viewport,stop.Token);}
catch(OperationCanceledException){ }
catch(Exception error){if(!stop.IsCancellationRequested)App.Runtime.Log("星图区域",error.Message);}
}
}