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