1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

UI - Updated Fluent Avalonia and made the titlebar work well on Linux

This commit is contained in:
Robert 2022-02-18 15:09:41 +01:00
parent 013bc1c316
commit ea9b87967d
29 changed files with 376 additions and 222 deletions

View File

@ -47,7 +47,6 @@ namespace Artemis.Core
/// </summary>
public List<ScriptConfiguration> ScriptConfigurations { get; }
/// <summary>
/// Gets or sets a boolean indicating whether this profile is freshly imported i.e. no changes have been made to it
/// since import

View File

@ -130,6 +130,16 @@ namespace Artemis.Core
/// </summary>
public event EventHandler<ProfileElementEventArgs>? ChildRemoved;
/// <summary>
/// Occurs when a child was added to the <see cref="Children" /> list of this element or any of it's descendents.
/// </summary>
public event EventHandler<ProfileElementEventArgs>? DescendentAdded;
/// <summary>
/// Occurs when a child was removed from the <see cref="Children" /> list of this element or any of it's descendents.
/// </summary>
public event EventHandler<ProfileElementEventArgs>? DescendentRemoved;
/// <summary>
/// Invokes the <see cref="ChildAdded" /> event
/// </summary>
@ -146,6 +156,24 @@ namespace Artemis.Core
ChildRemoved?.Invoke(this, new ProfileElementEventArgs(child));
}
/// <summary>
/// Invokes the <see cref="DescendentAdded" /> event
/// </summary>
protected virtual void OnDescendentAdded(ProfileElement child)
{
DescendentAdded?.Invoke(this, new ProfileElementEventArgs(child));
Parent?.OnDescendentAdded(child);
}
/// <summary>
/// Invokes the <see cref="DescendentRemoved" /> event
/// </summary>
protected virtual void OnDescendentRemoved(ProfileElement child)
{
DescendentRemoved?.Invoke(this, new ProfileElementEventArgs(child));
Parent?.OnDescendentRemoved(child);
}
/// <summary>
/// Disposes the profile element
/// </summary>
@ -200,6 +228,7 @@ namespace Artemis.Core
}
OnChildAdded(child);
OnDescendentAdded(child);
}
/// <summary>
@ -220,6 +249,7 @@ namespace Artemis.Core
}
OnChildRemoved(child);
OnDescendentRemoved(child);
}
private void StreamlineOrder()

View File

