mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 19:36:28 +00:00
Profiles - Removed the technical concept of extra timelines for events
Profiles - Properly separated out update and render logic Profile editor - Implemented layer visualization
This commit is contained in:
1 parent
ff4ec16690
commit
89beb92935
18 files changed
+431
-292
No files matched your search
@@ -79,10 +79,8 @@ internal class ProfileEditorService : IProfileEditorService
|
||||
else
|
||||
{
|
||||
renderElement.Enable();
|
||||
renderElement.Timeline.Override(
|
||||
time,
|
||||
(renderElement != _profileElementSubject.Value || renderElement.Timeline.Length < time) && renderElement.Timeline.PlayMode == TimelinePlayMode.Repeat
|
||||
);
|
||||
bool stickToMainSegment = (renderElement != _profileElementSubject.Value || renderElement.Timeline.Length < time) && renderElement.Timeline.PlayMode == TimelinePlayMode.Repeat;
|
||||
renderElement.OverrideTimelineAndApply(time, stickToMainSegment);
|
||||
|
||||
foreach (ProfileElement child in renderElement.Children)
|
||||
TickProfileElement(child, time);
|
||||
|
||||
@@ -56,6 +56,9 @@
|
||||
<Compile Update="Screens\ProfileEditor\Panels\Properties\PropertiesView.axaml.cs">
|
||||
<DependentUpon>PropertiesView.axaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Screens\ProfileEditor\Panels\VisualEditor\Visualizers\LayerShapeVisualizerView.axaml.cs">
|
||||
<DependentUpon>LayerShapeVisualizerView.axaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="DefaultTypes\DataModel\Display\" />
|
||||
|
||||
@@ -65,6 +65,7 @@ namespace Artemis.UI.Ninject.Factories
|
||||
ProfileEditorViewModel ProfileEditorViewModel(IScreen hostScreen);
|
||||
FolderTreeItemViewModel FolderTreeItemViewModel(TreeItemViewModel? parent, Folder folder);
|
||||
LayerTreeItemViewModel LayerTreeItemViewModel(TreeItemViewModel? parent, Layer layer);
|
||||
LayerShapeVisualizerViewModel LayerShapeVisualizerViewModel(Layer layer);
|
||||
LayerVisualizerViewModel LayerVisualizerViewModel(Layer layer);
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -48,6 +48,12 @@
|
||||
|
||||
<!-- The middle layer contains visualizers -->
|
||||
<ItemsControl Items="{CompiledBinding Visualizers}" ClipToBounds="False">
|
||||
<ItemsControl.Styles>
|
||||
<Style Selector="ContentPresenter">
|
||||
<Setter Property="Canvas.Left" Value="{Binding X}" />
|
||||
<Setter Property="Canvas.Top" Value="{Binding Y}" />
|
||||
</Style>
|
||||
</ItemsControl.Styles>
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<Canvas />
|
||||
|
||||
+25
-11
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reactive.Disposables;
|
||||
using Artemis.Core;
|
||||
@@ -8,6 +9,8 @@ using Artemis.UI.Screens.ProfileEditor.VisualEditor.Tools;
|
||||
using Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using DynamicData;
|
||||
using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor;
|
||||
@@ -16,14 +19,22 @@ public class VisualEditorViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly IProfileEditorVmFactory _vmFactory;
|
||||
private ObservableAsPropertyHelper<ProfileConfiguration?>? _profileConfiguration;
|
||||
private readonly SourceList<IVisualizerViewModel> _visualizers;
|
||||
|
||||
public VisualEditorViewModel(IProfileEditorService profileEditorService, IRgbService rgbService, IProfileEditorVmFactory vmFactory)
|
||||
{
|
||||
_vmFactory = vmFactory;
|
||||
_visualizers = new SourceList<IVisualizerViewModel>();
|
||||
|
||||
Devices = new ObservableCollection<ArtemisDevice>(rgbService.EnabledDevices);
|
||||
Visualizers = new ObservableCollection<IVisualizerViewModel>();
|
||||
Tools = new ObservableCollection<IToolViewModel>();
|
||||
|
||||
_visualizers.Connect()
|
||||
.Sort(SortExpressionComparer<IVisualizerViewModel>.Ascending(vm => vm.Order))
|
||||
.Bind(out ReadOnlyObservableCollection<IVisualizerViewModel> visualizers)
|
||||
.Subscribe();
|
||||
Visualizers = visualizers;
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
_profileConfiguration = profileEditorService.ProfileConfiguration.ToProperty(this, vm => vm.ProfileConfiguration).DisposeWith(d);
|
||||
@@ -34,21 +45,24 @@ public class VisualEditorViewModel : ActivatableViewModelBase
|
||||
public ProfileConfiguration? ProfileConfiguration => _profileConfiguration?.Value;
|
||||
|
||||
public ObservableCollection<ArtemisDevice> Devices { get; }
|
||||
public ObservableCollection<IVisualizerViewModel> Visualizers { get; set; }
|
||||
public ObservableCollection<IToolViewModel> Tools { get; set; }
|
||||
public ReadOnlyObservableCollection<IVisualizerViewModel> Visualizers { get; }
|
||||
public ObservableCollection<IToolViewModel> Tools { get; }
|
||||
|
||||
private void CreateVisualizers(ProfileConfiguration? profileConfiguration)
|
||||
{
|
||||
Visualizers.Clear();
|
||||
if (profileConfiguration?.Profile == null)
|
||||
return;
|
||||
|
||||
foreach (Layer layer in profileConfiguration.Profile.GetAllLayers())
|
||||
CreateVisualizer(layer);
|
||||
_visualizers.Edit(list =>
|
||||
{
|
||||
list.Clear();
|
||||
if (profileConfiguration?.Profile == null)
|
||||
return;
|
||||
foreach (Layer layer in profileConfiguration.Profile.GetAllLayers())
|
||||
CreateVisualizer(list, layer);
|
||||
});
|
||||
}
|
||||
|
||||
private void CreateVisualizer(Layer layer)
|
||||
private void CreateVisualizer(ICollection<IVisualizerViewModel> visualizerViewModels, Layer layer)
|
||||
{
|
||||
Visualizers.Add(_vmFactory.LayerVisualizerViewModel(layer));
|
||||
visualizerViewModels.Add(_vmFactory.LayerShapeVisualizerViewModel(layer));
|
||||
visualizerViewModels.Add(_vmFactory.LayerVisualizerViewModel(layer));
|
||||
}
|
||||
}
|
||||
+3
@@ -2,4 +2,7 @@
|
||||
|
||||
public interface IVisualizerViewModel
|
||||
{
|
||||
int X { get; }
|
||||
int Y { get; }
|
||||
int Order { get; }
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<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:visualizers="clr-namespace:Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers.LayerShapeVisualizerView"
|
||||
x:DataType="visualizers:LayerShapeVisualizerViewModel"
|
||||
ClipToBounds="False"
|
||||
ZIndex="2">
|
||||
<UserControl.Styles>
|
||||
<Style Selector="Path.layer-visualizer">
|
||||
<Setter Property="Stroke" Value="{StaticResource ButtonBorderBrushDisabled}" />
|
||||
</Style>
|
||||
<Style Selector="Path.layer-visualizer-selected">
|
||||
<Setter Property="Stroke" Value="{StaticResource SystemAccentColorLight1}" />
|
||||
</Style>
|
||||
<Style Selector="Path.layer-visualizer-unbound">
|
||||
<Setter Property="Stroke" Value="{StaticResource ButtonBorderBrushDisabled}" />
|
||||
<Setter Property="Opacity" Value="0.50" />
|
||||
</Style>
|
||||
<Style Selector="Path.layer-visualizer-unbound-selected">
|
||||
<Setter Property="Opacity" Value="0.75" />
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
<Canvas>
|
||||
<Path Classes="layer-visualizer-unbound"
|
||||
Classes.layer-visualizer-unbound-selected="{CompiledBinding Selected}"
|
||||
Data="{CompiledBinding ShapeGeometry, Mode=OneWay}"
|
||||
StrokeThickness="2"
|
||||
Margin="0 0 2 2"
|
||||
StrokeJoin="Round">
|
||||
</Path>
|
||||
<Border ClipToBounds="True"
|
||||
Width="{CompiledBinding LayerBounds.Width}"
|
||||
Height="{CompiledBinding LayerBounds.Height}">
|
||||
<Path Classes="layer-visualizer"
|
||||
Classes.layer-visualizer-selected="{CompiledBinding Selected}"
|
||||
Data="{CompiledBinding ShapeGeometry, Mode=OneWay}"
|
||||
StrokeThickness="2"
|
||||
Margin="0 0 2 2"
|
||||
StrokeJoin="Round">
|
||||
</Path>
|
||||
</Border>
|
||||
</Canvas>
|
||||
|
||||
|
||||
</UserControl>
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers
|
||||
{
|
||||
public partial class LayerShapeVisualizerView : ReactiveUserControl<LayerShapeVisualizerViewModel>
|
||||
{
|
||||
public LayerShapeVisualizerView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Reactive.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Extensions;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.Mixins;
|
||||
using Avalonia.Media;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
|
||||
|
||||
public class LayerShapeVisualizerViewModel : ActivatableViewModelBase, IVisualizerViewModel
|
||||
{
|
||||
private ObservableAsPropertyHelper<bool>? _selected;
|
||||
private Geometry? _shapeGeometry;
|
||||
|
||||
public LayerShapeVisualizerViewModel(Layer layer, IProfileEditorService profileEditorService)
|
||||
{
|
||||
Layer = layer;
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
Observable.FromEventPattern(x => Layer.RenderPropertiesUpdated += x, x => Layer.RenderPropertiesUpdated -= x).Subscribe(_ => Update()).DisposeWith(d);
|
||||
Observable.FromEventPattern<LayerPropertyEventArgs>(x => Layer.Transform.Position.CurrentValueSet += x, x => Layer.Transform.Position.CurrentValueSet -= x)
|
||||
.Subscribe(_ => UpdateTransform())
|
||||
.DisposeWith(d);
|
||||
Observable.FromEventPattern<LayerPropertyEventArgs>(x => Layer.Transform.Rotation.CurrentValueSet += x, x => Layer.Transform.Rotation.CurrentValueSet -= x)
|
||||
.Subscribe(_ => UpdateTransform())
|
||||
.DisposeWith(d);
|
||||
Observable.FromEventPattern<LayerPropertyEventArgs>(x => Layer.Transform.Scale.CurrentValueSet += x, x => Layer.Transform.Scale.CurrentValueSet -= x)
|
||||
.Subscribe(_ => UpdateTransform())
|
||||
.DisposeWith(d);
|
||||
Observable.FromEventPattern<LayerPropertyEventArgs>(x => Layer.Transform.AnchorPoint.CurrentValueSet += x, x => Layer.Transform.AnchorPoint.CurrentValueSet -= x)
|
||||
.Subscribe(_ => UpdateTransform())
|
||||
.DisposeWith(d);
|
||||
|
||||
_selected = profileEditorService.ProfileElement.Select(p => p == Layer).ToProperty(this, vm => vm.Selected).DisposeWith(d);
|
||||
|
||||
profileEditorService.Time.Subscribe(_ => UpdateTransform()).DisposeWith(d);
|
||||
Update();
|
||||
UpdateTransform();
|
||||
});
|
||||
}
|
||||
|
||||
public Layer Layer { get; }
|
||||
public bool Selected => _selected?.Value ?? false;
|
||||
public Rect LayerBounds => Layer.Bounds.ToRect();
|
||||
|
||||
public Geometry? ShapeGeometry
|
||||
{
|
||||
get => _shapeGeometry;
|
||||
set => this.RaiseAndSetIfChanged(ref _shapeGeometry, value);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Layer.General.ShapeType.CurrentValue == LayerShapeType.Rectangle)
|
||||
ShapeGeometry = new RectangleGeometry(new Rect(0, 0, Layer.Bounds.Width, Layer.Bounds.Height));
|
||||
else
|
||||
ShapeGeometry = new EllipseGeometry(new Rect(0, 0, Layer.Bounds.Width, Layer.Bounds.Height));
|
||||
|
||||
this.RaisePropertyChanged(nameof(X));
|
||||
this.RaisePropertyChanged(nameof(Y));
|
||||
this.RaisePropertyChanged(nameof(LayerBounds));
|
||||
}
|
||||
|
||||
private void UpdateTransform()
|
||||
{
|
||||
if (ShapeGeometry != null)
|
||||
ShapeGeometry.Transform = new MatrixTransform(Layer.GetTransformMatrix(true, true, true, true).ToMatrix());
|
||||
}
|
||||
|
||||
public int X => Layer.Bounds.Left;
|
||||
public int Y => Layer.Bounds.Top;
|
||||
public int Order => 2;
|
||||
}
|
||||
+10
-5
@@ -5,19 +5,24 @@
|
||||
xmlns:visualizers="clr-namespace:Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers.LayerVisualizerView"
|
||||
x:DataType="visualizers:LayerVisualizerViewModel">
|
||||
x:DataType="visualizers:LayerVisualizerViewModel"
|
||||
ZIndex="1">
|
||||
<UserControl.Styles>
|
||||
<Style Selector="Path.layer-visualizer">
|
||||
<Setter Property="Stroke" Value="{StaticResource ButtonBorderBrushDisabled}" />
|
||||
</Style>
|
||||
<Style Selector="Path.layer-visualizer-selected">
|
||||
<Setter Property="Stroke" Value="{StaticResource SystemAccentColorLight1}" />
|
||||
<Setter Property="Stroke" Value="{StaticResource SystemAccentColorDark2}" />
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
<Path Classes="layer-visualizer"
|
||||
Classes.layer-visualizer-selected="{CompiledBinding Selected}"
|
||||
Data="{CompiledBinding ShapeGeometry, Mode=OneWay}"
|
||||
StrokeThickness="2"
|
||||
Margin="0 0 2 2">
|
||||
Margin="0 0 2 2"
|
||||
StrokeDashArray="6,2"
|
||||
StrokeJoin="Round">
|
||||
<Path.Data>
|
||||
<RectangleGeometry Rect="{CompiledBinding LayerBounds}"></RectangleGeometry>
|
||||
</Path.Data>
|
||||
</Path>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
+14
-36
@@ -2,66 +2,44 @@
|
||||
using System.Reactive.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Extensions;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.Mixins;
|
||||
using Avalonia.Media;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.VisualEditor.Visualizers;
|
||||
|
||||
public class LayerVisualizerViewModel : ActivatableViewModelBase, IVisualizerViewModel
|
||||
{
|
||||
private Geometry? _shapeGeometry;
|
||||
private ObservableAsPropertyHelper<bool>? _selected;
|
||||
|
||||
public LayerVisualizerViewModel(Layer layer, IProfileEditorService profileEditorService)
|
||||
{
|
||||
Layer = layer;
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
Observable.FromEventPattern(x => Layer.RenderPropertiesUpdated += x, x => Layer.RenderPropertiesUpdated -= x).Subscribe(_ => UpdateShape()).DisposeWith(d);
|
||||
Observable.FromEventPattern<LayerPropertyEventArgs>(x => Layer.Transform.Position.CurrentValueSet += x, x => Layer.Transform.Position.CurrentValueSet -= x)
|
||||
.Subscribe(_ => UpdateTransform())
|
||||
Observable.FromEventPattern(x => Layer.RenderPropertiesUpdated += x, x => Layer.RenderPropertiesUpdated -= x)
|
||||
.Subscribe(_ => Update())
|
||||
.DisposeWith(d);
|
||||
Observable.FromEventPattern<LayerPropertyEventArgs>(x => Layer.Transform.Rotation.CurrentValueSet += x, x => Layer.Transform.Rotation.CurrentValueSet -= x)
|
||||
.Subscribe(_ => UpdateTransform())
|
||||
_selected = profileEditorService.ProfileElement
|
||||
.Select(p => p == Layer)
|
||||
.ToProperty(this, vm => vm.Selected)
|
||||
.DisposeWith(d);
|
||||
Observable.FromEventPattern<LayerPropertyEventArgs>(x => Layer.Transform.Scale.CurrentValueSet += x, x => Layer.Transform.Scale.CurrentValueSet -= x)
|
||||
.Subscribe(_ => UpdateTransform())
|
||||
.DisposeWith(d);
|
||||
Observable.FromEventPattern<LayerPropertyEventArgs>(x => Layer.Transform.AnchorPoint.CurrentValueSet += x, x => Layer.Transform.AnchorPoint.CurrentValueSet -= x)
|
||||
.Subscribe(_ => UpdateTransform())
|
||||
.DisposeWith(d);
|
||||
|
||||
_selected = profileEditorService.ProfileElement.Select(p => p == Layer).ToProperty(this, vm => vm.Selected).DisposeWith(d);
|
||||
|
||||
profileEditorService.Time.Subscribe(_ => UpdateTransform()).DisposeWith(d);
|
||||
UpdateShape();
|
||||
});
|
||||
}
|
||||
|
||||
public Layer Layer { get; }
|
||||
public bool Selected => _selected?.Value ?? false;
|
||||
public Rect LayerBounds => new(0, 0, Layer.Bounds.Width, Layer.Bounds.Height);
|
||||
|
||||
public Geometry? ShapeGeometry
|
||||
private void Update()
|
||||
{
|
||||
get => _shapeGeometry;
|
||||
set => this.RaiseAndSetIfChanged(ref _shapeGeometry, value);
|
||||
this.RaisePropertyChanged(nameof(X));
|
||||
this.RaisePropertyChanged(nameof(Y));
|
||||
this.RaisePropertyChanged(nameof(LayerBounds));
|
||||
}
|
||||
|
||||
private void UpdateShape()
|
||||
{
|
||||
if (Layer.General.ShapeType.CurrentValue == LayerShapeType.Rectangle)
|
||||
ShapeGeometry = new RectangleGeometry(Layer.Bounds.ToRect());
|
||||
else
|
||||
ShapeGeometry = new EllipseGeometry(Layer.Bounds.ToRect());
|
||||
}
|
||||
|
||||
private void UpdateTransform()
|
||||
{
|
||||
if (ShapeGeometry != null)
|
||||
ShapeGeometry.Transform = new MatrixTransform(Layer.GetTransformMatrix(false, true, true, true).ToMatrix());
|
||||
}
|
||||
public int X => Layer.Bounds.Left;
|
||||
public int Y => Layer.Bounds.Top;
|
||||
public int Order => 1;
|
||||
}
|
||||
Reference in new issue
Block a user