Add tabs view to endpoints

This commit is contained in:
BlossomiShymae
2024-12-05 12:31:52 -06:00
parent 58556283f0
commit dede2e909c
11 changed files with 177 additions and 62 deletions

View File

@@ -55,6 +55,9 @@
<Compile Update="Views\MainWindow\MainWindowView.axaml.cs">
<DependentUpon>MainWindowView.axaml</DependentUpon>
</Compile>
<Compile Update="Views\Pages\Endpoints\EndpointsNavigationView.axaml.cs">
<DependentUpon>EndpointsNavigationView.axaml</DependentUpon>
</Compile>
<Compile Update="Views\Pages\Endpoints\EndpointView.axaml.cs">
<DependentUpon>EndpointView.axaml</DependentUpon>
</Compile>

View File

@@ -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()

View File

@@ -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;
}
}

View File

@@ -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<string?, Guid> _onEndpointNavigation;
public EndpointsNavigationViewModel(IAvaloniaList<string> plugins, Action<string?, Guid> 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);
}
}

View File

@@ -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<DataReadyMessage>
{
public IAvaloniaList<string> Plugins { get; } = new AvaloniaList<string>();
public IAvaloniaList<EndpointItem> Endpoints { get; } = new AvaloniaList<EndpointItem>();
[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; }
}

View File

@@ -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<DataReadyMessage>
public partial class EndpointsViewModel : ObservableObject
{
public HttpClient HttpClient { get; }
public IAvaloniaList<string> Plugins { get; }
public IAvaloniaList<string> Query { get; }
public string Title => "Endpoints";
public Action<ObservableObject> OnClicked;
public IAvaloniaList<string> Plugins { get; } = new AvaloniaList<string>();
public IAvaloniaList<string> Query { get; } = new AvaloniaList<string>();
[ObservableProperty] private bool _isBusy = true;
[ObservableProperty] private string _search = string.Empty;
[ObservableProperty] private string? _selectedQuery = string.Empty;
public EndpointsViewModel(HttpClient httpClient, Action<ObservableObject> onClicked)
public Action<ObservableObject> OnClicked { get; }
public EndpointsViewModel(IAvaloniaList<string> plugins, Action<ObservableObject> 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)

View File

@@ -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">
<Grid RowDefinitions="auto,*"
ColumnDefinitions="*"
Margin="16">

View File

@@ -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();
}

View File

@@ -0,0 +1,35 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="https://github.com/projektanker/icons.avalonia"
xmlns:vm="using:Needlework.Net.ViewModels.Pages.Endpoints"
xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:controls="using:Needlework.Net.Controls"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Needlework.Net.Views.Pages.Endpoints.EndpointsTabView"
x:DataType="vm:EndpointsTabViewModel">
<controls:BusyArea IsBusy="{Binding IsBusy}"
BusyText="Loading...">
<Grid>
<ui:TabView TabItems="{Binding Endpoints}"
AddTabButtonCommand="{Binding AddEndpointCommand}"
TabCloseRequested="TabView_TabCloseRequested">
<ui:TabView.TabItemTemplate>
<DataTemplate DataType="vm:EndpointItem">
<ui:TabViewItem Header="{Binding Header}"
IconSource="{Binding IconSource}"
IsSelected="{Binding Selected}"
Content="{Binding}">
<ui:TabViewItem.ContentTemplate>
<DataTemplate DataType="vm:EndpointItem">
<ContentControl Content="{Binding Content}"/>
</DataTemplate>
</ui:TabViewItem.ContentTemplate>
</ui:TabViewItem>
</DataTemplate>
</ui:TabView.TabItemTemplate>
</ui:TabView>
</Grid>
</controls:BusyArea>
</UserControl>

View File

@@ -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);
}
}

View File

@@ -8,8 +8,6 @@
Name="EndpointsControl"
x:Class="Needlework.Net.Views.Pages.Endpoints.EndpointsView"
x:DataType="vm:EndpointsViewModel">
<controls:BusyArea IsBusy="{Binding IsBusy}"
BusyText="Loading...">
<Grid RowDefinitions="auto,auto,*" ColumnDefinitions="*">
<TextBox Watermark="Search" Margin="0 4" Text="{Binding Search}" Grid.Row="1" Grid.Column="0"/>
<ScrollViewer Grid.Row="2" Grid.Column="0">
@@ -28,5 +26,4 @@
</ItemsRepeater>
</ScrollViewer>
</Grid>
</controls:BusyArea>
</UserControl>