@ -222,12 +222,14 @@
},
"FluentAvaloniaUI": {
"type": "Transitive",
"resolved": "1.1.8",
"contentHash": "pWxi0zvl4+602rffgZgRIS2srUr/bKFCH/duiV72UodmMp291vaWLC3Lzbp3j5TzSuPHYAlcUBIFvEMlnu8WLQ==",
"resolved": "1.2.1",
"contentHash": "IH9eei7CrOUkUdxL2E/HZYKFgNupSVO+ju74CnVqmV7u7iolyz3g1cTHELqVgatEb+IqXw7KyeLr2459nUxYSw==",
"dependencies": {
"Avalonia": "0.10.11",
"Avalonia.Desktop": "0.10.11",
"Avalonia.Diagnostics": "0.10.11"
"Avalonia": "0.10.12",
"Avalonia.Desktop": "0.10.12",
"Avalonia.Diagnostics": "0.10.12",
"MicroCom.CodeGenerator.MSBuild": "0.10.4",
"MicroCom.Runtime": "0.10.4"
}
},
"Flurl": {
@ -335,6 +337,16 @@
"Microsoft.Extensions.DependencyModel": "5.0.0"
}
},
"MicroCom.CodeGenerator.MSBuild": {
"type": "Transitive",
"resolved": "0.10.4",
"contentHash": "aG1kLtkgX6lC8qpxVon4OFSCdWYEbQubIg+2/ychWTIFTrDHWFkhcC4YTn0IfGiVCLwh0Yj7eSc8nk5f3UoMKg=="
},
"MicroCom.Runtime": {
"type": "Transitive",
"resolved": "0.10.4",
"contentHash": "enc2U+/1UnF3rtocxb5ofcg7cJSmJI4adbYPr8DZa5bQzvhqA/VbjlcalxoqjI3CR2RvM5WWpjKT0p3BriFJjw=="
},
"Microsoft.CodeAnalysis.Analyzers": {
"type": "Transitive",
"resolved": "2.9.6",
@ -1753,7 +1765,7 @@
"Avalonia.ReactiveUI": "0.10.12",
"Avalonia.Svg.Skia": "0.10.12",
"DynamicData": "7.4.9",
"FluentAvaloniaUI": "1.1.8",
"FluentAvaloniaUI": "1.2.1",
"Flurl.Http": "3.2.0",
"Live.Avalonia": "1.3.1",
"Material.Icons.Avalonia": "1.0.2",
@ -1776,7 +1788,7 @@
"Avalonia.Xaml.Interactions": "0.10.12",
"Avalonia.Xaml.Interactivity": "0.10.12",
"DynamicData": "7.4.9",
"FluentAvaloniaUI": "1.1.8",
"FluentAvaloniaUI": "1.2.1",
"Material.Icons.Avalonia": "1.0.2",
"RGB.NET.Core": "1.0.0-prerelease7",
"ReactiveUI": "17.1.17",

View File

@ -222,12 +222,14 @@
},
"FluentAvaloniaUI": {
"type": "Transitive",
"resolved": "1.1.8",
"contentHash": "pWxi0zvl4+602rffgZgRIS2srUr/bKFCH/duiV72UodmMp291vaWLC3Lzbp3j5TzSuPHYAlcUBIFvEMlnu8WLQ==",
"resolved": "1.2.1",
"contentHash": "IH9eei7CrOUkUdxL2E/HZYKFgNupSVO+ju74CnVqmV7u7iolyz3g1cTHELqVgatEb+IqXw7KyeLr2459nUxYSw==",
"dependencies": {
"Avalonia": "0.10.11",
"Avalonia.Desktop": "0.10.11",
"Avalonia.Diagnostics": "0.10.11"
"Avalonia": "0.10.12",
"Avalonia.Desktop": "0.10.12",
"Avalonia.Diagnostics": "0.10.12",
"MicroCom.CodeGenerator.MSBuild": "0.10.4",
"MicroCom.Runtime": "0.10.4"
}
},
"Flurl": {
@ -335,6 +337,16 @@
"Microsoft.Extensions.DependencyModel": "5.0.0"
}
},
"MicroCom.CodeGenerator.MSBuild": {
"type": "Transitive",
"resolved": "0.10.4",
"contentHash": "aG1kLtkgX6lC8qpxVon4OFSCdWYEbQubIg+2/ychWTIFTrDHWFkhcC4YTn0IfGiVCLwh0Yj7eSc8nk5f3UoMKg=="
},
"MicroCom.Runtime": {
"type": "Transitive",
"resolved": "0.10.4",
"contentHash": "enc2U+/1UnF3rtocxb5ofcg7cJSmJI4adbYPr8DZa5bQzvhqA/VbjlcalxoqjI3CR2RvM5WWpjKT0p3BriFJjw=="
},
"Microsoft.CodeAnalysis.Analyzers": {
"type": "Transitive",
"resolved": "2.9.6",
@ -1753,7 +1765,7 @@
"Avalonia.ReactiveUI": "0.10.12",
"Avalonia.Svg.Skia": "0.10.12",
"DynamicData": "7.4.9",
"FluentAvaloniaUI": "1.1.8",
"FluentAvaloniaUI": "1.2.1",
"Flurl.Http": "3.2.0",
"Live.Avalonia": "1.3.1",
"Material.Icons.Avalonia": "1.0.2",
@ -1776,7 +1788,7 @@
"Avalonia.Xaml.Interactions": "0.10.12",
"Avalonia.Xaml.Interactivity": "0.10.12",
"DynamicData": "7.4.9",
"FluentAvaloniaUI": "1.1.8",
"FluentAvaloniaUI": "1.2.1",
"Material.Icons.Avalonia": "1.0.2",
"RGB.NET.Core": "1.0.0-prerelease7",
"ReactiveUI": "17.1.17",

View File

@ -24,7 +24,7 @@
<PackageReference Include="Avalonia.Xaml.Interactions" Version="0.10.12" />
<PackageReference Include="Avalonia.Xaml.Interactivity" Version="0.10.12" />
<PackageReference Include="DynamicData" Version="7.4.9" />
<PackageReference Include="FluentAvaloniaUI" Version="1.1.8" />
<PackageReference Include="FluentAvaloniaUI" Version="1.2.1" />
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2" />
<PackageReference Include="ReactiveUI" Version="17.1.17" />
<PackageReference Include="ReactiveUI.Validation" Version="2.2.1" />

View File

@ -22,13 +22,15 @@
<!-- Add Styles Here -->
<Style Selector="Border.router-container">
<Setter Property="Background" Value="{DynamicResource SolidBackgroundFillColorTertiary}" />
<Setter Property="Margin" Value="0 10 0 0"></Setter>
<Setter Property="CornerRadius" Value="8 0 0 0" />
<Setter Property="ClipToBounds" Value="True" />
</Style>
</Style>
<Style Selector="Window:windows Border.router-container">
<Setter Property="Margin" Value="0 31 0 0"></Setter>
<Style Selector="Border#TitleBar">
<Setter Property="Height" Value="40"></Setter>
</Style>
<Style Selector="Window:windows Border#TitleBar">
<Setter Property="Margin" Value="0 0 138 0"></Setter>
</Style>
<Style Selector="Border.card">

View File

@ -78,10 +78,11 @@
<Style Selector="Button.title-bar-button">
<Setter Property="Width" Value="46"></Setter>
<Setter Property="Height" Value="32"></Setter>
<Setter Property="Height" Value="30"></Setter>
<Setter Property="BorderThickness" Value="0"></Setter>
<Setter Property="CornerRadius" Value="0"></Setter>
<Setter Property="Margin" Value="0"></Setter>
<Setter Property="Background" Value="Transparent"></Setter>
</Style>
<Style Selector="Button.title-bar-button:pointerover">
<Setter Property="Background" Value="Red"></Setter>

View File

@ -81,13 +81,15 @@
},
"FluentAvaloniaUI": {
"type": "Direct",
"requested": "[1.1.8, )",
"resolved": "1.1.8",
"contentHash": "pWxi0zvl4+602rffgZgRIS2srUr/bKFCH/duiV72UodmMp291vaWLC3Lzbp3j5TzSuPHYAlcUBIFvEMlnu8WLQ==",
"requested": "[1.2.1, )",
"resolved": "1.2.1",
"contentHash": "IH9eei7CrOUkUdxL2E/HZYKFgNupSVO+ju74CnVqmV7u7iolyz3g1cTHELqVgatEb+IqXw7KyeLr2459nUxYSw==",
"dependencies": {
"Avalonia": "0.10.11",
"Avalonia.Desktop": "0.10.11",
"Avalonia.Diagnostics": "0.10.11"
"Avalonia": "0.10.12",
"Avalonia.Desktop": "0.10.12",
"Avalonia.Diagnostics": "0.10.12",
"MicroCom.CodeGenerator.MSBuild": "0.10.4",
"MicroCom.Runtime": "0.10.4"
}
},
"Material.Icons.Avalonia": {
@ -143,53 +145,53 @@
},
"Avalonia.Controls.DataGrid": {
"type": "Transitive",
"resolved": "0.10.11",
"contentHash": "zvt6QA2uwe18gJ/XdnSMTHG6L/2usvjoaAdPC+Lgg+DmUPNTjqN+Hm1l0AjUtNNId6G+4iIkysiZ2WiHPqGsEA==",
"resolved": "0.10.12",
"contentHash": "i3zM3P8PUY4FNhATZoFWkto3H66FcIrnJNMyOsl1fN0FPS6meysAwCKQwuou/oapyzZEODeAmCVdqB0AgjNHVw==",
"dependencies": {
"Avalonia": "0.10.11",
"Avalonia.Remote.Protocol": "0.10.11",
"Avalonia": "0.10.12",
"Avalonia.Remote.Protocol": "0.10.12",
"JetBrains.Annotations": "10.3.0",
"System.Reactive": "5.0.0"
}
},
"Avalonia.Desktop": {
"type": "Transitive",
"resolved": "0.10.11",
"contentHash": "PQTl4lm7IZidzltMwC7RSNaoz7TYNznU8SKa/WaAI6ycMzC0On2DsqiL1dXr6WhYzMazyMJj6kBhiQzHIc1lIQ==",
"resolved": "0.10.12",
"contentHash": "wy4k1uarrmZJSJENCe1hjNpdCJWhup0gt6KA2TtZILfGG7imj+an5IuQZUSXtA7cl7A+6tF6lPQLo82gESUlXQ==",
"dependencies": {
"Avalonia": "0.10.11",
"Avalonia.Native": "0.10.11",
"Avalonia.Skia": "0.10.11",
"Avalonia.Win32": "0.10.11",
"Avalonia.X11": "0.10.11"
"Avalonia": "0.10.12",
"Avalonia.Native": "0.10.12",
"Avalonia.Skia": "0.10.12",
"Avalonia.Win32": "0.10.12",
"Avalonia.X11": "0.10.12"
}
},
"Avalonia.Diagnostics": {
"type": "Transitive",
"resolved": "0.10.11",
"contentHash": "xBvBkF2DBKjddAfQbExd660zQ5RaDEXH1JgAdMyYOdu3qFL6d+QHyZdVHVeQFilNYE03F6C8AbMWrmj6dBUNlg==",
"resolved": "0.10.12",
"contentHash": "Pf9DGiSwl3+gPrRSHKFzDG20I9QJ5P1g6BexLKfHQH9+Cmax+a/UEVYQq4hGn0xhrmpuLYOeGHb8wasjAT4EfQ==",
"dependencies": {
"Avalonia": "0.10.11",
"Avalonia.Controls.DataGrid": "0.10.11",
"Avalonia": "0.10.12",
"Avalonia.Controls.DataGrid": "0.10.12",
"Microsoft.CodeAnalysis.CSharp.Scripting": "3.4.0",
"System.Reactive": "5.0.0"
}
},
"Avalonia.FreeDesktop": {
"type": "Transitive",
"resolved": "0.10.11",
"contentHash": "cj8T11WQ5/opR2IPttb1Bo89aHclkuvHYsCB7HzZU/F7l/cKXbKUOhyo60p44BdFzrCqjNXDnKQbxeRv+OSF7A==",
"resolved": "0.10.12",
"contentHash": "j42uWCWkAfZchYPrdRccr4mjB0kppSby3TEMCuNrp9GcQi+JhEPEbBAohU7FpR4bkv5FF2KAlDX5WiG2T+04kg==",
"dependencies": {
"Avalonia": "0.10.11",
"Avalonia": "0.10.12",
"Tmds.DBus": "0.9.0"
}
},
"Avalonia.Native": {
"type": "Transitive",
"resolved": "0.10.11",
"contentHash": "9fBC9UArVXEmsxL2Nd0KHGoZUCqcTo06NTlOTAeM3qdEWzE8a0qRVYiR2WeYfADXpKR1D/fQz5zWUZcebFYFIA==",
"resolved": "0.10.12",
"contentHash": "JnZc0zF7DcLcSX+SdnKQGzFa9mcKxawhTN8S3aiN8Eh3MZAKxa45LRrHFVTcHcy2jU4kOw+yPfONUmHpRcC0gw==",
"dependencies": {
"Avalonia": "0.10.11"
"Avalonia": "0.10.12"
}
},
"Avalonia.Remote.Protocol": {
@ -213,10 +215,10 @@
},
"Avalonia.Win32": {
"type": "Transitive",
"resolved": "0.10.11",
"contentHash": "bckqh8rnQ4+l2kdU4njO3cBKaT4l1HQkxdVYJLAgl44uMtoCpaN7EidrBTnuM40DXa0cpvOh97A+G8jpZgte6Q==",
"resolved": "0.10.12",
"contentHash": "CnC65T8ScMK23BB+qJuiMicWQ5QIEiinnRzPqvAGUGyQbjIGpA5uOCKwzsOjUmzkhGqt31iDR0/Y3ZFbi5Mjog==",
"dependencies": {
"Avalonia": "0.10.11",
"Avalonia": "0.10.12",
"Avalonia.Angle.Windows.Natives": "2.1.0.2020091801",
"System.Drawing.Common": "4.5.0",
"System.Numerics.Vectors": "4.5.0"
@ -224,12 +226,12 @@
},
"Avalonia.X11": {
"type": "Transitive",
"resolved": "0.10.11",
"contentHash": "joPVaMmPy4bC1STSk5+fAn5zZOT3gz4m/YSv6io3p2q68kEbc+d5KaYk/KcqA/WGiBBQx4a0ViPW/IRomI94kw==",
"resolved": "0.10.12",
"contentHash": "mUY1cF1p86/UgLl1cbSmY3nVIatKQsSCDOH4avssL07xmKlRfB2G7Gi8jlhWNkLJTLL7iQp/u3X6bv7bs+0zNQ==",
"dependencies": {
"Avalonia": "0.10.11",
"Avalonia.FreeDesktop": "0.10.11",
"Avalonia.Skia": "0.10.11"
"Avalonia": "0.10.12",
"Avalonia.FreeDesktop": "0.10.12",
"Avalonia.Skia": "0.10.12"
}
},
"Castle.Core": {
@ -335,6 +337,16 @@
"Microsoft.Extensions.DependencyModel": "5.0.0"
}
},
"MicroCom.CodeGenerator.MSBuild": {
"type": "Transitive",
"resolved": "0.10.4",
"contentHash": "aG1kLtkgX6lC8qpxVon4OFSCdWYEbQubIg+2/ychWTIFTrDHWFkhcC4YTn0IfGiVCLwh0Yj7eSc8nk5f3UoMKg=="
},
"MicroCom.Runtime": {
"type": "Transitive",
"resolved": "0.10.4",
"contentHash": "enc2U+/1UnF3rtocxb5ofcg7cJSmJI4adbYPr8DZa5bQzvhqA/VbjlcalxoqjI3CR2RvM5WWpjKT0p3BriFJjw=="
},
"Microsoft.CodeAnalysis.Analyzers": {
"type": "Transitive",
"resolved": "2.9.6",

View File

@ -238,12 +238,14 @@
},
"FluentAvaloniaUI": {
"type": "Transitive",
"resolved": "1.1.8",
"contentHash": "pWxi0zvl4+602rffgZgRIS2srUr/bKFCH/duiV72UodmMp291vaWLC3Lzbp3j5TzSuPHYAlcUBIFvEMlnu8WLQ==",
"resolved": "1.2.1",
"contentHash": "IH9eei7CrOUkUdxL2E/HZYKFgNupSVO+ju74CnVqmV7u7iolyz3g1cTHELqVgatEb+IqXw7KyeLr2459nUxYSw==",
"dependencies": {
"Avalonia": "0.10.11",
"Avalonia.Desktop": "0.10.11",
"Avalonia.Diagnostics": "0.10.11"
"Avalonia": "0.10.12",
"Avalonia.Desktop": "0.10.12",
"Avalonia.Diagnostics": "0.10.12",
"MicroCom.CodeGenerator.MSBuild": "0.10.4",
"MicroCom.Runtime": "0.10.4"
}
},
"Flurl": {
@ -351,6 +353,16 @@
"Microsoft.Extensions.DependencyModel": "5.0.0"
}
},
"MicroCom.CodeGenerator.MSBuild": {
"type": "Transitive",
"resolved": "0.10.4",
"contentHash": "aG1kLtkgX6lC8qpxVon4OFSCdWYEbQubIg+2/ychWTIFTrDHWFkhcC4YTn0IfGiVCLwh0Yj7eSc8nk5f3UoMKg=="
},
"MicroCom.Runtime": {
"type": "Transitive",
"resolved": "0.10.4",
"contentHash": "enc2U+/1UnF3rtocxb5ofcg7cJSmJI4adbYPr8DZa5bQzvhqA/VbjlcalxoqjI3CR2RvM5WWpjKT0p3BriFJjw=="
},
"Microsoft.CodeAnalysis.Analyzers": {
"type": "Transitive",
"resolved": "2.9.6",
@ -1769,7 +1781,7 @@
"Avalonia.ReactiveUI": "0.10.12",
"Avalonia.Svg.Skia": "0.10.12",
"DynamicData": "7.4.9",
"FluentAvaloniaUI": "1.1.8",
"FluentAvaloniaUI": "1.2.1",
"Flurl.Http": "3.2.0",
"Live.Avalonia": "1.3.1",
"Material.Icons.Avalonia": "1.0.2",
@ -1792,7 +1804,7 @@
"Avalonia.Xaml.Interactions": "0.10.12",
"Avalonia.Xaml.Interactivity": "0.10.12",
"DynamicData": "7.4.9",
"FluentAvaloniaUI": "1.1.8",
"FluentAvaloniaUI": "1.2.1",
"Material.Icons.Avalonia": "1.0.2",
"RGB.NET.Core": "1.0.0-prerelease7",
"ReactiveUI": "17.1.17",

View File

@ -22,7 +22,7 @@
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.12" />
<PackageReference Include="Avalonia.Svg.Skia" Version="0.10.12" />
<PackageReference Include="DynamicData" Version="7.4.9" />
<PackageReference Include="FluentAvaloniaUI" Version="1.1.8" />
<PackageReference Include="FluentAvaloniaUI" Version="1.2.1" />
<PackageReference Include="Flurl.Http" Version="3.2.0" />
<PackageReference Include="Live.Avalonia" Version="1.3.1" />
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2" />

View File

@ -7,12 +7,11 @@
x:Class="Artemis.UI.MainWindow"
Icon="/Assets/Images/Logo/application.ico"
Title="Artemis 2.0">
<!-- Use a panel here so the main window can host ContentDialogs -->
<Panel>
<DockPanel>
<ContentControl Content="{Binding SidebarViewModel}" DockPanel.Dock="Left"></ContentControl>
<Border Background="Transparent" Name="TitleBar" DockPanel.Dock="Top">
<ContentControl Content="{Binding TitleBarViewModel}" />
</Border>
<ContentControl Content="{Binding}" />
<Border Background="Transparent" x:Name="TitleBar" VerticalAlignment="Top" MinHeight="31" >
<ContentControl Content="{Binding TitleBarViewModel}" Margin="240 0 150 0"/>
</Border>
</Panel>
</DockPanel>
</controls:CoreWindow>

View File

@ -1,3 +1,4 @@
using System;
using Artemis.UI.Screens.Root;
using Avalonia;
using Avalonia.Controls;
@ -13,21 +14,27 @@ namespace Artemis.UI
{
public MainWindow()
{
Opened += OnOpened;
InitializeComponent();
SetupTitlebar();
#if DEBUG
this.AttachDevTools();
#endif
}
private void SetupTitlebar()
private void OnOpened(object? sender, EventArgs e)
{
Opened -= OnOpened;
ICoreApplicationView coreAppTitleBar = this;
if (coreAppTitleBar.TitleBar != null)
{
coreAppTitleBar.TitleBar.ExtendViewIntoTitleBar = true;
SetTitleBar(this.Get<Border>("TitleBar"));
}
}
private void SetupTitlebar()
{
}

View File

@ -5,7 +5,7 @@
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.MenuBar.MenuBarView">
<Menu Grid.Row="0" Grid.Column="0" Margin="0 2" VerticalAlignment="Top">
<Menu VerticalAlignment="Top">
<MenuItem Header="_File">
<MenuItem Header="New">
<MenuItem.Icon>

View File

@ -15,6 +15,7 @@
</Style>
</TreeView.Styles>
<TreeView.KeyBindings>
<KeyBinding Gesture="Escape" Command="{CompiledBinding ClearSelection}" />
<KeyBinding Gesture="F2" Command="{CompiledBinding SelectedChild.Rename}" />
<KeyBinding Gesture="Delete" Command="{CompiledBinding SelectedChild.Delete}" />
<KeyBinding Gesture="Ctrl+D" Command="{CompiledBinding SelectedChild.Duplicate}" />

View File

@ -15,11 +15,13 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
{
public class ProfileTreeViewModel : TreeItemViewModel
{
private readonly IProfileEditorService _profileEditorService;
private TreeItemViewModel? _selectedChild;
public ProfileTreeViewModel(IWindowService windowService, IProfileEditorService profileEditorService, IProfileEditorVmFactory profileEditorVmFactory, IRgbService rgbService)
: base(null, null, windowService, profileEditorService, rgbService, profileEditorVmFactory)
{
_profileEditorService = profileEditorService;
this.WhenActivated(d =>
{
profileEditorService.ProfileConfiguration.WhereNotNull().Subscribe(configuration =>
@ -43,8 +45,12 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
if (model?.ProfileElement is RenderProfileElement renderProfileElement)
profileEditorService.ChangeCurrentProfileElement(renderProfileElement);
});
ClearSelection = ReactiveCommand.Create(() => _profileEditorService.ChangeCurrentProfileElement(null));
}
public ReactiveCommand<Unit, Unit> ClearSelection { get; }
public TreeItemViewModel? SelectedChild
{
get => _selectedChild;

View File

@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools;
using Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.ProfileEditor;
@ -17,9 +19,9 @@ namespace Artemis.UI.Screens.ProfileEditor.VisualEditor;
public class VisualEditorViewModel : ActivatableViewModelBase
{
private readonly SourceList<IVisualizerViewModel> _visualizers;
private readonly IProfileEditorVmFactory _vmFactory;
private ObservableAsPropertyHelper<ProfileConfiguration?>? _profileConfiguration;
private readonly SourceList<IVisualizerViewModel> _visualizers;
private ReadOnlyObservableCollection<IToolViewModel> _tools;
public VisualEditorViewModel(IProfileEditorService profileEditorService, IRgbService rgbService, IProfileEditorVmFactory vmFactory)
@ -36,10 +38,33 @@ public class VisualEditorViewModel : ActivatableViewModelBase
this.WhenActivated(d =>
{
_profileConfiguration = profileEditorService.ProfileConfiguration.ToProperty(this, vm => vm.ProfileConfiguration).DisposeWith(d);
profileEditorService.ProfileConfiguration.Subscribe(CreateVisualizers).DisposeWith(d);
profileEditorService.Tools.Connect().AutoRefreshOnObservable(t => t.WhenAnyValue(vm => vm.IsSelected)).Filter(t => t.IsSelected).Bind(out ReadOnlyObservableCollection<IToolViewModel> tools).Subscribe().DisposeWith(d);
_profileConfiguration = profileEditorService.ProfileConfiguration
.ToProperty(this, vm => vm.ProfileConfiguration)
.DisposeWith(d);
profileEditorService.ProfileConfiguration
.Subscribe(CreateVisualizers)
.DisposeWith(d);
profileEditorService.Tools
.Connect()
.AutoRefreshOnObservable(t => t.WhenAnyValue(vm => vm.IsSelected)).Filter(t => t.IsSelected).Bind(out ReadOnlyObservableCollection<IToolViewModel> tools)
.Subscribe()
.DisposeWith(d);
Tools = tools;
this.WhenAnyValue(vm => vm.ProfileConfiguration)
.Select(p => p?.Profile != null
? Observable.FromEventPattern<ProfileElementEventArgs>(x => p.Profile.DescendentAdded += x, x => p.Profile.DescendentAdded -= x)
: Observable.Never<EventPattern<ProfileElementEventArgs>>())
.Switch()
.Subscribe(AddElement)
.DisposeWith(d);
this.WhenAnyValue(vm => vm.ProfileConfiguration)
.Select(p => p?.Profile != null
? Observable.FromEventPattern<ProfileElementEventArgs>(x => p.Profile.DescendentRemoved += x, x => p.Profile.DescendentRemoved -= x)
: Observable.Never<EventPattern<ProfileElementEventArgs>>())
.Switch()
.Subscribe(RemoveElement)
.DisposeWith(d);
});
}
@ -54,9 +79,21 @@ public class VisualEditorViewModel : ActivatableViewModelBase
set => RaiseAndSetIfChanged(ref _tools, value);
}
private void RemoveElement(EventPattern<ProfileElementEventArgs> eventPattern)
{
List<IVisualizerViewModel> visualizers = Visualizers.Where(v => v.ProfileElement == eventPattern.EventArgs.ProfileElement).ToList();
if (visualizers.Any())
_visualizers.RemoveMany(visualizers);
}
private void AddElement(EventPattern<ProfileElementEventArgs> eventPattern)
{
if (eventPattern.EventArgs.ProfileElement is Layer layer)
_visualizers.Edit(list => CreateVisualizer(list, layer));
}
private void CreateVisualizers(ProfileConfiguration? profileConfiguration)
{
// TODO: Monitor and respond to new layers/folders and deletions
_visualizers.Edit(list =>
{
list.Clear();

View File

@ -1,7 +1,10 @@
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
using Artemis.Core;
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
public interface IVisualizerViewModel
{
ProfileElement? ProfileElement { get; }
double X { get; }
double Y { get; }
int Order { get; }

View File

@ -14,6 +14,7 @@
<Setter.Value>
<Transitions>
<DoubleTransition Property="StrokeThickness" Duration="0:0:0.2" Easing="CubicEaseOut"></DoubleTransition>
<BrushTransition Property="Stroke" Duration="0:0:0.2" Easing="CubicEaseOut"></BrushTransition>
</Transitions>
</Setter.Value>
</Setter>

View File

@ -2,60 +2,79 @@ using System;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.PanAndZoom;
using Avalonia.Controls.Shapes;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
using ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
public partial class LayerShapeVisualizerView : ReactiveUserControl<LayerShapeVisualizerViewModel>
{
public partial class LayerShapeVisualizerView : ReactiveUserControl<LayerShapeVisualizerViewModel>
private ZoomBorder? _zoomBorder;
private readonly Path _layerVisualizerUnbound;
private readonly Path _layerVisualizer;
public LayerShapeVisualizerView()
{
private ZoomBorder? _zoomBorder;
private readonly Path _layerVisualizerUnbound;
private readonly Path _layerVisualizer;
InitializeComponent();
_layerVisualizer = this.Get<Path>("LayerVisualizer");
_layerVisualizerUnbound = this.Get<Path>("LayerVisualizerUnbound");
public LayerShapeVisualizerView()
{
InitializeComponent();
_layerVisualizer = this.Get<Path>("LayerVisualizer");
_layerVisualizerUnbound = this.Get<Path>("LayerVisualizerUnbound");
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
#region Overrides of TemplatedControl
/// <inheritdoc />
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
_zoomBorder = (ZoomBorder?) this.GetLogicalAncestors().FirstOrDefault(l => l is ZoomBorder);
if (_zoomBorder != null)
_zoomBorder.PropertyChanged += ZoomBorderOnPropertyChanged;
base.OnAttachedToLogicalTree(e);
}
/// <inheritdoc />
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
if (_zoomBorder != null)
_zoomBorder.PropertyChanged -= ZoomBorderOnPropertyChanged;
base.OnDetachedFromLogicalTree(e);
}
private void ZoomBorderOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property != ZoomBorder.ZoomXProperty || _zoomBorder == null)
return;
_layerVisualizer.StrokeThickness = Math.Max(1, 4 / _zoomBorder.ZoomX);
_layerVisualizerUnbound.StrokeThickness = _layerVisualizer.StrokeThickness;
}
#endregion
this.WhenActivated(d => ViewModel.WhenAnyValue(vm => vm.Selected).Subscribe(_ => UpdateStrokeThickness()).DisposeWith(d));
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
#region Overrides of TemplatedControl
/// <inheritdoc />
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
_zoomBorder = (ZoomBorder?) this.GetLogicalAncestors().FirstOrDefault(l => l is ZoomBorder);
if (_zoomBorder != null)
_zoomBorder.PropertyChanged += ZoomBorderOnPropertyChanged;
base.OnAttachedToLogicalTree(e);
}
/// <inheritdoc />
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
if (_zoomBorder != null)
_zoomBorder.PropertyChanged -= ZoomBorderOnPropertyChanged;
base.OnDetachedFromLogicalTree(e);
}
private void ZoomBorderOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property != ZoomBorder.ZoomXProperty || _zoomBorder == null)
return;
UpdateStrokeThickness();
}
private void UpdateStrokeThickness()
{
if (_zoomBorder == null)
return;
if (ViewModel != null && ViewModel.Selected)
{
_layerVisualizer.StrokeThickness = Math.Max(1, 4 / _zoomBorder.ZoomX);
_layerVisualizerUnbound.StrokeThickness = Math.Max(1, 4 / _zoomBorder.ZoomX);
}
else
{
_layerVisualizer.StrokeThickness = Math.Max(1, 4 / _zoomBorder.ZoomX) / 2;
_layerVisualizerUnbound.StrokeThickness = Math.Max(1, 4 / _zoomBorder.ZoomX) / 2;
}
}
#endregion
}

View File

@ -49,6 +49,7 @@ public class LayerShapeVisualizerViewModel : ActivatableViewModelBase, IVisualiz
}
public Layer Layer { get; }
public ProfileElement ProfileElement => Layer;
public bool Selected => _selected?.Value ?? false;
public Rect LayerBounds
@ -56,7 +57,7 @@ public class LayerShapeVisualizerViewModel : ActivatableViewModelBase, IVisualiz
get => _layerBounds;
private set => RaiseAndSetIfChanged(ref _layerBounds, value);
}
public double X
{
get => _x;

View File

@ -9,10 +9,19 @@
ClipToBounds="False">
<UserControl.Styles>
<Style Selector="Path.layer-visualizer">
<Setter Property="Stroke" Value="{StaticResource ButtonBorderBrushDisabled}" />
<Setter Property="Stroke" Value="{StaticResource SystemAccentColorDark2}" />
<Setter Property="Opacity" Value="0" />
<Setter Property="Transitions">
<Setter.Value>
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.2" Easing="CubicEaseOut"></DoubleTransition>
</Transitions>
</Setter.Value>
</Setter>
</Style>
<Style Selector="Path.layer-visualizer-selected">
<Setter Property="Stroke" Value="{StaticResource SystemAccentColorDark2}" />
<Setter Property="Opacity" Value="1" />
</Style>
</UserControl.Styles>
<Path Name="LayerVisualizer"

View File

@ -36,6 +36,7 @@ public class LayerVisualizerViewModel : ActivatableViewModelBase, IVisualizerVie
}
public Layer Layer { get; }
public ProfileElement ProfileElement => Layer;
public bool Selected => _selected?.Value ?? false;
public Rect LayerBounds
@ -43,7 +44,7 @@ public class LayerVisualizerViewModel : ActivatableViewModelBase, IVisualizerVie
get => _layerBounds;
private set => RaiseAndSetIfChanged(ref _layerBounds, value);
}
public double X
{
get => _x;

View File

@ -10,8 +10,8 @@
<!-- This border enables dragging the window in between the menu and the buttons-->
<Border Grid.Row="0" Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="Transparent" IsHitTestVisible="False" />
<Button Grid.Column="2" Classes="icon-button icon-button-small" Command="{Binding ShowDebugger}" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0 6 0 0">
<Button Grid.Column="2" Classes="title-bar-button" Command="{Binding ShowDebugger}" HorizontalAlignment="Right" VerticalAlignment="Top">
<avalonia:MaterialIcon Kind="Bug"></avalonia:MaterialIcon>
</Button>
</Grid>

View File

@ -38,9 +38,6 @@
<Style Selector="Window:windows Grid.editor-grid">
<Setter Property="Margin" Value="0 0 4 4"></Setter>
</Style>
<Style Selector="Window:windows Grid.editor-grid">
<Setter Property="Margin" Value="0 41 4 4"></Setter>
</Style>
</UserControl.Styles>
<Grid Classes="editor-grid">
<Grid.ColumnDefinitions>
@ -52,7 +49,6 @@
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentControl Content="{CompiledBinding MenuBarViewModel}"></ContentControl>
<Grid Grid.Row="0" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />

View File

@ -3,85 +3,75 @@ using System.Collections.ObjectModel;
using System.Reactive.Disposables;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Screens.ProfileEditor.MenuBar;
using Artemis.UI.Screens.ProfileEditor.ProfileTree;
using Artemis.UI.Screens.ProfileEditor.Properties;
using Artemis.UI.Screens.ProfileEditor.StatusBar;
using Artemis.UI.Screens.ProfileEditor.VisualEditor;
using Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
using Artemis.UI.Shared.Services.ProfileEditor;
using DynamicData;
using DynamicData.Binding;
using Ninject;
using ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor
namespace Artemis.UI.Screens.ProfileEditor;
public class ProfileEditorViewModel : MainScreenViewModel
{
public class ProfileEditorViewModel : MainScreenViewModel
private readonly ISettingsService _settingsService;
private ObservableAsPropertyHelper<ProfileEditorHistory?>? _history;
private ObservableAsPropertyHelper<ProfileConfiguration?>? _profileConfiguration;
private ReadOnlyObservableCollection<IToolViewModel>? _tools;
/// <inheritdoc />
public ProfileEditorViewModel(IScreen hostScreen,
IProfileEditorService profileEditorService,
ISettingsService settingsService,
VisualEditorViewModel visualEditorViewModel,
ProfileTreeViewModel profileTreeViewModel,
ProfileEditorTitleBarViewModel profileEditorTitleBarViewModel,
PropertiesViewModel propertiesViewModel,
StatusBarViewModel statusBarViewModel)
: base(hostScreen, "profile-editor")
{
private readonly ISettingsService _settingsService;
private ObservableAsPropertyHelper<ProfileConfiguration?>? _profileConfiguration;
private ObservableAsPropertyHelper<ProfileEditorHistory?>? _history;
private ReadOnlyObservableCollection<IToolViewModel> _tools;
_settingsService = settingsService;
VisualEditorViewModel = visualEditorViewModel;
ProfileTreeViewModel = profileTreeViewModel;
PropertiesViewModel = propertiesViewModel;
StatusBarViewModel = statusBarViewModel;
TitleBarViewModel = profileEditorTitleBarViewModel;
/// <inheritdoc />
public ProfileEditorViewModel(IScreen hostScreen,
IProfileEditorService profileEditorService,
ISettingsService settingsService,
VisualEditorViewModel visualEditorViewModel,
ProfileTreeViewModel profileTreeViewModel,
ProfileEditorTitleBarViewModel profileEditorTitleBarViewModel,
MenuBarViewModel menuBarViewModel,
PropertiesViewModel propertiesViewModel,
StatusBarViewModel statusBarViewModel)
: base(hostScreen, "profile-editor")
this.WhenActivated(d =>
{
_settingsService = settingsService;
VisualEditorViewModel = visualEditorViewModel;
ProfileTreeViewModel = profileTreeViewModel;
PropertiesViewModel = propertiesViewModel;
StatusBarViewModel = statusBarViewModel;
_profileConfiguration = profileEditorService.ProfileConfiguration.ToProperty(this, vm => vm.ProfileConfiguration).DisposeWith(d);
_history = profileEditorService.History.ToProperty(this, vm => vm.History).DisposeWith(d);
profileEditorService.Tools.Connect()
.Filter(t => t.ShowInToolbar)
.Sort(SortExpressionComparer<IToolViewModel>.Ascending(vm => vm.Order))
.Bind(out ReadOnlyObservableCollection<IToolViewModel> tools)
.Subscribe()
.DisposeWith(d);
Tools = tools;
});
}
if (OperatingSystem.IsWindows())
TitleBarViewModel = profileEditorTitleBarViewModel;
else
MenuBarViewModel = menuBarViewModel;
public VisualEditorViewModel VisualEditorViewModel { get; }
public ProfileTreeViewModel ProfileTreeViewModel { get; }
public PropertiesViewModel PropertiesViewModel { get; }
public StatusBarViewModel StatusBarViewModel { get; }
this.WhenActivated(d =>
{
_profileConfiguration = profileEditorService.ProfileConfiguration.ToProperty(this, vm => vm.ProfileConfiguration).DisposeWith(d);
_history = profileEditorService.History.ToProperty(this, vm => vm.History).DisposeWith(d);
profileEditorService.Tools.Connect()
.Filter(t => t.ShowInToolbar)
.Sort(SortExpressionComparer<IToolViewModel>.Ascending(vm => vm.Order))
.Bind(out ReadOnlyObservableCollection<IToolViewModel> tools)
.Subscribe()
.DisposeWith(d);
Tools = tools;
});
}
public ReadOnlyObservableCollection<IToolViewModel>? Tools
{
get => _tools;
set => RaiseAndSetIfChanged(ref _tools, value);
}
public VisualEditorViewModel VisualEditorViewModel { get; }
public ProfileTreeViewModel ProfileTreeViewModel { get; }
public MenuBarViewModel? MenuBarViewModel { get; }
public PropertiesViewModel PropertiesViewModel { get; }
public StatusBarViewModel StatusBarViewModel { get; }
public ProfileConfiguration? ProfileConfiguration => _profileConfiguration?.Value;
public ProfileEditorHistory? History => _history?.Value;
public PluginSetting<double> TreeWidth => _settingsService.GetSetting("ProfileEditor.TreeWidth", 350.0);
public PluginSetting<double> ConditionsHeight => _settingsService.GetSetting("ProfileEditor.ConditionsHeight", 300.0);
public PluginSetting<double> PropertiesHeight => _settingsService.GetSetting("ProfileEditor.PropertiesHeight", 300.0);
public ReadOnlyObservableCollection<IToolViewModel> Tools
{
get => _tools;
set => RaiseAndSetIfChanged(ref _tools, value);
}
public ProfileConfiguration? ProfileConfiguration => _profileConfiguration?.Value;
public ProfileEditorHistory? History => _history?.Value;
public PluginSetting<double> TreeWidth => _settingsService.GetSetting("ProfileEditor.TreeWidth", 350.0);
public PluginSetting<double> ConditionsHeight => _settingsService.GetSetting("ProfileEditor.ConditionsHeight", 300.0);
public PluginSetting<double> PropertiesHeight => _settingsService.GetSetting("ProfileEditor.PropertiesHeight", 300.0);
public void OpenUrl(string url)
{
Utilities.OpenUrl(url);
}
public void OpenUrl(string url)
{
Utilities.OpenUrl(url);
}
}

