- add: ContentView think

This commit is contained in:
2025-01-11 20:39:58 +03:00
parent 77a1134823
commit 6d46e02d6f
10 changed files with 388 additions and 42 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,48 +1,351 @@
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.Views.Pages;
using Nebula.Shared;
using Nebula.Shared.Models;
using Nebula.Shared.Services;
using Nebula.Shared.Utils;
namespace Nebula.Launcher.ViewModels;
[ViewModelRegister(typeof(ContentBrowserView))]
public sealed partial class ContentBrowserViewModel : ViewModelBase
{
public ObservableCollection<ContentEntry> Entries = new();
private readonly IServiceProvider _provider;
private readonly ContentService _contentService;
private readonly CancellationService _cancellationService;
private readonly DebugService _debugService;
private readonly PopupMessageService _popupService;
public ObservableCollection<ContentEntry> Entries { get; } = new();
private readonly List<ContentEntry> _root = new();
private List<string> _history = new();
[ObservableProperty] private string _message = "";
[ObservableProperty] private string _searchText = "";
private ContentEntry? _selectedEntry;
public ContentEntry? SelectedEntry
{
get => _selectedEntry;
set
{
_selectedEntry = value;
Entries.Clear();
Console.WriteLine("Entries clear!");
if(value == null) return;
foreach (var (_,entryCh) in value.Childs)
{
Entries.Add(entryCh);
}
}
}
public ContentBrowserViewModel() : base()
{
var a = new ContentEntry(this, "A:", "");
var b = new ContentEntry(this, "B", "");
a.TryAddChild(b);
Entries.Add(a);
}
public ContentBrowserViewModel(IServiceProvider provider) : base(provider)
public ContentBrowserViewModel(IServiceProvider provider, ContentService contentService, CancellationService cancellationService,
FileService fileService, HubService hubService, DebugService debugService, PopupMessageService popupService) : base(provider)
{
_provider = provider;
_contentService = contentService;
_cancellationService = cancellationService;
_debugService = debugService;
_popupService = popupService;
hubService.HubServerChangedEventArgs += HubServerChangedEventArgs;
hubService.HubServerLoaded += GoHome;
}
private void GoHome()
{
SelectedEntry = null;
foreach (var entry in _root)
{
Entries.Add(entry);
}
}
private void HubServerChangedEventArgs(HubServerChangedEventArgs obj)
{
if(obj.Action == HubServerChangeAction.Clear) _root.Clear();
if (obj.Action == HubServerChangeAction.Add)
{
foreach (var info in obj.Items)
{
_root.Add(new ContentEntry(this, ToContentUrl(info.Address.ToRobustUrl()),info.Address));
}
};
}
public async void Go(ContentPath path)
{
if (path.Pathes.Count == 0)
{
SearchText = "";
GoHome();
return;
}
if (path.Pathes[0] != "content:" || path.Pathes.Count < 3)
{
return;
}
SearchText = path.Path;
path.Pathes.RemoveAt(0);
path.Pathes.RemoveAt(0);
var serverUrl = path.Pathes[0];
path.Pathes.RemoveAt(0);
_debugService.Debug(path.Path + " " + serverUrl +" "+ SelectedEntry?.ServerName);
try
{
ContentEntry entry;
if (serverUrl == SelectedEntry?.ServerName)
entry = SelectedEntry.GetRoot();
else
entry = await CreateEntry(serverUrl);
if (!entry.TryGetEntry(path, out var centry))
{
throw new Exception("Not found!");
}
SelectedEntry = centry;
}
catch (Exception e)
{
Console.WriteLine(e);
_popupService.Popup(e);
//throw;
}
}
public void OnBackEnter()
{
Go(new ContentPath(GetHistory()));
}
public void OnGoEnter()
{
Go(new ContentPath(SearchText));
}
private async Task<ContentEntry> CreateEntry(string serverUrl)
{
var rurl = serverUrl.ToRobustUrl();
var info = await _contentService.GetBuildInfo(rurl, _cancellationService.Token);
var loading = _provider.GetService<LoadingContextViewModel>()!;
loading.LoadingName = "Loading entry";
_popupService.Popup(loading);
var items = await _contentService.EnsureItems(info.RobustManifestInfo, loading,
_cancellationService.Token);
var rootEntry = new ContentEntry(this,ToContentUrl(rurl), serverUrl);
foreach (var item in items)
{
var path = new ContentPath(item.Path);
rootEntry.CreateItem(path, item);
}
loading.Dispose();
return rootEntry;
}
private void AppendHistory(string str)
{
if(_history.Count >= 10) _history.RemoveAt(9);
_history.Insert(0, str);
}
private string GetHistory()
{
if (_history.Count == 0) return "";
var h = _history[0];
_history.RemoveAt(0);
return h;
}
private string ToContentUrl(RobustUrl serverUrl)
{
var port = serverUrl.Uri.Port != -1 ? (":"+serverUrl.Uri.Port) : "";
return "content://" + serverUrl.Uri.Host + port;
}
}
public sealed class ContentEntry
public class ContentEntry
{
private readonly ContentBrowserViewModel _viewModel;
public static IImage DirImage = new Bitmap(AssetLoader.Open(new Uri("avares://Nebula.Launcher/Assets/dir.png")));
public static IImage IconImage = new Bitmap(AssetLoader.Open(new Uri("avares://Nebula.Launcher/Assets/file.png")));
public RobustManifestItem? Item;
public bool IsDirectory => Item == null;
public string Name { get; private set; }
public string ServerName { get; private set; }
public IImage IconPath { get; set; } = DirImage;
public ContentEntry? Parent { get; private set; }
public bool IsRoot => Parent == null;
private readonly Dictionary<string, ContentEntry> _childs = new();
public IReadOnlyDictionary<string, ContentEntry> Childs => _childs.ToFrozenDictionary();
public bool TryGetChild(string name,[NotNullWhen(true)] out ContentEntry? child)
{
return _childs.TryGetValue(name, out child);
}
public bool TryAddChild(ContentEntry contentEntry)
{
if(_childs.TryAdd(contentEntry.Name, contentEntry))
{
contentEntry.Parent = this;
return true;
}
return false;
}
internal ContentEntry(ContentBrowserViewModel viewModel, string name, string serverName)
{
Name = name;
ServerName = serverName;
_viewModel = viewModel;
}
public ContentPath GetPath()
{
if (Parent != null)
{
var path = Parent.GetPath();
path.Pathes.Add(Name);
return path;
}
return new ContentPath(Name);
}
public ContentEntry GetOrCreateDirectory(ContentPath rootPath)
{
if (rootPath.Pathes.Count == 0) return this;
var fName = rootPath.Pathes[0];
rootPath.Pathes.RemoveAt(0);
if(!TryGetChild(fName, out var child))
{
child = new ContentEntry(_viewModel, fName, ServerName);
TryAddChild(child);
}
return child.GetOrCreateDirectory(rootPath);
}
public ContentEntry GetRoot()
{
if (Parent == null) return this;
return Parent.GetRoot();
}
public ContentEntry CreateItem(ContentPath path, RobustManifestItem item)
{
var dir = path.GetDirectory();
var dirEntry = GetOrCreateDirectory(dir);
var entry = new ContentEntry(_viewModel, path.GetName(), ServerName)
{
Item = item
};
dirEntry.TryAddChild(entry);
entry.IconPath = IconImage;
return entry;
}
public bool TryGetEntry(ContentPath path, out ContentEntry? entry)
{
entry = null;
if (path.Pathes.Count == 0)
{
entry = this;
return true;
}
var fName = path.Pathes[0];
path.Pathes.RemoveAt(0);
if(!TryGetChild(fName, out var child))
{
return false;
}
return child.TryGetEntry(path, out entry);
}
public void OnPathGo()
{
_viewModel.Go(GetPath());
}
}
public sealed class ContentPath
public struct ContentPath
{
public RobustUrl ServerUrl;
public List<string> Pathes;
public ContentPath(List<string> pathes)
{
Pathes = pathes;
}
public ContentPath(string path)
{
Pathes = path.Split("/").ToList();
}
public ContentPath GetDirectory()
{
var p = Pathes.ToList();
p.RemoveAt(Pathes.Count - 1);
return new ContentPath(p);
}
public string GetName()
{
return Pathes.Last();
}
public string Path => string.Join("/", Pathes);
}

