From dede2e909c0f6d74b90df713381b53e09d05f85a Mon Sep 17 00:00:00 2001 From: BlossomiShymae <87099578+BlossomiShymae@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:31:52 -0600 Subject: [PATCH] Add tabs view to endpoints --- Needlework.Net/Needlework.Net.csproj | 3 + Needlework.Net/ViewLocator.cs | 4 ++ .../Endpoints/EndpointsContainerViewModel.cs | 30 --------- .../Endpoints/EndpointsNavigationViewModel.cs | 41 ++++++++++++ .../Pages/Endpoints/EndpointsTabViewModel.cs | 62 +++++++++++++++++++ .../Pages/Endpoints/EndpointsViewModel.cs | 32 +++------- ...ew.axaml => EndpointsNavigationView.axaml} | 4 +- ...ml.cs => EndpointsNavigationView.axaml.cs} | 4 +- .../Pages/Endpoints/EndpointsTabView.axaml | 35 +++++++++++ .../Pages/Endpoints/EndpointsTabView.axaml.cs | 19 ++++++ .../Views/Pages/Endpoints/EndpointsView.axaml | 5 +- 11 files changed, 177 insertions(+), 62 deletions(-) delete mode 100644 Needlework.Net/ViewModels/Pages/Endpoints/EndpointsContainerViewModel.cs create mode 100644 Needlework.Net/ViewModels/Pages/Endpoints/EndpointsNavigationViewModel.cs create mode 100644 Needlework.Net/ViewModels/Pages/Endpoints/EndpointsTabViewModel.cs rename Needlework.Net/Views/Pages/Endpoints/{EndpointsContainerView.axaml => EndpointsNavigationView.axaml} (89%) rename Needlework.Net/Views/Pages/Endpoints/{EndpointsContainerView.axaml.cs => EndpointsNavigationView.axaml.cs} (55%) create mode 100644 Needlework.Net/Views/Pages/Endpoints/EndpointsTabView.axaml create mode 100644 Needlework.Net/Views/Pages/Endpoints/EndpointsTabView.axaml.cs diff --git a/Needlework.Net/Needlework.Net.csproj b/Needlework.Net/Needlework.Net.csproj index 3d32a89..bc0981e 100644 --- a/Needlework.Net/Needlework.Net.csproj +++ b/Needlework.Net/Needlework.Net.csproj @@ -55,6 +55,9 @@ MainWindowView.axaml + + EndpointsNavigationView.axaml + EndpointView.axaml diff --git a/Needlework.Net/ViewLocator.cs b/Needlework.Net/ViewLocator.cs index 76985f6..14f5a17 100644 --- a/Needlework.Net/ViewLocator.cs +++ b/Needlework.Net/ViewLocator.cs @@ -19,6 +19,10 @@ namespace Needlework.Net { return new TextBlock { Text = "Data is null or has no name." }; } + if (!name.Contains("ViewModel")) + { + return new TextBlock { Text = "Data name must end with ViewModel." }; + } name = name.Replace("ViewModel", "View"); var type = Assembly.GetExecutingAssembly() diff --git a/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsContainerViewModel.cs b/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsContainerViewModel.cs deleted file mode 100644 index 225f84e..0000000 --- a/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsContainerViewModel.cs +++ /dev/null @@ -1,30 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.Input; -using System.Net.Http; - -namespace Needlework.Net.ViewModels.Pages.Endpoints; - -public partial class EndpointsContainerViewModel : PageBase -{ - [ObservableProperty] private ObservableObject _activeViewModel; - [ObservableProperty] private ObservableObject _endpointsViewModel; - [ObservableProperty] private string _title = string.Empty; - - public EndpointsContainerViewModel(HttpClient httpClient) : base("Endpoints", "list-alt", -500) - { - _activeViewModel = _endpointsViewModel = new EndpointsViewModel(httpClient, OnClicked); - } - - private void OnClicked(ObservableObject viewModel) - { - ActiveViewModel = viewModel; - if (viewModel is EndpointViewModel endpoint) Title = endpoint.Title; - } - - [RelayCommand] - private void GoBack() - { - ActiveViewModel = EndpointsViewModel; - Title = string.Empty; - } -} diff --git a/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsNavigationViewModel.cs b/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsNavigationViewModel.cs new file mode 100644 index 0000000..fe04b72 --- /dev/null +++ b/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsNavigationViewModel.cs @@ -0,0 +1,41 @@ +using Avalonia.Collections; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using System; + +namespace Needlework.Net.ViewModels.Pages.Endpoints; + +public partial class EndpointsNavigationViewModel : ObservableObject +{ + public Guid Guid { get; } = Guid.NewGuid(); + + [ObservableProperty] private ObservableObject _activeViewModel; + [ObservableProperty] private ObservableObject _endpointsViewModel; + [ObservableProperty] private string _title = string.Empty; + + private readonly Action _onEndpointNavigation; + + public EndpointsNavigationViewModel(IAvaloniaList plugins, Action onEndpointNavigation) + { + _activeViewModel = _endpointsViewModel = new EndpointsViewModel(plugins, OnClicked); + _onEndpointNavigation = onEndpointNavigation; + } + + private void OnClicked(ObservableObject viewModel) + { + ActiveViewModel = viewModel; + if (viewModel is EndpointViewModel endpoint) + { + Title = endpoint.Title; + _onEndpointNavigation.Invoke(endpoint.Title, Guid); + } + } + + [RelayCommand] + private void GoBack() + { + ActiveViewModel = EndpointsViewModel; + Title = string.Empty; + _onEndpointNavigation.Invoke(null, Guid); + } +} diff --git a/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsTabViewModel.cs b/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsTabViewModel.cs new file mode 100644 index 0000000..0b50f7e --- /dev/null +++ b/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsTabViewModel.cs @@ -0,0 +1,62 @@ +using Avalonia.Collections; +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using FluentAvalonia.UI.Controls; +using Needlework.Net.Messages; +using System; + +namespace Needlework.Net.ViewModels.Pages.Endpoints; + +public partial class EndpointsTabViewModel : PageBase, IRecipient +{ + public IAvaloniaList Plugins { get; } = new AvaloniaList(); + public IAvaloniaList Endpoints { get; } = new AvaloniaList(); + + [ObservableProperty] private bool _isBusy = true; + + public EndpointsTabViewModel() : base("Endpoints", "list-alt", -500) + { + WeakReferenceMessenger.Default.RegisterAll(this); + } + + public void Receive(DataReadyMessage message) + { + IsBusy = false; + Plugins.Clear(); + Plugins.AddRange(message.Value.Plugins.Keys); + + Dispatcher.UIThread.Post(AddEndpoint); + } + + [RelayCommand] + private void AddEndpoint() + { + Endpoints.Add(new() + { + Content = new EndpointsNavigationViewModel(Plugins, OnEndpointNavigation), + Selected = true + }); + } + + private void OnEndpointNavigation(string? title, Guid guid) + { + foreach (var endpoint in Endpoints) + { + if (endpoint.Content.Guid.Equals(guid)) + { + endpoint.Header = title ?? "Endpoints"; + break; + } + } + } +} + +public partial class EndpointItem : ObservableObject +{ + [ObservableProperty] private string _header = "Endpoints"; + public IconSource IconSource { get; set; } = new SymbolIconSource() { Symbol = Symbol.Document, FontSize = 20.0, Foreground = Avalonia.Media.Brushes.White }; + public bool Selected { get; set; } = false; + public required EndpointsNavigationViewModel Content { get; init; } +} \ No newline at end of file diff --git a/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsViewModel.cs b/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsViewModel.cs index 69863fd..2275a75 100644 --- a/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsViewModel.cs +++ b/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsViewModel.cs @@ -1,42 +1,26 @@ using Avalonia.Collections; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using CommunityToolkit.Mvvm.Messaging; -using Needlework.Net.Messages; using System; using System.Linq; -using System.Net.Http; namespace Needlework.Net.ViewModels.Pages.Endpoints; -public partial class EndpointsViewModel : ObservableObject, IRecipient +public partial class EndpointsViewModel : ObservableObject { - public HttpClient HttpClient { get; } + public IAvaloniaList Plugins { get; } + public IAvaloniaList Query { get; } - public string Title => "Endpoints"; - public Action OnClicked; - public IAvaloniaList Plugins { get; } = new AvaloniaList(); - public IAvaloniaList Query { get; } = new AvaloniaList(); - - [ObservableProperty] private bool _isBusy = true; [ObservableProperty] private string _search = string.Empty; [ObservableProperty] private string? _selectedQuery = string.Empty; - public EndpointsViewModel(HttpClient httpClient, Action onClicked) + public Action OnClicked { get; } + + public EndpointsViewModel(IAvaloniaList plugins, Action onClicked) { - HttpClient = httpClient; + Plugins = plugins; + Query = plugins; OnClicked = onClicked; - - WeakReferenceMessenger.Default.Register(this); - } - - public void Receive(DataReadyMessage message) - { - IsBusy = false; - Plugins.Clear(); - Plugins.AddRange(message.Value.Plugins.Keys); - Query.Clear(); - Query.AddRange(Plugins); } partial void OnSearchChanged(string value) diff --git a/Needlework.Net/Views/Pages/Endpoints/EndpointsContainerView.axaml b/Needlework.Net/Views/Pages/Endpoints/EndpointsNavigationView.axaml similarity index 89% rename from Needlework.Net/Views/Pages/Endpoints/EndpointsContainerView.axaml rename to Needlework.Net/Views/Pages/Endpoints/EndpointsNavigationView.axaml index c7e8122..e3eed3c 100644 --- a/Needlework.Net/Views/Pages/Endpoints/EndpointsContainerView.axaml +++ b/Needlework.Net/Views/Pages/Endpoints/EndpointsNavigationView.axaml @@ -6,8 +6,8 @@ xmlns:avalonEdit="https://github.com/avaloniaui/avaloniaedit" xmlns:i="https://github.com/projektanker/icons.avalonia" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="Needlework.Net.Views.Pages.Endpoints.EndpointsContainerView" - x:DataType="vm:EndpointsContainerViewModel"> + x:Class="Needlework.Net.Views.Pages.Endpoints.EndpointsNavigationView" + x:DataType="vm:EndpointsNavigationViewModel"> diff --git a/Needlework.Net/Views/Pages/Endpoints/EndpointsContainerView.axaml.cs b/Needlework.Net/Views/Pages/Endpoints/EndpointsNavigationView.axaml.cs similarity index 55% rename from Needlework.Net/Views/Pages/Endpoints/EndpointsContainerView.axaml.cs rename to Needlework.Net/Views/Pages/Endpoints/EndpointsNavigationView.axaml.cs index f1e4494..f82c8e4 100644 --- a/Needlework.Net/Views/Pages/Endpoints/EndpointsContainerView.axaml.cs +++ b/Needlework.Net/Views/Pages/Endpoints/EndpointsNavigationView.axaml.cs @@ -2,9 +2,9 @@ using Avalonia.Controls; namespace Needlework.Net.Views.Pages.Endpoints; -public partial class EndpointsContainerView : UserControl +public partial class EndpointsNavigationView : UserControl { - public EndpointsContainerView() + public EndpointsNavigationView() { InitializeComponent(); } diff --git a/Needlework.Net/Views/Pages/Endpoints/EndpointsTabView.axaml b/Needlework.Net/Views/Pages/Endpoints/EndpointsTabView.axaml new file mode 100644 index 0000000..64441ea --- /dev/null +++ b/Needlework.Net/Views/Pages/Endpoints/EndpointsTabView.axaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Needlework.Net/Views/Pages/Endpoints/EndpointsTabView.axaml.cs b/Needlework.Net/Views/Pages/Endpoints/EndpointsTabView.axaml.cs new file mode 100644 index 0000000..9c910d9 --- /dev/null +++ b/Needlework.Net/Views/Pages/Endpoints/EndpointsTabView.axaml.cs @@ -0,0 +1,19 @@ +using Avalonia.Controls; +using Needlework.Net.ViewModels.Pages.Endpoints; +using System.Collections; + +namespace Needlework.Net.Views.Pages.Endpoints; + +public partial class EndpointsTabView : UserControl +{ + public EndpointsTabView() + { + InitializeComponent(); + } + + private void TabView_TabCloseRequested(FluentAvalonia.UI.Controls.TabView sender, FluentAvalonia.UI.Controls.TabViewTabCloseRequestedEventArgs args) + { + if (args.Tab.Content is EndpointItem item) + ((IList)sender.TabItems).Remove(item); + } +} \ No newline at end of file diff --git a/Needlework.Net/Views/Pages/Endpoints/EndpointsView.axaml b/Needlework.Net/Views/Pages/Endpoints/EndpointsView.axaml index c78f2f5..e0f6a12 100644 --- a/Needlework.Net/Views/Pages/Endpoints/EndpointsView.axaml +++ b/Needlework.Net/Views/Pages/Endpoints/EndpointsView.axaml @@ -8,9 +8,7 @@ Name="EndpointsControl" x:Class="Needlework.Net.Views.Pages.Endpoints.EndpointsView" x:DataType="vm:EndpointsViewModel"> - - + @@ -28,5 +26,4 @@ - \ No newline at end of file