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

UI - Further sidebar improvements and fixed titlebar

This commit is contained in:
Robert 2021-10-10 22:37:37 +02:00
parent 1784d2b8b5
commit 5d8a541624
23 changed files with 583 additions and 95 deletions

View File

@ -21,5 +21,8 @@
<Reference Include="Material.Icons.Avalonia">
<HintPath>..\..\..\..\Users\Robert\.nuget\packages\material.icons.avalonia\1.0.2\lib\netstandard2.0\Material.Icons.Avalonia.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Core">
<HintPath>..\..\..\RGB.NET\bin\net5.0\RGB.NET.Core.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@ -1,14 +1,17 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.UI.Avalonia.Shared.Events;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
namespace Artemis.UI.Avalonia.Shared.Controls
@ -18,6 +21,121 @@ namespace Artemis.UI.Avalonia.Shared.Controls
/// </summary>
public class DeviceVisualizer : Control
{
private readonly DispatcherTimer _timer;
private readonly List<DeviceVisualizerLed> _deviceVisualizerLeds;
private Bitmap? _deviceImage;
private List<DeviceVisualizerLed>? _dimmedLeds;
private List<DeviceVisualizerLed>? _highlightedLeds;
private ArtemisDevice? _oldDevice;
/// <inheritdoc />
public DeviceVisualizer()
{
// Run an update timer at 25 fps
_timer = new DispatcherTimer(DispatcherPriority.Render) {Interval = TimeSpan.FromMilliseconds(40)};
_deviceVisualizerLeds = new List<DeviceVisualizerLed>();
PointerReleased += OnPointerReleased;
}
/// <inheritdoc />
public override void Render(DrawingContext drawingContext)
{
if (Device == null)
return;
// Determine the scale required to fit the desired size of the control
Rect measureSize = MeasureDevice();
double scale = Math.Min(Bounds.Width / measureSize.Width, Bounds.Height / measureSize.Height);
// Scale the visualization in the desired bounding box
if (Bounds.Width > 0 && Bounds.Height > 0)
drawingContext.PushPostTransform(Matrix.CreateScale(scale, scale));
// Apply device rotation
drawingContext.PushPostTransform(Matrix.CreateTranslation(0 - measureSize.Left, 0 - measureSize.Top));
drawingContext.PushPostTransform(Matrix.CreateRotation(Device.Rotation));
// Apply device scale
drawingContext.PushPostTransform(Matrix.CreateScale(Device.Scale, Device.Scale));
// Render device and LED images
if (_deviceImage != null)
drawingContext.DrawImage(_deviceImage, new Rect(0, 0, Device.RgbDevice.Size.Width, Device.RgbDevice.Size.Height));
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)
deviceVisualizerLed.RenderImage(drawingContext);
}
/// <summary>
/// Occurs when a LED of the device has been clicked
/// </summary>
public event EventHandler<LedClickedEventArgs>? LedClicked;
/// <summary>
/// Invokes the <see cref="LedClicked" /> event
/// </summary>
/// <param name="e"></param>
protected virtual void OnLedClicked(LedClickedEventArgs e)
{
LedClicked?.Invoke(this, e);
}
private void Update()
{
InvalidateVisual();
}
private void UpdateTransform()
{
InvalidateVisual();
InvalidateMeasure();
}
private Rect MeasureDevice()
{
if (Device == null)
return Rect.Empty;
Rect deviceRect = new(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height);
Geometry geometry = new RectangleGeometry(deviceRect);
geometry.Transform = new RotateTransform(Device.Rotation);
return geometry.Bounds;
}
private void TimerOnTick(object? sender, EventArgs e)
{
if (ShowColors && IsVisible && Opacity > 0)
Update();
}
private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (Device == null)
return;
Point position = e.GetPosition(this);
double x = position.X / Bounds.Width;
double y = position.Y / Bounds.Height;
Point scaledPosition = new(x * Device.Rectangle.Width, y * Device.Rectangle.Height);
DeviceVisualizerLed? deviceVisualizerLed = _deviceVisualizerLeds.FirstOrDefault(l => l.HitTest(scaledPosition));
if (deviceVisualizerLed != null)
OnLedClicked(new LedClickedEventArgs(deviceVisualizerLed.Led.Device, deviceVisualizerLed.Led));
}
private void DevicePropertyChanged(object? sender, PropertyChangedEventArgs e)
{
Dispatcher.UIThread.Post(SetupForDevice);
}
private void DeviceUpdated(object? sender, EventArgs e)
{
Dispatcher.UIThread.Post(SetupForDevice);
}
#region Properties
/// <summary>
@ -67,26 +185,6 @@ namespace Artemis.UI.Avalonia.Shared.Controls
#endregion
private readonly DispatcherTimer _timer;
/// <inheritdoc />
public DeviceVisualizer()
{
// Run an update timer at 25 fps
_timer = new DispatcherTimer(DispatcherPriority.Render) {Interval = TimeSpan.FromMilliseconds(40)};
}
/// <inheritdoc />
public override void Render(DrawingContext context)
{
base.Render(context);
}
private void Update()
{
throw new NotImplementedException();
}
#region Lifetime management
/// <inheritdoc />
@ -105,14 +203,37 @@ namespace Artemis.UI.Avalonia.Shared.Controls
base.OnDetachedFromLogicalTree(e);
}
#endregion
#region Event handlers
private void TimerOnTick(object? sender, EventArgs e)
private void SetupForDevice()
{
if (ShowColors && IsVisible && Opacity > 0)
Update();
_deviceImage = null;
_deviceVisualizerLeds.Clear();
_highlightedLeds = new List<DeviceVisualizerLed>();
_dimmedLeds = new List<DeviceVisualizerLed>();
if (Device == null)
return;
if (_oldDevice != null)
{
Device.RgbDevice.PropertyChanged -= DevicePropertyChanged;
Device.DeviceUpdated -= DeviceUpdated;
}
_oldDevice = Device;
Device.RgbDevice.PropertyChanged += DevicePropertyChanged;
Device.DeviceUpdated += DeviceUpdated;
UpdateTransform();
// Load the device main image
if (Device.Layout?.Image != null && File.Exists(Device.Layout.Image.LocalPath))
_deviceImage = new Bitmap(Device.Layout.Image.AbsolutePath);
// Create all the LEDs
foreach (ArtemisLed artemisLed in Device.Leds)
_deviceVisualizerLeds.Add(new DeviceVisualizerLed(artemisLed));
InvalidateMeasure();
}
#endregion