View File

@ -5,7 +5,7 @@
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Root.DefaultTitleBarView">
<Button Classes="title-bar-button" Command="{Binding ShowDebugger}" HorizontalAlignment="Right">
<Button Classes="title-bar-button" Command="{Binding ShowDebugger}" VerticalAlignment="Top" HorizontalAlignment="Right">
<avalonia:MaterialIcon Kind="Bug"></avalonia:MaterialIcon>
</Button>
</UserControl>

View File

@ -5,17 +5,9 @@
xmlns:reactiveUi="http://reactiveui.net"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Root.RootView">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="240" MinWidth="175" MaxWidth="400" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ContentControl x:Name="SidebarContentControl" Grid.Column="0" Content="{Binding SidebarViewModel}" />
<reactiveUi:RoutedViewHost Grid.Column="1" Router="{Binding Router}">
<reactiveUi:RoutedViewHost.PageTransition>
<CrossFade Duration="0.1" />
</reactiveUi:RoutedViewHost.PageTransition>
</reactiveUi:RoutedViewHost>
</Grid>
<reactiveUi:RoutedViewHost Router="{Binding Router}">
<reactiveUi:RoutedViewHost.PageTransition>
<CrossFade Duration="0.1" />
</reactiveUi:RoutedViewHost.PageTransition>
</reactiveUi:RoutedViewHost>
</UserControl>

