Core - Reworked profile render override for the editor and new previewer

This commit is contained in:
Robert committed 2023-08-10 11:54:37 +02:00
1 parent c6a318b6e3
commit d2b8123a30
42 files changed
+812 -399

No files matched your search

@@ -1,4 +1,3 @@
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Workshop.Profile;
@@ -9,9 +8,4 @@ public partial class ProfileDetailsView : ReactiveUserControl<ProfileDetailsView
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
@@ -1,4 +1,3 @@
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Workshop.Profile;
@@ -9,9 +8,4 @@ public partial class ProfileListView : ReactiveUserControl<ProfileListViewModel>
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
@@ -0,0 +1,58 @@
<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"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Workshop.Profile.ProfilePreviewView"
x:DataType="profile:ProfilePreviewViewModel">
<UserControl.Resources>
<VisualBrush x:Key="LargeCheckerboardBrush" TileMode="Tile" Stretch="Uniform" SourceRect="0,0,20,20">
<VisualBrush.Visual>
<Canvas Width="20" Height="20">
<Rectangle Width="10" Height="10" Fill="Black" Opacity="0.15" />
<Rectangle Width="10" Height="10" Canvas.Left="10" />
<Rectangle Width="10" Height="10" Canvas.Top="10" />
<Rectangle Width="10" Height="10" Canvas.Left="10" Canvas.Top="10" Fill="Black" Opacity="0.15" />
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</UserControl.Resources>
<ZoomBorder Name="ZoomBorder"
Stretch="None"
ClipToBounds="True"
Focusable="True"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Background="{StaticResource LargeCheckerboardBrush}"
ZoomChanged="ZoomBorder_OnZoomChanged">
<Grid Name="ContainerGrid" Background="Transparent">
<Grid.Transitions>
<Transitions>
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.2" Easing="CubicEaseOut" />
</Transitions>
</Grid.Transitions>
<ItemsControl Name="DevicesContainer" ItemsSource="{CompiledBinding Devices}" ClipToBounds="False">
<ItemsControl.Styles>
<Style Selector="ContentPresenter" x:DataType="core:ArtemisDevice">
<Setter Property="Canvas.Left" Value="{CompiledBinding X}" />
<Setter Property="Canvas.Top" Value="{CompiledBinding Y}" />
</Style>
</ItemsControl.Styles>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="core:ArtemisDevice">
<shared:DeviceVisualizer Device="{CompiledBinding}" ShowColors="True" RenderOptions.BitmapInterpolationMode="MediumQuality" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</ZoomBorder>
</UserControl>
@@ -0,0 +1,79 @@
using System;
using System.Linq;
using System.Reactive.Linq;
using Avalonia;
using Avalonia.Controls.PanAndZoom;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.ReactiveUI;
using ReactiveUI;
namespace Artemis.UI.Screens.Workshop.Profile;
public partial class ProfilePreviewView : ReactiveUserControl<ProfilePreviewViewModel>
{
private bool _movedByUser;
public ProfilePreviewView()
{
InitializeComponent();
ZoomBorder.PropertyChanged += ZoomBorderOnPropertyChanged;
ZoomBorder.PointerMoved += ZoomBorderOnPointerMoved;
ZoomBorder.PointerWheelChanged += ZoomBorderOnPointerWheelChanged;
UpdateZoomBorderBackground();
this.WhenAnyValue(v => v.Bounds).Where(_ => !_movedByUser).Subscribe(_ => AutoFit());
}
private void ZoomBorderOnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
{
_movedByUser = true;
}
private void ZoomBorderOnPointerMoved(object? sender, PointerEventArgs e)
{
if (e.GetCurrentPoint(ZoomBorder).Properties.IsMiddleButtonPressed)
_movedByUser = true;
}
private void ZoomBorderOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property.Name == nameof(ZoomBorder.Background))
UpdateZoomBorderBackground();
}
private void UpdateZoomBorderBackground()
{
if (ZoomBorder.Background is VisualBrush visualBrush)
visualBrush.DestinationRect = new RelativeRect(ZoomBorder.OffsetX * -1, ZoomBorder.OffsetY * -1, 20, 20, RelativeUnit.Absolute);
}
private void ZoomBorder_OnZoomChanged(object sender, ZoomChangedEventArgs e)
{
UpdateZoomBorderBackground();
}
private void AutoFit()
{
if (ViewModel == null || !ViewModel.Devices.Any())
return;
double left = ViewModel.Devices.Select(d => d.Rectangle.Left).Min();
double top = ViewModel.Devices.Select(d => d.Rectangle.Top).Min();
double bottom = ViewModel.Devices.Select(d => d.Rectangle.Bottom).Max();
double right = ViewModel.Devices.Select(d => d.Rectangle.Right).Max();
// Add a 10 pixel margin around the rect
Rect scriptRect = new(new Point(left - 10, top - 10), new Point(right + 10, bottom + 10));
// The scale depends on the available space
double scale = Math.Min(3, Math.Min(Bounds.Width / scriptRect.Width, Bounds.Height / scriptRect.Height));
// Pan and zoom to make the script fit
ZoomBorder.Zoom(scale, 0, 0, true);
ZoomBorder.Pan(Bounds.Center.X - scriptRect.Center.X * scale, Bounds.Center.Y - scriptRect.Center.Y * scale, true);
_movedByUser = false;
}
}
@@ -0,0 +1,55 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Disposables;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using ReactiveUI;
namespace Artemis.UI.Screens.Workshop.Profile;
public class ProfilePreviewViewModel : ActivatableViewModelBase
{
private readonly IProfileService _profileService;
private readonly IWindowService _windowService;
private ProfileConfiguration? _profileConfiguration;
public ProfilePreviewViewModel(IProfileService profileService, IRgbService rgbService, IWindowService windowService)
{
_profileService = profileService;
_windowService = windowService;
Devices = new ObservableCollection<ArtemisDevice>(rgbService.EnabledDevices.OrderBy(d => d.ZIndex));
this.WhenAnyValue(vm => vm.ProfileConfiguration).Subscribe(_ => Update());
this.WhenActivated(d => Disposable.Create(() => PreviewProfile(null)).DisposeWith(d));
}
public ObservableCollection<ArtemisDevice> Devices { get; }
public ProfileConfiguration? ProfileConfiguration
{
get => _profileConfiguration;
set => RaiseAndSetIfChanged(ref _profileConfiguration, value);
}
private void Update()
{
try
{
PreviewProfile(ProfileConfiguration);
}
catch (Exception e)
{
_windowService.ShowExceptionDialog("Failed to load preview", e);
}
}
private void PreviewProfile(ProfileConfiguration? profileConfiguration)
{
_profileService.FocusProfile = profileConfiguration;
_profileService.UpdateFocusProfile = profileConfiguration != null;
}
}