diff --git a/src/Artemis.UI.Avalonia/App.axaml b/src/Artemis.UI.Avalonia/App.axaml index 0daed3338..fb754aab7 100644 --- a/src/Artemis.UI.Avalonia/App.axaml +++ b/src/Artemis.UI.Avalonia/App.axaml @@ -2,25 +2,23 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Artemis.UI.Avalonia" xmlns:sty="using:FluentAvalonia.Styling" - xmlns:ui="using:FluentAvalonia.UI.Controls" - xmlns:uip="using:FluentAvalonia.UI.Controls.Primitives" x:Class="Artemis.UI.Avalonia.App"> - + - - - + + + - + - - - + + + - + \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj b/src/Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj index 2a4fb9d50..e3b4cb988 100644 --- a/src/Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj +++ b/src/Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj @@ -1,76 +1,72 @@  - - WinExe - net5.0 - enable - - - - - - - - - - - - - - - - - - - - - - - - - - - - %(Filename) - - - %(Filename) - - - %(Filename) - - - %(Filename) - - - %(Filename) - - - SidebarView.axaml - Code - - - RootView.axaml - Code - - - - - - - - - - - - - ..\..\..\RGB.NET\bin\net5.0\RGB.NET.Core.dll - - - - - - - - - - + + WinExe + net5.0 + enable + + + + + + + + + + + + + + + + + + + + + + + + %(Filename) + + + %(Filename) + + + %(Filename) + + + %(Filename) + + + %(Filename) + + + SidebarView.axaml + Code + + + RootView.axaml + Code + + + + + + + + + + + + + ..\..\..\RGB.NET\bin\net5.0\RGB.NET.Core.dll + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/Converters/ColorToSolidColorBrushConverter.cs b/src/Artemis.UI.Avalonia/Converters/ColorToSolidColorBrushConverter.cs index b29bfe53a..c49188a74 100644 --- a/src/Artemis.UI.Avalonia/Converters/ColorToSolidColorBrushConverter.cs +++ b/src/Artemis.UI.Avalonia/Converters/ColorToSolidColorBrushConverter.cs @@ -14,7 +14,6 @@ namespace Artemis.UI.Avalonia.Converters /// public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - return new SolidColorBrush(!(value is RGBColor color) ? new Color(0, 0, 0, 0) : new Color((byte) color.A, (byte) color.R, (byte) color.G, (byte) color.B)); diff --git a/src/Artemis.UI.Avalonia/Converters/EnumToCollectionConverter.cs b/src/Artemis.UI.Avalonia/Converters/EnumToCollectionConverter.cs index 1d1977444..a3e6609d1 100644 --- a/src/Artemis.UI.Avalonia/Converters/EnumToCollectionConverter.cs +++ b/src/Artemis.UI.Avalonia/Converters/EnumToCollectionConverter.cs @@ -8,19 +8,8 @@ using Avalonia.Markup.Xaml; namespace Artemis.UI.Avalonia.Converters { - public class EnumToCollectionConverter : MarkupExtension, IValueConverter { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return GetAllValuesAndDescriptions(value.GetType()); - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return null; - } - public override object ProvideValue(IServiceProvider serviceProvider) { return this; @@ -44,5 +33,15 @@ namespace Artemis.UI.Avalonia.Converters return Enum.GetValues(t).Cast().Select(e => new Tuple(e, Description(e))).ToList(); } + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return GetAllValuesAndDescriptions(value.GetType()); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return null; + } } } \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/Converters/ValuesAdditionConverter.cs b/src/Artemis.UI.Avalonia/Converters/ValuesAdditionConverter.cs index 1139efa67..55fe27a36 100644 --- a/src/Artemis.UI.Avalonia/Converters/ValuesAdditionConverter.cs +++ b/src/Artemis.UI.Avalonia/Converters/ValuesAdditionConverter.cs @@ -8,7 +8,6 @@ namespace Artemis.UI.Avalonia.Converters { public class ValuesAdditionConverter : IMultiValueConverter { - /// public object Convert(IList values, Type targetType, object parameter, CultureInfo culture) { diff --git a/src/Artemis.UI.Avalonia/MainWindow.axaml b/src/Artemis.UI.Avalonia/MainWindow.axaml index 2b5967a0c..2472b6c84 100644 --- a/src/Artemis.UI.Avalonia/MainWindow.axaml +++ b/src/Artemis.UI.Avalonia/MainWindow.axaml @@ -2,7 +2,6 @@ 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:reactiveUi="http://reactiveui.net" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Artemis.UI.Avalonia.MainWindow" Icon="/Assets/avalonia-logo.ico" diff --git a/src/Artemis.UI.Avalonia/MainWindow.axaml.cs b/src/Artemis.UI.Avalonia/MainWindow.axaml.cs index d57321c31..b6c98ac3f 100644 --- a/src/Artemis.UI.Avalonia/MainWindow.axaml.cs +++ b/src/Artemis.UI.Avalonia/MainWindow.axaml.cs @@ -5,7 +5,7 @@ using Avalonia.ReactiveUI; namespace Artemis.UI.Avalonia { - public partial class MainWindow : ReactiveWindow + public class MainWindow : ReactiveWindow { public MainWindow() { diff --git a/src/Artemis.UI.Avalonia/Ninject/Factories/IVMFactory.cs b/src/Artemis.UI.Avalonia/Ninject/Factories/IVMFactory.cs index f393dfecb..316935f82 100644 --- a/src/Artemis.UI.Avalonia/Ninject/Factories/IVMFactory.cs +++ b/src/Artemis.UI.Avalonia/Ninject/Factories/IVMFactory.cs @@ -1,5 +1,6 @@ using Artemis.Core; using Artemis.UI.Avalonia.Screens.Root.ViewModels; +using ReactiveUI; namespace Artemis.UI.Avalonia.Ninject.Factories { @@ -9,6 +10,7 @@ namespace Artemis.UI.Avalonia.Ninject.Factories public interface ISidebarVmFactory : IVmFactory { + SidebarViewModel SidebarViewModel(IScreen hostScreen); SidebarCategoryViewModel SidebarCategoryViewModel(ProfileCategory profileCategory); SidebarProfileConfigurationViewModel SidebarProfileConfigurationViewModel(ProfileConfiguration profileConfiguration); } diff --git a/src/Artemis.UI.Avalonia/Program.cs b/src/Artemis.UI.Avalonia/Program.cs index 6d2382455..486440ee6 100644 --- a/src/Artemis.UI.Avalonia/Program.cs +++ b/src/Artemis.UI.Avalonia/Program.cs @@ -1,29 +1,29 @@ -using System; -using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; +using Avalonia; using Avalonia.ReactiveUI; using Ninject; -using ReactiveUI; -using Splat; -using Splat.Ninject; namespace Artemis.UI.Avalonia { - class Program + internal class Program { private static StandardKernel _kernel; // Initialization code. Don't use any Avalonia, third-party APIs or any // SynchronizationContext-reliant code before AppMain is called: things aren't initialized // yet and stuff might break. - public static void Main(string[] args) => BuildAvaloniaApp() - .StartWithClassicDesktopLifetime(args); + public static void Main(string[] args) + { + BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + } // Avalonia configuration, don't remove; also used by visual designer. public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() + { + return AppBuilder.Configure() .UsePlatformDetect() .LogToTrace() .UseReactiveUI(); + } } -} +} \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/Screens/Home/ViewModels/HomeViewModel.cs b/src/Artemis.UI.Avalonia/Screens/Home/ViewModels/HomeViewModel.cs index c91071b45..46ee6aa2a 100644 --- a/src/Artemis.UI.Avalonia/Screens/Home/ViewModels/HomeViewModel.cs +++ b/src/Artemis.UI.Avalonia/Screens/Home/ViewModels/HomeViewModel.cs @@ -4,14 +4,9 @@ namespace Artemis.UI.Avalonia.Screens.Home.ViewModels { public class HomeViewModel : MainScreenViewModel { - public HomeViewModel(IScreen hostScreens) : base(hostScreens, "home") + public HomeViewModel(IScreen hostScreen) : base(hostScreen, "home") { DisplayName = "Home"; } - - public void OpenUrl(string url) - { - Core.Utilities.OpenUrl(url); - } } } \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/Screens/Home/Views/HomeView.axaml b/src/Artemis.UI.Avalonia/Screens/Home/Views/HomeView.axaml new file mode 100644 index 000000000..daac27813 --- /dev/null +++ b/src/Artemis.UI.Avalonia/Screens/Home/Views/HomeView.axaml @@ -0,0 +1,8 @@ + + Home :> + \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/Screens/Home/Views/HomeView.axaml.cs b/src/Artemis.UI.Avalonia/Screens/Home/Views/HomeView.axaml.cs new file mode 100644 index 000000000..bb3f58517 --- /dev/null +++ b/src/Artemis.UI.Avalonia/Screens/Home/Views/HomeView.axaml.cs @@ -0,0 +1,19 @@ +using Artemis.UI.Avalonia.Screens.Home.ViewModels; +using Avalonia.Markup.Xaml; +using Avalonia.ReactiveUI; + +namespace Artemis.UI.Avalonia.Screens.Home.Views +{ + public class HomeView : ReactiveUserControl + { + public HomeView() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/Screens/MainScreenViewModel.cs b/src/Artemis.UI.Avalonia/Screens/MainScreenViewModel.cs index 466d7b0c0..a4e5453e4 100644 --- a/src/Artemis.UI.Avalonia/Screens/MainScreenViewModel.cs +++ b/src/Artemis.UI.Avalonia/Screens/MainScreenViewModel.cs @@ -2,7 +2,7 @@ namespace Artemis.UI.Avalonia.Screens { - public abstract class MainScreenViewModel : ViewModelBase, IRoutableViewModel + public abstract class MainScreenViewModel : ActivatableViewModelBase, IRoutableViewModel { protected MainScreenViewModel(IScreen hostScreen, string urlPathSegment) { diff --git a/src/Artemis.UI.Avalonia/Screens/ProfileEditor/ViewModels/ProfileEditorViewModel.cs b/src/Artemis.UI.Avalonia/Screens/ProfileEditor/ViewModels/ProfileEditorViewModel.cs new file mode 100644 index 000000000..0e10cd877 --- /dev/null +++ b/src/Artemis.UI.Avalonia/Screens/ProfileEditor/ViewModels/ProfileEditorViewModel.cs @@ -0,0 +1,6 @@ +namespace Artemis.UI.Avalonia.Screens.ProfileEditor.ViewModels +{ + public class ProfileEditorViewModel : ActivatableViewModelBase + { + } +} \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/Screens/ProfileEditor/Views/ProfileEditorView.axaml b/src/Artemis.UI.Avalonia/Screens/ProfileEditor/Views/ProfileEditorView.axaml new file mode 100644 index 000000000..1db8610af --- /dev/null +++ b/src/Artemis.UI.Avalonia/Screens/ProfileEditor/Views/ProfileEditorView.axaml @@ -0,0 +1,8 @@ + + Welcome to Avalonia! + diff --git a/src/Artemis.UI.Avalonia/Screens/ProfileEditor/Views/ProfileEditorView.axaml.cs b/src/Artemis.UI.Avalonia/Screens/ProfileEditor/Views/ProfileEditorView.axaml.cs new file mode 100644 index 000000000..5dc3141ed --- /dev/null +++ b/src/Artemis.UI.Avalonia/Screens/ProfileEditor/Views/ProfileEditorView.axaml.cs @@ -0,0 +1,19 @@ +using Artemis.UI.Avalonia.Screens.ProfileEditor.ViewModels; +using Avalonia.Markup.Xaml; +using Avalonia.ReactiveUI; + +namespace Artemis.UI.Avalonia.Screens.ProfileEditor.Views +{ + public class ProfileEditorView : ReactiveUserControl + { + public ProfileEditorView() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/RootViewModel.cs b/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/RootViewModel.cs index 376906861..e3110a5d5 100644 --- a/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/RootViewModel.cs +++ b/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/RootViewModel.cs @@ -1,19 +1,17 @@ -using System; -using System.Reactive; -using Artemis.Core.Services; +using Artemis.Core.Services; +using Artemis.UI.Avalonia.Ninject.Factories; using ReactiveUI; namespace Artemis.UI.Avalonia.Screens.Root.ViewModels { - public class RootViewModel : ViewModelBase, IScreen, IActivatableViewModel + public class RootViewModel : ActivatableViewModelBase, IScreen { private readonly ICoreService _coreService; - public RootViewModel(ICoreService coreService, SidebarViewModel sidebarViewModel) + public RootViewModel(ICoreService coreService, ISidebarVmFactory sidebarVmFactory) { Router = new RoutingState(); - SidebarViewModel = sidebarViewModel; - SidebarViewModel.Router = Router; + SidebarViewModel = sidebarVmFactory.SidebarViewModel(this); _coreService = coreService; _coreService.Initialize(); @@ -21,9 +19,6 @@ namespace Artemis.UI.Avalonia.Screens.Root.ViewModels public SidebarViewModel SidebarViewModel { get; } - /// - public ViewModelActivator Activator { get; } = new(); - /// public RoutingState Router { get; } } diff --git a/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarScreenViewModel.cs b/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarScreenViewModel.cs index 2b2769587..0a8b89867 100644 --- a/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarScreenViewModel.cs +++ b/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarScreenViewModel.cs @@ -1,7 +1,7 @@ using System; -using System.Reactive.Linq; using Material.Icons; using Ninject; +using Ninject.Parameters; using ReactiveUI; namespace Artemis.UI.Avalonia.Screens.Root.ViewModels @@ -12,9 +12,11 @@ namespace Artemis.UI.Avalonia.Screens.Root.ViewModels { } - public override MainScreenViewModel CreateInstance(IKernel kernel) + public override Type ScreenType => typeof(T); + + public override MainScreenViewModel CreateInstance(IKernel kernel, IScreen screen) { - return kernel.Get(); + return kernel.Get(new ConstructorArgument("hostScreen", screen)); } } @@ -29,7 +31,8 @@ namespace Artemis.UI.Avalonia.Screens.Root.ViewModels public MaterialIconKind Icon { get; } public string DisplayName { get; } - public abstract MainScreenViewModel CreateInstance(IKernel kernel); + public abstract Type ScreenType { get; } + public abstract MainScreenViewModel CreateInstance(IKernel kernel, IScreen screen); public bool IsActive(IObservable routerCurrentViewModel) { diff --git a/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarViewModel.cs b/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarViewModel.cs index a8b63697a..7456da69a 100644 --- a/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarViewModel.cs +++ b/src/Artemis.UI.Avalonia/Screens/Root/ViewModels/SidebarViewModel.cs @@ -1,5 +1,7 @@ -using System.Collections.ObjectModel; +using System; +using System.Collections.ObjectModel; using System.Linq; +using System.Reactive.Disposables; using Artemis.Core; using Artemis.Core.Services; using Artemis.UI.Avalonia.Ninject.Factories; @@ -14,20 +16,19 @@ using RGB.NET.Core; namespace Artemis.UI.Avalonia.Screens.Root.ViewModels { - public class SidebarViewModel : ViewModelBase + public class SidebarViewModel : ActivatableViewModelBase { - private readonly IKernel _kernel; + private readonly IScreen _hostScreen; private readonly IProfileService _profileService; private readonly IRgbService _rgbService; private readonly ISidebarVmFactory _sidebarVmFactory; private ArtemisDevice? _headerDevice; - private SidebarScreenViewModel _selectedSidebarScreen; - private RoutingState _router; + private SidebarScreenViewModel? _selectedSidebarScreen; - public SidebarViewModel(IKernel kernel, IProfileService profileService, IRgbService rgbService, ISidebarVmFactory sidebarVmFactory) + public SidebarViewModel(IScreen hostScreen, IKernel kernel, IProfileService profileService, IRgbService rgbService, ISidebarVmFactory sidebarVmFactory) { - _kernel = kernel; + _hostScreen = hostScreen; _profileService = profileService; _rgbService = rgbService; _sidebarVmFactory = sidebarVmFactory; @@ -43,6 +44,17 @@ namespace Artemis.UI.Avalonia.Screens.Root.ViewModels UpdateProfileCategories(); UpdateHeaderDevice(); + + this.WhenActivated(disposables => + { + this.WhenAnyObservable(vm => vm._hostScreen.Router.CurrentViewModel) + .WhereNotNull() + .Subscribe(c => SelectedSidebarScreen = SidebarScreens.FirstOrDefault(s => s.ScreenType == c.GetType())) + .DisposeWith(disposables); + this.WhenAnyValue(vm => vm.SelectedSidebarScreen) + .WhereNotNull() + .Subscribe(s => _hostScreen.Router.Navigate.Execute(s.CreateInstance(kernel, _hostScreen))); + }); } public ObservableCollection SidebarScreens { get; } @@ -54,21 +66,10 @@ namespace Artemis.UI.Avalonia.Screens.Root.ViewModels set => this.RaiseAndSetIfChanged(ref _headerDevice, value); } - public SidebarScreenViewModel SelectedSidebarScreen + public SidebarScreenViewModel? SelectedSidebarScreen { get => _selectedSidebarScreen; - set - { - this.RaiseAndSetIfChanged(ref _selectedSidebarScreen, value); - // if (!SelectedSidebarScreen.IsActive(Router.CurrentViewModel)) - // Router.Navigate.Execute(SelectedSidebarScreen.CreateInstance(_kernel)).Subscribe(); - } - } - - public RoutingState Router - { - get => _router; - set => this.RaiseAndSetIfChanged(ref _router, value); + set => this.RaiseAndSetIfChanged(ref _selectedSidebarScreen, value); } public SidebarCategoryViewModel AddProfileCategoryViewModel(ProfileCategory profileCategory) diff --git a/src/Artemis.UI.Avalonia/Screens/Root/Views/RootView.axaml b/src/Artemis.UI.Avalonia/Screens/Root/Views/RootView.axaml index 472555f77..13ae28f59 100644 --- a/src/Artemis.UI.Avalonia/Screens/Root/Views/RootView.axaml +++ b/src/Artemis.UI.Avalonia/Screens/Root/Views/RootView.axaml @@ -23,6 +23,11 @@ - + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/Screens/Root/Views/SidebarCategoryView.axaml b/src/Artemis.UI.Avalonia/Screens/Root/Views/SidebarCategoryView.axaml index bd9fc5c88..d6d3b7633 100644 --- a/src/Artemis.UI.Avalonia/Screens/Root/Views/SidebarCategoryView.axaml +++ b/src/Artemis.UI.Avalonia/Screens/Root/Views/SidebarCategoryView.axaml @@ -8,10 +8,10 @@ x:Class="Artemis.UI.Avalonia.Screens.Root.Views.SidebarCategoryView"> - + \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/Styles/Sidebar.axaml b/src/Artemis.UI.Avalonia/Styles/Sidebar.axaml index bfed7dcec..e4d4ac6b1 100644 --- a/src/Artemis.UI.Avalonia/Styles/Sidebar.axaml +++ b/src/Artemis.UI.Avalonia/Styles/Sidebar.axaml @@ -1,6 +1,5 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> @@ -14,7 +13,7 @@ - + \ No newline at end of file diff --git a/src/Artemis.UI.Avalonia/ViewLocator.cs b/src/Artemis.UI.Avalonia/ViewLocator.cs index 34e508620..b4d3fafd1 100644 --- a/src/Artemis.UI.Avalonia/ViewLocator.cs +++ b/src/Artemis.UI.Avalonia/ViewLocator.cs @@ -15,7 +15,7 @@ namespace Artemis.UI.Avalonia if (type != null) return (Control) Activator.CreateInstance(type)!; - return new TextBlock { Text = "Not Found: " + name }; + return new TextBlock {Text = "Not Found: " + name}; } public bool Match(object data) diff --git a/src/Artemis.UI.Avalonia/ViewModelBase.cs b/src/Artemis.UI.Avalonia/ViewModelBase.cs index 38fbea65d..b7cf2eb1d 100644 --- a/src/Artemis.UI.Avalonia/ViewModelBase.cs +++ b/src/Artemis.UI.Avalonia/ViewModelBase.cs @@ -2,7 +2,7 @@ namespace Artemis.UI.Avalonia { - public class ViewModelBase : ReactiveObject + public abstract class ViewModelBase : ReactiveObject { private string? _displayName; @@ -12,4 +12,9 @@ namespace Artemis.UI.Avalonia set => this.RaiseAndSetIfChanged(ref _displayName, value); } } -} + + public abstract class ActivatableViewModelBase : ViewModelBase, IActivatableViewModel + { + public ViewModelActivator Activator { get; } = new(); + } +} \ No newline at end of file