View File

@ -0,0 +1,159 @@
using System;
using System.IO;
using Artemis.Core;
using Avalonia;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using RGB.NET.Core;
using Color = Avalonia.Media.Color;
using Point = Avalonia.Point;
using SolidColorBrush = Avalonia.Media.SolidColorBrush;
namespace Artemis.UI.Avalonia.Shared.Controls
{
internal class DeviceVisualizerLed
{
private const byte Dimmed = 100;
private const byte NonDimmed = 255;
public DeviceVisualizerLed(ArtemisLed led)
{
Led = led;
LedRect = new Rect(
Led.RgbLed.Location.X,
Led.RgbLed.Location.Y,
Led.RgbLed.Size.Width,
Led.RgbLed.Size.Height
);
if (Led.Layout?.Image != null && File.Exists(Led.Layout.Image.LocalPath))
LedImage = new Bitmap(Led.Layout.Image.AbsolutePath);
CreateLedGeometry();
}
public ArtemisLed Led { get; }
public Rect LedRect { get; set; }
public Bitmap? LedImage { get; set; }
public Geometry? DisplayGeometry { get; private set; }
public void RenderImage(DrawingContext drawingContext)
{
if (LedImage == null)
return;
drawingContext.DrawImage(LedImage, LedRect);
byte r = Led.RgbLed.Color.GetR();
byte g = Led.RgbLed.Color.GetG();
byte b = Led.RgbLed.Color.GetB();
SolidColorBrush fillBrush = new(new Color(100, r, g, b));
SolidColorBrush penBrush = new(new Color(255, r, g, b));
// Create transparent pixels covering the entire LedRect so the image size matched the LedRect size
// drawingContext.DrawRectangle(new SolidColorBrush(Colors.Transparent), new Pen(new SolidColorBrush(Colors.Transparent), 1), LedRect);
// Translate to the top-left of the LedRect
using DrawingContext.PushedState push = drawingContext.PushPostTransform(Matrix.CreateTranslation(LedRect.X, LedRect.Y));
// Render the LED geometry
drawingContext.DrawGeometry(fillBrush, new Pen(penBrush) {LineJoin = PenLineJoin.Round}, DisplayGeometry);
}
public bool HitTest(Point position)
{
if (DisplayGeometry == null)
return false;
Geometry translatedGeometry = DisplayGeometry.Clone();
translatedGeometry.Transform = new TranslateTransform(Led.RgbLed.Location.X, Led.RgbLed.Location.Y);
return translatedGeometry.FillContains(position);
}
private void CreateLedGeometry()
{
// The minimum required size for geometry to be created
if (Led.RgbLed.Size.Width < 2 || Led.RgbLed.Size.Height < 2)
return;
switch (Led.RgbLed.Shape)
{
case Shape.Custom:
if (Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)
CreateCustomGeometry(2.0);
else
CreateCustomGeometry(1.0);
break;
case Shape.Rectangle:
if (Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)
CreateKeyCapGeometry();
else
CreateRectangleGeometry();
break;
case Shape.Circle:
CreateCircleGeometry();
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private void CreateRectangleGeometry()
{
DisplayGeometry = new RectangleGeometry(new Rect(0.5, 0.5, Led.RgbLed.Size.Width - 1, Led.RgbLed.Size.Height - 1));
}
private void CreateCircleGeometry()
{
DisplayGeometry = new EllipseGeometry(new Rect(0.5, 0.5, Led.RgbLed.Size.Width - 1, Led.RgbLed.Size.Height - 1));
}
private void CreateKeyCapGeometry()
{
PathGeometry path = PathGeometry.Parse($"M1,1" +
$"h{Led.RgbLed.Size.Width - 2} a10," +
$"10 0 0 1 10," +
$"10 v{Led.RgbLed.Size.Height - 2} a10," +
$"10 0 0 1 -10," +
$"10 h-{Led.RgbLed.Size.Width - 2} a10," +
$"10 0 0 1 -10," +
$"-10 v-{Led.RgbLed.Size.Height - 2} a10," +
$"10 0 0 1 10,-10 z");
DisplayGeometry = path;
}
private void CreateCustomGeometry(double deflateAmount)
{
try
{
double width = Led.RgbLed.Size.Width - deflateAmount;
double height = Led.RgbLed.Size.Height - deflateAmount;
Geometry geometry = Geometry.Parse(Led.RgbLed.ShapeData);
geometry.Transform = new ScaleTransform(width, height);
geometry = geometry.Clone();
geometry.Transform = new TranslateTransform(deflateAmount / 2, deflateAmount / 2);
DisplayGeometry = geometry.Clone();
// TODO: Figure out wtf was going on here
// if (DisplayGeometry.Bounds.Width > width)
// {
// DisplayGeometry = Geometry.Combine(Geometry.Empty, DisplayGeometry, GeometryCombineMode.Union, new TransformGroup
// {
// Children = new TransformCollection {new ScaleTransform(width / DisplayGeometry.Bounds.Width, 1)}
// });
// }
//
// if (DisplayGeometry.Bounds.Height > height)
// {
// DisplayGeometry = Geometry.Combine(Geometry.Empty, DisplayGeometry, GeometryCombineMode.Union, new TransformGroup
// {
// Children = new TransformCollection {new ScaleTransform(1, height / DisplayGeometry.Bounds.Height)}
// });
// }
}
catch (Exception)
{
CreateRectangleGeometry();
}
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using Artemis.Core;
namespace Artemis.UI.Avalonia.Shared.Events
{
/// <summary>
/// Provides data about selection events raised by <see cref="DataModelDynamicViewModel" />
/// </summary>
public class DataModelInputDynamicEventArgs : EventArgs
{
internal DataModelInputDynamicEventArgs(DataModelPath? dataModelPath)
{
DataModelPath = dataModelPath;
}
/// <summary>
/// Gets the data model path that was selected
/// </summary>
public DataModelPath? DataModelPath { get; }
}
}

View File

@ -0,0 +1,20 @@
using System;
namespace Artemis.UI.Avalonia.Shared.Events
{
/// <summary>
/// Provides data about submit events raised by <see cref="DataModelStaticViewModel" />
/// </summary>
public class DataModelInputStaticEventArgs : EventArgs
{
internal DataModelInputStaticEventArgs(object? value)
{
Value = value;
}
/// <summary>
/// The value that was submitted
/// </summary>
public object? Value { get; }
}
}

View File

@ -0,0 +1,27 @@
using System;
using Artemis.Core;
namespace Artemis.UI.Avalonia.Shared.Events
{
/// <summary>
/// Provides data on LED click events raised by the device visualizer
/// </summary>
public class LedClickedEventArgs : EventArgs
{
internal LedClickedEventArgs(ArtemisDevice device, ArtemisLed led)
{
Device = device;
Led = led;
}
/// <summary>
/// The device that was clicked
/// </summary>
public ArtemisDevice Device { get; set; }
/// <summary>
/// The LED that was clicked
/// </summary>
public ArtemisLed Led { get; set; }
}
}

View File

@ -0,0 +1,32 @@
using System;
using Artemis.Core;
namespace Artemis.UI.Avalonia.Shared.Events
{
/// <summary>
/// Provides data on profile related events raised by the profile editor
/// </summary>
public class ProfileConfigurationEventArgs : EventArgs
{
internal ProfileConfigurationEventArgs(ProfileConfiguration? profileConfiguration)
{
ProfileConfiguration = profileConfiguration;
}
internal ProfileConfigurationEventArgs(ProfileConfiguration? profileConfiguration, ProfileConfiguration? previousProfileConfiguration)
{
ProfileConfiguration = profileConfiguration;
PreviousProfileConfiguration = previousProfileConfiguration;
}
/// <summary>
/// Gets the profile the event was raised for
/// </summary>
public ProfileConfiguration? ProfileConfiguration { get; }
/// <summary>
/// If applicable, the previous active profile before the event was raised
/// </summary>
public ProfileConfiguration? PreviousProfileConfiguration { get; }
}
}

View File

@ -0,0 +1,32 @@
using System;
using Artemis.Core;
namespace Artemis.UI.Avalonia.Shared.Events
{
/// <summary>
/// Provides data on profile element related events raised by the profile editor
/// </summary>
public class RenderProfileElementEventArgs : EventArgs
{
internal RenderProfileElementEventArgs(RenderProfileElement? renderProfileElement)
{
RenderProfileElement = renderProfileElement;
}
internal RenderProfileElementEventArgs(RenderProfileElement? renderProfileElement, RenderProfileElement? previousRenderProfileElement)
{
RenderProfileElement = renderProfileElement;
PreviousRenderProfileElement = previousRenderProfileElement;
}
/// <summary>
/// Gets the profile element the event was raised for
/// </summary>
public RenderProfileElement? RenderProfileElement { get; }
/// <summary>
/// If applicable, the previous active profile element before the event was raised
/// </summary>
public RenderProfileElement? PreviousRenderProfileElement { get; }
}
}

View File

@ -11,8 +11,13 @@
<Application.Styles>
<!-- Third party styles -->
<sty:FluentAvaloniaTheme CustomAccentColor="#4db6ac"/>
<StyleInclude Source="avares://Material.Icons.Avalonia/App.xaml"></StyleInclude>
<sty:FluentAvaloniaTheme RequestedTheme="Dark" CustomAccentColor="#4db6ac"/>
<StyleInclude Source="avares://Material.Icons.Avalonia/App.xaml"/>
<!-- Grab the window styling from Avalonia -->
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/Window.xaml" />
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TitleBar.xaml" />
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml" />
<!-- Global styles -->
<StyleInclude Source="/Styles/Button.axaml"></StyleInclude>

View File

@ -1,10 +1,10 @@
using Artemis.Core.Ninject;
using Artemis.UI.Avalonia.Ninject;
using Artemis.UI.Avalonia.Screens.Main.Views;
using Artemis.UI.Avalonia.Screens.Root.ViewModels;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using FluentAvalonia.Styling;
using Ninject;
using Splat.Ninject;
@ -23,10 +23,13 @@ namespace Artemis.UI.Avalonia
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow
{
DataContext = _kernel.Get<RootViewModel>()
};
AvaloniaLocator.Current.GetService<FluentAvaloniaTheme>().ForceNativeTitleBarToTheme(desktop.MainWindow, "Dark");
}
base.OnFrameworkInitializationCompleted();
}

View File

@ -4,11 +4,11 @@
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.Screens.Main.Views.MainWindow"
x:Class="Artemis.UI.Avalonia.MainWindow"
Icon="/Assets/avalonia-logo.ico"
Title="Artemis.UI.Avalonia"
ExtendClientAreaToDecorationsHint="True"
TransparencyLevelHint="AcrylicBlur"
Background="Transparent"
ExtendClientAreaToDecorationsHint="True"
Content="{Binding}">
</Window>

View File

@ -3,7 +3,7 @@ using Avalonia;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Main.Views
namespace Artemis.UI.Avalonia
{
public partial class MainWindow : ReactiveWindow<RootViewModel>
{

View File

@ -3,6 +3,7 @@ using Artemis.UI.Avalonia.Ninject.Factories;
using Artemis.UI.Avalonia.Screens;
using Ninject.Extensions.Conventions;
using Ninject.Modules;
using Ninject.Planning.Bindings.Resolvers;
namespace Artemis.UI.Avalonia.Ninject
{
@ -13,6 +14,7 @@ namespace Artemis.UI.Avalonia.Ninject
if (Kernel == null)
throw new ArgumentNullException("Kernel shouldn't be null here.");
Kernel.Components.Add<IMissingBindingResolver, SelfBindingResolver>();
Kernel.Bind(x =>
{

View File

@ -1,8 +1,10 @@
namespace Artemis.UI.Avalonia.Screens.Home.ViewModels
using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Home.ViewModels
{
public class HomeViewModel : MainScreenViewModel
{
public HomeViewModel()
public HomeViewModel(IScreen hostScreens) : base(hostScreens, "home")
{
DisplayName = "Home";
}

View File

@ -1,7 +1,19 @@
namespace Artemis.UI.Avalonia.Screens
using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens
{
public class MainScreenViewModel : ViewModelBase
public abstract class MainScreenViewModel : ViewModelBase, IRoutableViewModel
{
protected MainScreenViewModel(IScreen hostScreen, string urlPathSegment)
{
HostScreen = hostScreen;
UrlPathSegment = urlPathSegment;
}
/// <inheritdoc />
public string UrlPathSegment { get; }
/// <inheritdoc />
public IScreen HostScreen { get; }
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Reactive;
using Artemis.Core.Services;
using ReactiveUI;
@ -10,18 +11,20 @@ namespace Artemis.UI.Avalonia.Screens.Root.ViewModels
public RootViewModel(ICoreService coreService, SidebarViewModel sidebarViewModel)
{
Router = new RoutingState();
SidebarViewModel = sidebarViewModel;
SidebarViewModel.Router = Router;
_coreService = coreService;
_coreService.Initialize();
Console.WriteLine("test");
}
public SidebarViewModel SidebarViewModel { get; }
/// <inheritdoc />
public ViewModelActivator Activator { get; } = new();
/// <inheritdoc />
public RoutingState Router { get; } = new();
public RoutingState Router { get; }
}
}

View File

@ -1,5 +1,8 @@
using Material.Icons;
using System;
using System.Reactive.Linq;
using Material.Icons;
using Ninject;
using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Root.ViewModels
{
@ -27,5 +30,10 @@ namespace Artemis.UI.Avalonia.Screens.Root.ViewModels
public string DisplayName { get; }
public abstract MainScreenViewModel CreateInstance(IKernel kernel);
public bool IsActive(IObservable<IRoutableViewModel?> routerCurrentViewModel)
{
return false;
}
}
}

View File

@ -10,6 +10,7 @@ using Artemis.UI.Avalonia.Screens.Workshop.ViewModels;
using Material.Icons;
using Ninject;
using ReactiveUI;
using RGB.NET.Core;
namespace Artemis.UI.Avalonia.Screens.Root.ViewModels
{
@ -17,13 +18,18 @@ namespace Artemis.UI.Avalonia.Screens.Root.ViewModels
{
private readonly IKernel _kernel;
private readonly IProfileService _profileService;
private readonly IRgbService _rgbService;
private readonly ISidebarVmFactory _sidebarVmFactory;
private SidebarScreenViewModel _selectedSidebarScreen;
private ArtemisDevice? _headerDevice;
public SidebarViewModel(IKernel kernel, IProfileService profileService, ISidebarVmFactory sidebarVmFactory)
private SidebarScreenViewModel _selectedSidebarScreen;
private RoutingState _router;
public SidebarViewModel(IKernel kernel, IProfileService profileService, IRgbService rgbService, ISidebarVmFactory sidebarVmFactory)
{
_kernel = kernel;
_profileService = profileService;
_rgbService = rgbService;
_sidebarVmFactory = sidebarVmFactory;
SidebarScreens = new ObservableCollection<SidebarScreenViewModel>
@ -33,17 +39,43 @@ namespace Artemis.UI.Avalonia.Screens.Root.ViewModels
new SidebarScreenViewModel<SurfaceEditorViewModel>(MaterialIconKind.Devices, "Surface Editor"),
new SidebarScreenViewModel<SettingsViewModel>(MaterialIconKind.Cog, "Settings")
};
SelectedSidebarScreen = SidebarScreens.First();
_selectedSidebarScreen = SidebarScreens.First();
UpdateProfileCategories();
UpdateHeaderDevice();
}
public ObservableCollection<SidebarScreenViewModel> SidebarScreens { get; }
public ObservableCollection<SidebarCategoryViewModel> SidebarCategories { get; } = new();
public ArtemisDevice? HeaderDevice
{
get => _headerDevice;
set => this.RaiseAndSetIfChanged(ref _headerDevice, value);
}
public SidebarScreenViewModel SelectedSidebarScreen
{
get => _selectedSidebarScreen;
set => this.RaiseAndSetIfChanged(ref _selectedSidebarScreen, value);
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);
}
public SidebarCategoryViewModel AddProfileCategoryViewModel(ProfileCategory profileCategory)
{
SidebarCategoryViewModel viewModel = _sidebarVmFactory.SidebarCategoryViewModel(profileCategory);
SidebarCategories.Add(viewModel);
return viewModel;
}
private void UpdateProfileCategories()
@ -53,11 +85,9 @@ namespace Artemis.UI.Avalonia.Screens.Root.ViewModels
AddProfileCategoryViewModel(profileCategory);
}
public SidebarCategoryViewModel AddProfileCategoryViewModel(ProfileCategory profileCategory)
private void UpdateHeaderDevice()
{
SidebarCategoryViewModel viewModel = _sidebarVmFactory.SidebarCategoryViewModel(profileCategory);
SidebarCategories.Add(viewModel);
return viewModel;
HeaderDevice = _rgbService.Devices.FirstOrDefault(d => d.DeviceType == RGBDeviceType.Keyboard && d.Layout is {IsValid: true});
}
}
}

View File

@ -1,10 +1,10 @@
<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:reactiveUi="http://reactiveui.net"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Avalonia.Screens.Root.Views.RootView">
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.Screens.Root.Views.RootView">
<Grid>
<Grid.ColumnDefinitions>
@ -19,11 +19,10 @@
TintOpacity="1"
MaterialOpacity="0.65" />
</ExperimentalAcrylicBorder.Material>
<ContentControl Content="{Binding SidebarViewModel}"/>
<ContentControl Content="{Binding SidebarViewModel}" />
</ExperimentalAcrylicBorder>
<Border Grid.Column="1" Background="Black">
<reactiveUi:RoutedViewHost Router="{Binding Router}" Margin="0 20 0 0" Padding="15" />
</Border>
<Border Grid.Column="1" Background="#101010" IsHitTestVisible="False" />
<reactiveUi:RoutedViewHost Grid.Column="1" Router="{Binding Router}" Margin="0 20 0 0" Padding="15" />
</Grid>
</UserControl>

View File

@ -4,7 +4,9 @@
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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:svg="clr-namespace:Avalonia.Svg.Skia;assembly=Avalonia.Svg.Skia"
xmlns:shared="clr-namespace:Artemis.UI.Avalonia.Shared.Controls;assembly=Artemis.UI.Avalonia.Shared"
mc:Ignorable="d" d:DesignWidth="240" d:DesignHeight="450"
x:Class="Artemis.UI.Avalonia.Screens.Root.Views.SidebarView">
<Grid>
<Grid.RowDefinitions>
@ -16,40 +18,41 @@
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" ClipToBounds="True">
<Grid Grid.Row="0" IsHitTestVisible="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- <shared:DeviceVisualizer Grid.Column="0" -->
<!-- Grid.ColumnSpan="2" -->
<!-- Device="{Binding HeaderDevice}" -->
<!-- ShowColors="True" -->
<!-- RenderTransformOrigin="0.5 0.5" -->
<!-- VerticalAlignment="Center" -->
<!-- HorizontalAlignment="Center" -->
<!-- RenderOptions.BitmapScalingMode="HighQuality"> -->
<!-- <shared:DeviceVisualizer.RenderTransform> -->
<!-- <TransformGroup> -->
<!-- <RotateTransform Angle="20" /> -->
<!-- <ScaleTransform ScaleX="2" ScaleY="2" /> -->
<!-- </TransformGroup> -->
<!-- </shared:DeviceVisualizer.RenderTransform> -->
<!-- </shared:DeviceVisualizer> -->
<Rectangle Grid.Column="0" Grid.ColumnSpan="2" Height="60" VerticalAlignment="Bottom">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,1" EndPoint="0,0">
<GradientStop Color="Black" Offset="0" />
<GradientStop Color="#00000000" Offset="1" />
<Border Grid.Column="0"
Grid.ColumnSpan="2">
<shared:DeviceVisualizer Device="{Binding HeaderDevice}"
ShowColors="True"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<shared:DeviceVisualizer.RenderTransform>
<TransformGroup>
<RotateTransform Angle="20" />
<ScaleTransform ScaleX="2" ScaleY="2" />
</TransformGroup>
</shared:DeviceVisualizer.RenderTransform>
</shared:DeviceVisualizer>
<Border.OpacityMask>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,60">
<GradientStop Color="White" Offset="0"></GradientStop>
<GradientStop Color="Transparent" Offset="1"></GradientStop>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Border.OpacityMask>
</Border>
<Image Grid.Column="0" Source="{SvgImage /Assets/Images/Logo/bow.svg}" Height="35" Width="35" Margin="10" />
<Image Grid.Column="0" >
<Image.Source>
<svg:SvgImage Source="/Assets/Images/Logo/bow.svg" />
</Image.Source>
</Image>
<TextBlock Grid.Column="1"
FontSize="24"
VerticalAlignment="Center"
Foreground="{DynamicResource MaterialDesignDarkForeground}">
Artemis 2
</TextBlock>

View File

@ -1,8 +1,10 @@
namespace Artemis.UI.Avalonia.Screens.Settings.ViewModels
using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Settings.ViewModels
{
public class SettingsViewModel : MainScreenViewModel
{
public SettingsViewModel()
public SettingsViewModel(IScreen hostScreens) : base(hostScreens, "settings")
{
DisplayName = "Settings";
}

View File

@ -1,8 +1,10 @@
namespace Artemis.UI.Avalonia.Screens.SurfaceEditor.ViewModels
using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.SurfaceEditor.ViewModels
{
public class SurfaceEditorViewModel : MainScreenViewModel
{
public SurfaceEditorViewModel()
public SurfaceEditorViewModel(IScreen hostScreens) : base(hostScreens, "surface-editor")
{
DisplayName = "Surface Editor";
}

View File

@ -1,8 +1,10 @@
namespace Artemis.UI.Avalonia.Screens.Workshop.ViewModels
using ReactiveUI;
namespace Artemis.UI.Avalonia.Screens.Workshop.ViewModels
{
public class WorkshopViewModel : MainScreenViewModel
{
public WorkshopViewModel()
public WorkshopViewModel(IScreen hostScreens) : base(hostScreens, "workshop")
{
DisplayName = "Workshop";
}