1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 13:28:33 +00:00

Sidebar - Added subitems support

This commit is contained in:
Robert 2023-07-07 22:55:02 +02:00
parent 229d93901b
commit 99a365be0b
33 changed files with 572 additions and 162 deletions

View File

@ -62,7 +62,15 @@ public abstract class RoutableScreen<TScreen> : ActivatableViewModelBase, IRouta
void IRoutableScreen.InternalChangeScreen(object? screen)
{
Screen = screen as TScreen;
if (screen == null)
{
Screen = null;
return;
}
if (screen is not TScreen typedScreen)
throw new ArtemisRoutingException($"Screen cannot be hosted, {screen.GetType().Name} is not assignable to {typeof(TScreen).Name}.");
Screen = typedScreen;
}
async Task IRoutableScreen.InternalOnNavigating(NavigationArguments args, CancellationToken cancellationToken)

View File

@ -0,0 +1,17 @@
namespace Artemis.UI.Shared.Routing.ParameterParsers;
internal class IntParameterParser : IRouteParameterParser
{
/// <inheritdoc />
public bool IsMatch(RouteSegment segment, string source)
{
return int.TryParse(source, out _);
}
/// <inheritdoc />
public object GetValue(RouteSegment segment, string source)
{
return int.Parse(source);
}
}

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DryIoc;
namespace Artemis.UI.Shared.Routing;

View File

@ -48,11 +48,14 @@ public partial class RouteSegment
private IRouteParameterParser GetParameterParser(string parameterType)
{
if (parameterType == "guid")
return new GuidParameterParser();
return parameterType switch
{
"guid" => new GuidParameterParser(),
"int" => new IntParameterParser(),
_ => new StringParameterParser()
};
// Default to a string parser which just returns the segment as is
return new StringParameterParser();
}
[GeneratedRegex(@"\{(\w+):(\w+)\}")]

View File

@ -13,9 +13,15 @@
<!-- Add Styles Here -->
<Style Selector="ListBox.sidebar-listbox ListBoxItem">
<Setter Property="MinHeight" Value="35" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="MinHeight" Value="{DynamicResource NavigationViewItemOnLeftMinHeight}" />
</Style>
<Style Selector="ListBox.sidebar-listbox ContentPresenter">
<Setter Property="Margin" Value="0" />
<!-- <Style Selector="ListBox.sidebar-listbox ContentPresenter"> -->
<!-- <Setter Property="Margin" Value="0" /> -->
<!-- </Style> -->
<Style Selector="ListBox.sidebar-listbox ListBoxItem /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="MinHeight" Value="{DynamicResource NavigationViewItemOnLeftMinHeight}" />
<Setter Property="CornerRadius" Value="{DynamicResource OverlayCornerRadius}" />
</Style>
</Styles>

View File