View File

@@ -45,7 +45,8 @@ public partial class MainViewModel : ViewModelBase
private readonly List<ListItemTemplate> _templates =
[
new ListItemTemplate(typeof(AccountInfoViewModel), "Account", "Account"),
new ListItemTemplate(typeof(ServerListViewModel), "HomeRegular", "Servers")
new ListItemTemplate(typeof(ServerListViewModel), "HomeRegular", "Servers"),
new ListItemTemplate(typeof(ContentBrowserViewModel), "HomeRegular", "Content")
];
[ObservableProperty]

View File

@@ -34,9 +34,15 @@ public partial class ServerListViewModel : ViewModelBase
_serviceProvider = serviceProvider;
_hubService = hubService;
hubService.HubServerChangedEventArgs += HubServerChangedEventArgs;
hubService.HubServerLoaded += HubServerLoaded;
OnSearchChange += OnChangeSearch;
}
private void HubServerLoaded()
{
SortServers();
}
private void OnChangeSearch()
{
SortServers();
@@ -62,8 +68,6 @@ public partial class ServerListViewModel : ViewModelBase
{
UnsortedServers.Clear();
}
SortServers();
}
private void SortServers()

View File

@@ -1,39 +1,69 @@
<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:viewModels="clr-namespace:Nebula.Launcher.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Nebula.Launcher.Views.Pages.ContentBrowserView"
x:DataType="viewModels:ContentBrowserViewModel">
<UserControl
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d"
x:Class="Nebula.Launcher.Views.Pages.ContentBrowserView"
x:DataType="viewModels:ContentBrowserViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Nebula.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.DataContext>
<viewModels:ContentBrowserViewModel />
</Design.DataContext>
<StackPanel>
<Border
BorderThickness="2,0,0,0"
CornerRadius="10"
Grid.Row="1">
<StackPanel Margin="5">
<Border BorderThickness="2,0,0,0" CornerRadius="10">
<Grid ColumnDefinitions="*,40,40" RowDefinitions="*">
<TextBox
Margin="0"
Text="{Binding SearchText}"
VerticalAlignment="Center"
Watermark="Server name..." />
Watermark="Path..." />
<Button
Command="{Binding OnBackEnter}"
Grid.Column="1"
Padding="10">
<Image Source="/Assets/filter.png" />
<Image Source="/Assets/back.png" />
</Button>
<Button
Command="{Binding OnGoEnter}"
Grid.Column="2"
Padding="10">
<Image Source="/Assets/refresh.png" />
<Image Source="/Assets/go.png" />
</Button>
</Grid>
</Border>
<ScrollViewer Margin="0,5,10,5">
<ItemsControl
Background="#00000000"
ItemsSource="{Binding Entries}"
Padding="0">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type viewModels:ContentEntry}">
<Button
Command="{Binding OnPathGo}"
CornerRadius="0"
HorizontalAlignment="Stretch">
<StackPanel Orientation="Horizontal" Spacing="15">
<Border
Background="#00000000"
BorderThickness="0,0,2,0"
CornerRadius="0">
<Image
Height="15"
Margin="10,0,10,0"
Source="{Binding IconPath}" />
</Border>
<Label>
<TextBlock Text="{Binding Name}" />
</Label>
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</StackPanel>
</UserControl>