View File

@ -9,7 +9,7 @@
</ResourceDictionary>
</Styles.Resources>
<!-- Third party styles -->
<styling:FluentAvaloniaTheme RequestedTheme="Dark" />
<styling:FluentAvaloniaTheme />
<!-- <FluentTheme Mode="Dark"></FluentTheme> -->
<StyleInclude Source="avares://Material.Icons.Avalonia/App.xaml" />
<StyleInclude Source="avares://Artemis.UI.Shared/Styles/Artemis.axaml" />

View File

@ -85,13 +85,15 @@
},
"FluentAvaloniaUI": {
"type": "Direct",
"requested": "[1.1.8, )",
"resolved": "1.1.8",
"contentHash": "pWxi0zvl4+602rffgZgRIS2srUr/bKFCH/duiV72UodmMp291vaWLC3Lzbp3j5TzSuPHYAlcUBIFvEMlnu8WLQ==",
"requested": "[1.2.1, )",
"resolved": "1.2.1",
"contentHash": "IH9eei7CrOUkUdxL2E/HZYKFgNupSVO+ju74CnVqmV7u7iolyz3g1cTHELqVgatEb+IqXw7KyeLr2459nUxYSw==",
"dependencies": {
"Avalonia": "0.10.11",
"Avalonia.Desktop": "0.10.11",
"Avalonia.Diagnostics": "0.10.11"
"Avalonia": "0.10.12",
"Avalonia.Desktop": "0.10.12",
"Avalonia.Diagnostics": "0.10.12",
"MicroCom.CodeGenerator.MSBuild": "0.10.4",
"MicroCom.Runtime": "0.10.4"
}
},
"Flurl.Http": {
@ -387,6 +389,16 @@
"Microsoft.Extensions.DependencyModel": "5.0.0"
}
},
"MicroCom.CodeGenerator.MSBuild": {
"type": "Transitive",
"resolved": "0.10.4",
"contentHash": "aG1kLtkgX6lC8qpxVon4OFSCdWYEbQubIg+2/ychWTIFTrDHWFkhcC4YTn0IfGiVCLwh0Yj7eSc8nk5f3UoMKg=="
},
"MicroCom.Runtime": {
"type": "Transitive",
"resolved": "0.10.4",
"contentHash": "enc2U+/1UnF3rtocxb5ofcg7cJSmJI4adbYPr8DZa5bQzvhqA/VbjlcalxoqjI3CR2RvM5WWpjKT0p3BriFJjw=="
},
"Microsoft.CodeAnalysis.Analyzers": {
"type": "Transitive",
"resolved": "2.9.6",
@ -1764,7 +1776,7 @@
"Avalonia.Xaml.Interactions": "0.10.12",
"Avalonia.Xaml.Interactivity": "0.10.12",
"DynamicData": "7.4.9",
"FluentAvaloniaUI": "1.1.8",
"FluentAvaloniaUI": "1.2.1",
"Material.Icons.Avalonia": "1.0.2",
"RGB.NET.Core": "1.0.0-prerelease7",
"ReactiveUI": "17.1.17",