@ -43,4 +43,16 @@
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="Screens\Workshop\Layout\LayoutDetailsView.axaml" />
<AdditionalFiles Include="Screens\Workshop\Layout\LayoutListView.axaml" />
<AdditionalFiles Include="Screens\Workshop\Profile\ProfileDetailsView.axaml" />
<AdditionalFiles Include="Screens\Workshop\Profile\ProfileListView.axaml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Screens\Workshop\Categories\Profile\ProfileDetailsView.axaml" />
<UpToDateCheckInput Remove="Screens\Workshop\Categories\Profile\ProfileListView.axaml" />
</ItemGroup>
</Project>

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reactive;
using Artemis.Core;
@ -30,6 +31,8 @@ using Artemis.UI.Shared;
using Artemis.UI.Shared.Routing;
using Artemis.WebClient.Updating;
using DryIoc;
using DynamicData;
using Material.Icons;
using ReactiveUI;
namespace Artemis.UI.DryIoc.Factories;
@ -137,7 +140,7 @@ public class SidebarVmFactory : ISidebarVmFactory
{
_container = container;
}
public SidebarCategoryViewModel SidebarCategoryViewModel(ProfileCategory profileCategory)
{
return _container.Resolve<SidebarCategoryViewModel>(new object[] { profileCategory });

View File

@ -5,6 +5,8 @@ using Artemis.UI.Screens.Settings;
using Artemis.UI.Screens.Settings.Updating;
using Artemis.UI.Screens.SurfaceEditor;
using Artemis.UI.Screens.Workshop;
using Artemis.UI.Screens.Workshop.Layout;
using Artemis.UI.Screens.Workshop.Profile;
using Artemis.UI.Shared.Routing;
namespace Artemis.UI.Routing;
@ -14,8 +16,15 @@ public static class Routes
public static List<IRouterRegistration> ArtemisRoutes = new()
{
new RouteRegistration<HomeViewModel>("home"),
new RouteRegistration<WorkshopViewModel>("workshop"),
new RouteRegistration<ProfileListViewModel>("workshop/profiles/{page:int}"),
new RouteRegistration<ProfileDetailsViewModel>("workshop/profiles/{entryId:guid}"),
new RouteRegistration<LayoutListViewModel>("workshop/layouts/{page:int}"),
new RouteRegistration<LayoutDetailsViewModel>("workshop/layouts/{entryId:guid}"),
new RouteRegistration<SurfaceEditorViewModel>("surface-editor"),
new RouteRegistration<SettingsViewModel>("settings")
{
Children = new List<IRouterRegistration>
@ -25,7 +34,7 @@ public static class Routes
new RouteRegistration<DevicesTabViewModel>("devices"),
new RouteRegistration<ReleasesTabViewModel>("releases")
{
Children = new List<IRouterRegistration>()
Children = new List<IRouterRegistration>
{
new RouteRegistration<ReleaseDetailsViewModel>("{releaseId:guid}")
}

View File

@ -99,7 +99,6 @@
</Style>
<Style Selector="ListBox.sidebar-listbox ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="(i:Interaction.Behaviors)">
<i:BehaviorCollectionTemplate>
<i:BehaviorCollection>

View File

@ -76,8 +76,8 @@
x:Name="ProfileIcon"
VerticalAlignment="Center"
ConfigurationIcon="{CompiledBinding ProfileConfiguration.Icon}"
Width="20"
Height="20"
Width="22"
Height="22"
Margin="0 0 5 0">
<shared:ProfileConfigurationIcon.Transitions>
<Transitions>

View File

@ -4,11 +4,11 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:vm="clr-namespace:Artemis.UI.Screens.Sidebar;assembly=Artemis.UI"
x:DataType="vm:SidebarScreenViewModel"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Sidebar.SidebarScreenView">
<StackPanel Orientation="Horizontal">
<avalonia:MaterialIcon Kind="{CompiledBinding Icon}" Width="16" Height="16" />
<TextBlock FontSize="12" Margin="10 0" VerticalAlignment="Center" Text="{CompiledBinding DisplayName}" />
x:Class="Artemis.UI.Screens.Sidebar.SidebarScreenView"
x:DataType="vm:SidebarScreenViewModel">
<StackPanel Orientation="Horizontal" Background="Transparent" PointerReleased="InputElement_OnPointerReleased" DoubleTapped="InputElement_OnDoubleTapped">
<avalonia:MaterialIcon Kind="{CompiledBinding Icon}" Width="18" Height="18" />
<TextBlock Margin="10 0" VerticalAlignment="Center" FontSize="13" Text="{CompiledBinding DisplayName}" />
</StackPanel>
</UserControl>

View File

@ -1,13 +1,25 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Sidebar;
public partial class SidebarScreenView : UserControl
public partial class SidebarScreenView : ReactiveUserControl<SidebarScreenViewModel>
{
public SidebarScreenView()
{
InitializeComponent();
}
private void InputElement_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
// if (ViewModel != null)
// ViewModel.IsExpanded = !ViewModel.IsExpanded;
}
private void InputElement_OnDoubleTapped(object? sender, TappedEventArgs e)
{
e.Handled = true;
}
}

View File

@ -1,25 +1,71 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Routing;
using Artemis.UI.Shared.Services;
using Avalonia.Threading;
using Material.Icons;
using ReactiveUI;
namespace Artemis.UI.Screens.Sidebar;
public class SidebarScreenViewModel : ViewModelBase
{
public SidebarScreenViewModel(MaterialIconKind icon, string displayName, string path)
private bool _isExpanded;
public SidebarScreenViewModel(MaterialIconKind icon, string displayName, string path, string? rootPath = null, ObservableCollection<SidebarScreenViewModel>? screens = null)
{
Icon = icon;
Path = path;
RootPath = rootPath ?? path;
DisplayName = displayName;
Screens = screens ?? new ObservableCollection<SidebarScreenViewModel>();
}
public MaterialIconKind Icon { get; }
public string Path { get; }
public string RootPath { get; }
public ObservableCollection<SidebarScreenViewModel> Screens { get; }
public bool IsExpanded
{
get => _isExpanded;
set => RaiseAndSetIfChanged(ref _isExpanded, value);
}
public bool Matches(string? path)
{
if (path == null)
return false;
return path.StartsWith(Path, StringComparison.InvariantCultureIgnoreCase);
return path.StartsWith(RootPath, StringComparison.InvariantCultureIgnoreCase);
}
public SidebarScreenViewModel? GetMatch(string path)
{
foreach (SidebarScreenViewModel sidebarScreenViewModel in Screens)
{
SidebarScreenViewModel? match = sidebarScreenViewModel.GetMatch(path);
if (match != null)
return match;
}
return Screens.FirstOrDefault(s => s.Matches(path));
}
public void ExpandIfRequired(SidebarScreenViewModel selected)
{
if (selected == this && Screens.Any())
{
IsExpanded = true;
return;
}
if (Screens.Contains(selected))
IsExpanded = true;
foreach (SidebarScreenViewModel sidebarScreenViewModel in Screens)
sidebarScreenViewModel.ExpandIfRequired(selected);
}
}

View File

@ -3,7 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:sidebar="clr-namespace:Artemis.UI.Screens.Sidebar"
mc:Ignorable="d" d:DesignWidth="240" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Sidebar.SidebarView"
@ -20,11 +20,23 @@
</Grid>
<!-- Built-in screens -->
<ListBox Classes="sidebar-listbox"
Grid.Row="1"
Margin="10 2"
ItemsSource="{CompiledBinding SidebarScreens}"
SelectedItem="{CompiledBinding SelectedSidebarScreen}" />
<TreeView Grid.Row="1"
Margin="10 2"
ItemsSource="{CompiledBinding SidebarScreen.Screens}"
SelectedItem="{CompiledBinding SelectedScreen}"
ItemContainerTheme="{StaticResource MenuTreeViewItem}">
<TreeView.Styles>
<Style Selector="TreeViewItem">
<Setter Property="IsExpanded" Value="{CompiledBinding IsExpanded, Mode=TwoWay, DataType=sidebar:SidebarScreenViewModel}" />
</Style>
</TreeView.Styles>
<TreeView.ItemTemplate>
<TreeDataTemplate ItemsSource="{CompiledBinding Screens}">
<ContentControl Content="{CompiledBinding}" />
</TreeDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<Border Grid.Row="2" Margin="8" Height="1" Background="{DynamicResource ButtonBorderBrush}"></Border>
<!-- Categories -->
@ -41,51 +53,51 @@
<!-- Bottom buttons -->
<Border Grid.Row="4" Margin="8" Height="1" Background="{DynamicResource ButtonBorderBrush}"></Border>
<WrapPanel Grid.Row="5" Orientation="Horizontal" HorizontalAlignment="Left" Margin="5 0 5 5">
<controls:HyperlinkButton Classes="icon-button"
Width="44"
Height="44"
ToolTip.Tip="View website"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5"
NavigateUri="https://artemis-rgb.com?mtm_campaign=artemis&amp;mtm_kwd=sidebar">
<ui:HyperlinkButton Classes="icon-button"
Width="44"
Height="44"
ToolTip.Tip="View website"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5"
NavigateUri="https://artemis-rgb.com?mtm_campaign=artemis&amp;mtm_kwd=sidebar">
<avalonia:MaterialIcon Kind="Web" Width="20" Height="20" />
</controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button"
Width="44"
Height="44"
ToolTip.Tip="View GitHub repository"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5"
NavigateUri="https://github.com/Artemis-RGB/Artemis">
</ui:HyperlinkButton>
<ui:HyperlinkButton Classes="icon-button"
Width="44"
Height="44"
ToolTip.Tip="View GitHub repository"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5"
NavigateUri="https://github.com/Artemis-RGB/Artemis">
<avalonia:MaterialIcon Kind="Github" Width="20" Height="20" />
</controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button"
Width="44"
Height="44"
ToolTip.Tip="View Wiki"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5"
NavigateUri="https://wiki.artemis-rgb.com?mtm_campaign=artemis&amp;mtm_kwd=sidebar">
</ui:HyperlinkButton>
<ui:HyperlinkButton Classes="icon-button"
Width="44"
Height="44"
ToolTip.Tip="View Wiki"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5"
NavigateUri="https://wiki.artemis-rgb.com?mtm_campaign=artemis&amp;mtm_kwd=sidebar">
<avalonia:MaterialIcon Kind="BookOpenOutline" Width="20" Height="20" />
</controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button"
Width="44"
Height="44"
ToolTip.Tip="Join our Discord"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5"
NavigateUri="https://discord.gg/S3MVaC9">
</ui:HyperlinkButton>
<ui:HyperlinkButton Classes="icon-button"
Width="44"
Height="44"
ToolTip.Tip="Join our Discord"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5"
NavigateUri="https://discord.gg/S3MVaC9">
<avalonia:MaterialIcon Kind="Chat" Width="20" Height="20" />
</controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button"
Width="44"
Height="44"
ToolTip.Tip="View donation options"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5"
NavigateUri="https://wiki.artemis-rgb.com/en/donating?mtm_campaign=artemis&amp;mtm_kwd=sidebar">
</ui:HyperlinkButton>
<ui:HyperlinkButton Classes="icon-button"
Width="44"
Height="44"
ToolTip.Tip="View donation options"
ToolTip.Placement="Top"
ToolTip.VerticalOffset="-5"
NavigateUri="https://wiki.artemis-rgb.com/en/donating?mtm_campaign=artemis&amp;mtm_kwd=sidebar">
<avalonia:MaterialIcon Kind="Gift" Width="20" Height="20" />
</controls:HyperlinkButton>
</ui:HyperlinkButton>
</WrapPanel>
</Grid>
</UserControl>

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
@ -23,34 +24,38 @@ namespace Artemis.UI.Screens.Sidebar;
public class SidebarViewModel : ActivatableViewModelBase
{
public const string ROOT_SCREEN = "root";
private readonly IRouter _router;
private readonly IWindowService _windowService;
private SidebarScreenViewModel? _selectedSidebarScreen;
private ReadOnlyObservableCollection<SidebarCategoryViewModel> _sidebarCategories = new(new ObservableCollection<SidebarCategoryViewModel>());
private SidebarScreenViewModel? _selectedScreen;
public SidebarViewModel(IRouter router, IProfileService profileService, IWindowService windowService, ISidebarVmFactory sidebarVmFactory)
{
_router = router;
_windowService = windowService;
SidebarScreens = new ObservableCollection<SidebarScreenViewModel>
SidebarScreen = new SidebarScreenViewModel(MaterialIconKind.Abacus, ROOT_SCREEN, "", null, new ObservableCollection<SidebarScreenViewModel>()
{
new(MaterialIconKind.Home, "Home", "home"),
#if DEBUG
new(MaterialIconKind.TestTube, "Workshop", "workshop"),
#endif
new(MaterialIconKind.HomeOutline, "Home", "home"),
new(MaterialIconKind.TestTube, "Workshop", "workshop", null, new ObservableCollection<SidebarScreenViewModel>
{
new(MaterialIconKind.FolderVideo, "Profiles", "workshop/profiles/1", "workshop/profiles"),
new(MaterialIconKind.KeyboardVariant, "Layouts", "workshop/layouts/1", "workshop/layouts"),
}),
new(MaterialIconKind.Devices, "Surface Editor", "surface-editor"),
new(MaterialIconKind.Cog, "Settings", "settings")
};
new(MaterialIconKind.SettingsOutline, "Settings", "settings")
});
AddCategory = ReactiveCommand.CreateFromTask(ExecuteAddCategory);
this.WhenAnyValue(vm => vm.SelectedScreen).WhereNotNull().Subscribe(NavigateToScreen);
this.WhenAnyValue(vm => vm.SelectedScreen).WhereNotNull().Subscribe(s => SidebarScreen.ExpandIfRequired(s));
SourceList<ProfileCategory> profileCategories = new();
this.WhenAnyValue(vm => vm.SelectedSidebarScreen).WhereNotNull().Subscribe(NavigateToScreen);
this.WhenActivated(d =>
{
_router.CurrentPath.WhereNotNull().Subscribe(r => SelectedSidebarScreen = SidebarScreens.FirstOrDefault(s => s.Matches(r))).DisposeWith(d);
_router.CurrentPath.WhereNotNull().Subscribe(r => SelectedScreen = SidebarScreen.GetMatch(r)).DisposeWith(d);
Observable.FromEventPattern<ProfileCategoryEventArgs>(x => profileService.ProfileCategoryAdded += x, x => profileService.ProfileCategoryAdded -= x)
.Subscribe(e => profileCategories.Add(e.EventArgs.ProfileCategory))
@ -75,11 +80,17 @@ public class SidebarViewModel : ActivatableViewModelBase
.DisposeWith(d);
SidebarCategories = categoryViewModels;
SelectedSidebarScreen = SidebarScreens.First();
});
SelectedScreen = SidebarScreen.Screens.First();
}
public ObservableCollection<SidebarScreenViewModel> SidebarScreens { get; }
public SidebarScreenViewModel SidebarScreen { get; }
public SidebarScreenViewModel? SelectedScreen
{
get => _selectedScreen;
set => RaiseAndSetIfChanged(ref _selectedScreen, value);
}
public ReadOnlyObservableCollection<SidebarCategoryViewModel> SidebarCategories
{
@ -87,12 +98,6 @@ public class SidebarViewModel : ActivatableViewModelBase
set => RaiseAndSetIfChanged(ref _sidebarCategories, value);
}
public SidebarScreenViewModel? SelectedSidebarScreen
{
get => _selectedSidebarScreen;
set => RaiseAndSetIfChanged(ref _selectedSidebarScreen, value);
}
public ReactiveCommand<Unit, Unit> AddCategory { get; }
private async Task ExecuteAddCategory()
@ -112,7 +117,7 @@ public class SidebarViewModel : ActivatableViewModelBase
{
try
{
await _router.Navigate(sidebarScreenViewModel.Path, new RouterNavigationOptions {IgnoreOnPartialMatch = true});
await _router.Navigate(sidebarScreenViewModel.Path);
}
catch (Exception e)
{

View File

@ -0,0 +1,21 @@
<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:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:ui="clr-namespace:Artemis.UI"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Workshop.Layout.LayoutDetailsView">
<Border Classes="router-container">
<Grid ColumnDefinitions="300,*" Margin="10">
<Border Classes="card-condensed" Grid.Column="0" Margin="0 0 10 0">
<TextBlock>Side panel</TextBlock>
</Border>
<Border Classes="card-condensed" Grid.Column="1">
<TextBlock>Main panel</TextBlock>
</Border>
</Grid>
</Border>
</UserControl>

View File

@ -0,0 +1,17 @@
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Workshop.Layout;
public partial class LayoutDetailsView : ReactiveUserControl<LayoutDetailsViewModel>
{
public LayoutDetailsView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}

View File

@ -0,0 +1,8 @@
using Artemis.UI.Shared;
namespace Artemis.UI.Screens.Workshop.Layout;
public class LayoutDetailsViewModel : ActivatableViewModelBase
{
}

View File

@ -0,0 +1,22 @@
<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:layout="clr-namespace:Artemis.UI.Screens.Workshop.Layout"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Workshop.Layout.LayoutListView"
x:DataType="layout:LayoutListViewModel">
<Border Classes="router-container">
<Grid ColumnDefinitions="300,*" Margin="10">
<Border Classes="card-condensed" Grid.Column="0" Margin="0 0 10 0">
<TextBlock>Side panel</TextBlock>
</Border>
<Border Classes="card-condensed" Grid.Column="1">
<TextBlock>
<Run Text="Layout list main panel, page: " /><Run Text="{CompiledBinding Page}"></Run>
</TextBlock>
</Border>
</Grid>
</Border>
</UserControl>

View File

@ -0,0 +1,17 @@
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Workshop.Layout;
public partial class LayoutListView : ReactiveUserControl<LayoutListViewModel>
{
public LayoutListView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}

View File

@ -0,0 +1,26 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Routing;
namespace Artemis.UI.Screens.Workshop.Layout;
public class LayoutListViewModel : RoutableScreen<ActivatableViewModelBase, WorkshopListParameters>, IMainScreenViewModel
{
private int _page;
public int Page
{
get => _page;
set => RaiseAndSetIfChanged(ref _page, value);
}
public override Task OnNavigating(WorkshopListParameters parameters, NavigationArguments args, CancellationToken cancellationToken)
{
Page = Math.Max(1, parameters.Page);
return Task.CompletedTask;
}
public ViewModelBase? TitleBarViewModel => null;
}

View File

@ -0,0 +1,8 @@
<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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Workshop.Profile.ProfileDetailsView">
Welcome to Avalonia!
</UserControl>

View File

@ -0,0 +1,17 @@
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Workshop.Profile;
public partial class ProfileDetailsView : ReactiveUserControl<ProfileDetailsViewModel>
{
public ProfileDetailsView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}

View File

@ -0,0 +1,8 @@
using Artemis.UI.Shared;
namespace Artemis.UI.Screens.Workshop.Profile;
public class ProfileDetailsViewModel : ActivatableViewModelBase
{
}

View File

@ -0,0 +1,22 @@
<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:profile="clr-namespace:Artemis.UI.Screens.Workshop.Profile"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Workshop.Profile.ProfileListView"
x:DataType="profile:ProfileListViewModel">
<Border Classes="router-container">
<Grid ColumnDefinitions="300,*" Margin="10">
<Border Classes="card-condensed" Grid.Column="0" Margin="0 0 10 0">
<TextBlock>Side panel</TextBlock>
</Border>
<Border Classes="card-condensed" Grid.Column="1">
<TextBlock>
<Run Text="Profile list main panel, page: " /><Run Text="{CompiledBinding Page}"></Run>
</TextBlock>
</Border>
</Grid>
</Border>
</UserControl>

View File

@ -0,0 +1,17 @@
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Workshop.Profile;
public partial class ProfileListView : ReactiveUserControl<ProfileListViewModel>
{
public ProfileListView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}

View File

@ -0,0 +1,26 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Routing;
namespace Artemis.UI.Screens.Workshop.Profile;
public class ProfileListViewModel : RoutableScreen<ActivatableViewModelBase, WorkshopListParameters>, IMainScreenViewModel
{
private int _page;
public int Page
{
get => _page;
set => RaiseAndSetIfChanged(ref _page, value);
}
public override Task OnNavigating(WorkshopListParameters parameters, NavigationArguments args, CancellationToken cancellationToken)
{
Page = Math.Max(1, parameters.Page);
return Task.CompletedTask;
}
public ViewModelBase? TitleBarViewModel => null;
}

View File

@ -0,0 +1,6 @@
namespace Artemis.UI.Screens.Workshop;
public class WorkshopListParameters
{
public int Page { get; set; }
}

View File

@ -2,35 +2,13 @@
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:builders="clr-namespace:Artemis.UI.Shared.Services.Builders;assembly=Artemis.UI.Shared"
xmlns:controls1="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:attachedProperties="clr-namespace:Artemis.UI.Shared.AttachedProperties;assembly=Artemis.UI.Shared"
xmlns:workshop="clr-namespace:Artemis.UI.Screens.Workshop"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:controls="clr-namespace:Artemis.UI.Shared.Controls;assembly=Artemis.UI.Shared"
xmlns:gradientPicker="clr-namespace:Artemis.UI.Shared.Controls.GradientPicker;assembly=Artemis.UI.Shared"
xmlns:materialIconPicker="clr-namespace:Artemis.UI.Shared.MaterialIconPicker;assembly=Artemis.UI.Shared"
xmlns:workshop1="clr-namespace:Artemis.WebClient.Workshop;assembly=Artemis.WebClient.Workshop"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:ui="clr-namespace:Artemis.UI"
mc:Ignorable="d" d:DesignWidth="800"
x:Class="Artemis.UI.Screens.Workshop.WorkshopView"
x:DataType="workshop:WorkshopViewModel">
<Border Classes="router-container">
<Grid ColumnDefinitions="300,*" Margin="0 10">
<Border Classes="card-condensed" Grid.Column="0" Margin="0 0 10 0">
<ListBox ItemsSource="{CompiledBinding Test}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="workshop1:IGetEntries_Entries_Nodes">
<Panel>
<StackPanel Margin="4">
<TextBlock Text="{CompiledBinding Name}" VerticalAlignment="Center" />
<TextBlock Text="{CompiledBinding Author}" VerticalAlignment="Center" Classes="subtitle" FontSize="13" />
</StackPanel>
</Panel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
<ContentControl Grid.Column="1" VerticalAlignment="Top" HorizontalAlignment="Right" Content="{CompiledBinding CurrentUserViewModel}" Margin="8"></ContentControl>
</Grid>
<TextBlock>Workshop overview</TextBlock>
</Border>
</UserControl>

View File

@ -1,4 +1,3 @@
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Workshop;
@ -9,5 +8,4 @@ public partial class WorkshopView : ReactiveUserControl<WorkshopViewModel>
{
InitializeComponent();
}
}

View File

@ -1,55 +1,13 @@
using System;
using System.Collections.ObjectModel;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.UI.Screens.Workshop.CurrentUser;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
using Artemis.WebClient.Workshop;
using Avalonia.Input;
using ReactiveUI;
using SkiaSharp;
using StrawberryShake;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Routing;
namespace Artemis.UI.Screens.Workshop;
public class WorkshopViewModel : ActivatableViewModelBase, IMainScreenViewModel
public class WorkshopViewModel : RoutableScreen<object>, IMainScreenViewModel
{
private readonly IWorkshopClient _workshopClient;
public WorkshopViewModel(IScreen hostScreen, IWorkshopClient workshopClient, CurrentUserViewModel currentUserViewModel) : base(hostScreen, "workshop")
public WorkshopViewModel()
{
CurrentUserViewModel = currentUserViewModel;
_workshopClient = workshopClient;
DisplayName = "Workshop";
Task.Run(() => GetEntries());
}
public ObservableCollection<IGetEntries_Entries_Nodes> Test { get; set; } = new();
public CurrentUserViewModel CurrentUserViewModel { get; set; }
private async Task GetEntries()
{
try
{
IOperationResult<IGetEntriesResult> entries = await _workshopClient.GetEntries.ExecuteAsync();
if (entries.Data?.Entries?.Nodes == null)
return;
foreach (IGetEntries_Entries_Nodes getEntriesEntriesNodes in entries.Data.Entries.Nodes)
{
Console.WriteLine(getEntriesEntriesNodes);
Test.Add(getEntriesEntriesNodes);
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
public ViewModelBase? TitleBarViewModel => null;
}

View File

@ -8,4 +8,12 @@
<!-- <FluentTheme Mode="Dark"></FluentTheme> -->
<StyleInclude Source="avares://Artemis.UI.Shared/Styles/Artemis.axaml" />
<Styles.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<MergeResourceInclude Source="TreeView.axaml"></MergeResourceInclude>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Styles.Resources>
</Styles>

View File

@ -0,0 +1,122 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls"
xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
x:CompileBindings="True">
<Design.PreviewWith>
<Border Padding="30" MinWidth="350">
<TreeView ItemContainerTheme="{StaticResource MenuTreeViewItem}">
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<avalonia:MaterialIcon Kind="Home" Width="16" Height="16" />
<TextBlock FontSize="12" Margin="10 0" VerticalAlignment="Center" Text="Home" />
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
<TreeViewItem IsExpanded="True">
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<avalonia:MaterialIcon Kind="TestTube" Width="16" Height="16" />
<TextBlock FontSize="12" Margin="10 0" VerticalAlignment="Center" Text="Workshop" />
</StackPanel>
</TreeViewItem.Header>
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<avalonia:MaterialIcon Kind="FolderVideo" Width="16" Height="16" />
<TextBlock FontSize="12" Margin="10 0" VerticalAlignment="Center" Text="Profiles" />
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<avalonia:MaterialIcon Kind="KeyboardVariant" Width="16" Height="16" />
<TextBlock FontSize="12" Margin="10 0" VerticalAlignment="Center" Text="Layouts" />
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
</TreeViewItem>
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<avalonia:MaterialIcon Kind="Devices" Width="16" Height="16" />
<TextBlock FontSize="12" Margin="10 0" VerticalAlignment="Center" Text="Surface Editor" />
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<avalonia:MaterialIcon Kind="Cog" Width="16" Height="16" />
<TextBlock FontSize="12" Margin="10 0" VerticalAlignment="Center" Text="Settings" />
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
</TreeView>
</Border>
</Design.PreviewWith>
<x:Double x:Key="TreeViewItemIndent">31</x:Double>
<x:Double x:Key="TreeViewItemExpandCollapseChevronSize">12</x:Double>
<Thickness x:Key="TreeViewItemExpandCollapseChevronMargin">12, 0, 12, 0</Thickness>
<converters:MarginMultiplierConverter Indent="{StaticResource TreeViewItemIndent}"
Left="True"
x:Key="TreeViewItemLeftMarginConverter" />
<ControlTheme TargetType="TreeViewItem" x:Key="MenuTreeViewItem" BasedOn="{StaticResource {x:Type TreeViewItem}}">
<Setter Property="Template">
<ControlTemplate>
<StackPanel>
<Border Name="PART_LayoutRoot"
Classes="TreeViewItemLayoutRoot"
Focusable="True"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
MinHeight="{DynamicResource NavigationViewItemOnLeftMinHeight}"
CornerRadius="{DynamicResource OverlayCornerRadius}"
TemplatedControl.IsTemplateFocusTarget="True"
Margin="2">
<Panel>
<Grid Name="PART_Header"
ColumnDefinitions="12, *, Auto"
Margin="{TemplateBinding Level, Mode=OneWay, Converter={StaticResource TreeViewItemLeftMarginConverter}}">
<Rectangle Name="SelectionIndicator"
Width="3"
Height="16"
HorizontalAlignment="Left"
VerticalAlignment="Center"
RadiusX="2"
RadiusY="2"
IsVisible="False"
Fill="{DynamicResource TreeViewItemSelectionIndicatorForeground}" />
<ContentPresenter Name="PART_HeaderPresenter"
Grid.Column="1"
Focusable="False"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Margin="{TemplateBinding Padding}" />
<Panel Name="PART_ExpandCollapseChevronContainer" Grid.Column="2">
<ToggleButton Name="PART_ExpandCollapseChevron"
Theme="{StaticResource TreeViewChevronButton}"
Focusable="False"
Margin="{StaticResource TreeViewItemExpandCollapseChevronMargin}"
IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
</Panel>
</Grid>
</Panel>
</Border>
<ItemsPresenter Name="PART_ItemsPresenter"
IsVisible="{TemplateBinding IsExpanded}"
ItemsPanel="{TemplateBinding ItemsPanel}" />
</StackPanel>
</ControlTemplate>
</Setter>
</ControlTheme>
</ResourceDictionary>