View File

@@ -15,7 +15,10 @@
<viewModels:ServerListViewModel />
</Design.DataContext>
<Grid ColumnDefinitions="*" RowDefinitions="*,40" Margin="15">
<Grid
ColumnDefinitions="*"
Margin="5"
RowDefinitions="*,40">
<ScrollViewer Margin="0,0,0,10" Padding="0,0,8,0">
<ItemsControl
Background="#00000000"
@@ -122,29 +125,32 @@
<Panel Grid.Column="1" Grid.Row="1">
<Button
Command="{Binding RunInstance}"
IsVisible="{Binding RunVisible}"
HorizontalAlignment="Stretch"
CornerRadius="10,0,10,10"
HorizontalAlignment="Stretch"
IsVisible="{Binding RunVisible}"
VerticalAlignment="Stretch">
<Label HorizontalAlignment="Center" VerticalAlignment="Center">
Play
</Label>
</Button>
<Grid Grid.ColumnDefinitions="*,*" IsVisible="{Binding !RunVisible}">
<Button Command="{Binding ReadLog}"
CornerRadius="10,0,0,10"
Margin="0,0,1,0"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch">
<Button
Command="{Binding ReadLog}"
CornerRadius="10,0,0,10"
HorizontalAlignment="Stretch"
Margin="0,0,1,0"
VerticalAlignment="Stretch">
<Label HorizontalAlignment="Center" VerticalAlignment="Center">
Log
</Label>
</Button>
<Button Grid.Column="1" HorizontalAlignment="Stretch"
CornerRadius="0,0,10,0"
Margin="1,0,0,0"
VerticalAlignment="Stretch"
Command="{Binding StopInstance}">
<Button
Command="{Binding StopInstance}"
CornerRadius="0,0,10,0"
Grid.Column="1"
HorizontalAlignment="Stretch"
Margin="1,0,0,0"
VerticalAlignment="Stretch">
<Label HorizontalAlignment="Center" VerticalAlignment="Center">
Stop
</Label>

View File

@@ -9,6 +9,7 @@ public class HubService
private readonly RestService _restService;
public Action<HubServerChangedEventArgs>? HubServerChangedEventArgs;
public Action? HubServerLoaded;
private bool _isUpdating = false;
public HubService(ConfigurationService configurationService, RestService restService)
@@ -35,6 +36,7 @@ public class HubService
}
_isUpdating = false;
HubServerLoaded?.Invoke();
}
}