XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
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);}
    }
}