1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-31 09:43:46 +00:00

UI - Large amount of memory improvements in the VMs

Core - Minor memory improvements
This commit is contained in:
SpoinkyNL 2020-06-19 21:48:26 +02:00
parent 63cb6d211e
commit 24afb6b0f5
26 changed files with 243 additions and 191 deletions

View File

@ -1,4 +1,5 @@
using RGB.NET.Core; using System.Text;
using RGB.NET.Core;
namespace Artemis.Core.Extensions namespace Artemis.Core.Extensions
{ {
@ -6,11 +7,17 @@ namespace Artemis.Core.Extensions
{ {
public static string GetDeviceIdentifier(this IRGBDevice rgbDevice) public static string GetDeviceIdentifier(this IRGBDevice rgbDevice)
{ {
return rgbDevice.DeviceInfo.DeviceName + var builder = new StringBuilder();
"-" + rgbDevice.DeviceInfo.Manufacturer + builder.Append(rgbDevice.DeviceInfo.DeviceName);
"-" + rgbDevice.DeviceInfo.Model + builder.Append('-');
"-" + rgbDevice.DeviceInfo.DeviceType + builder.Append(rgbDevice.DeviceInfo.Manufacturer);
"-" + rgbDevice.DeviceInfo.Lighting; builder.Append('-');
builder.Append(rgbDevice.DeviceInfo.Model);
builder.Append('-');
builder.Append(rgbDevice.DeviceInfo.DeviceType);
builder.Append('-');
builder.Append(rgbDevice.DeviceInfo.Lighting);
return builder.ToString();
} }
} }
} }

View File

@ -80,7 +80,7 @@ namespace Artemis.Core.Models.Profile
{ {
if (!Enabled || Path == null || !Children.Any(c => c.Enabled)) if (!Enabled || Path == null || !Children.Any(c => c.Enabled))
return; return;
if (_folderBitmap == null) if (_folderBitmap == null)
_folderBitmap = new SKBitmap(new SKImageInfo((int) Path.Bounds.Width, (int) Path.Bounds.Height)); _folderBitmap = new SKBitmap(new SKImageInfo((int) Path.Bounds.Width, (int) Path.Bounds.Height));
else if (_folderBitmap.Info.Width != (int) Path.Bounds.Width || _folderBitmap.Info.Height != (int) Path.Bounds.Height) else if (_folderBitmap.Info.Width != (int) Path.Bounds.Width || _folderBitmap.Info.Height != (int) Path.Bounds.Height)
@ -89,11 +89,11 @@ namespace Artemis.Core.Models.Profile
_folderBitmap = new SKBitmap(new SKImageInfo((int) Path.Bounds.Width, (int) Path.Bounds.Height)); _folderBitmap = new SKBitmap(new SKImageInfo((int) Path.Bounds.Width, (int) Path.Bounds.Height));
} }
using var folderPath = new SKPath(Path);
using var folderCanvas = new SKCanvas(_folderBitmap); using var folderCanvas = new SKCanvas(_folderBitmap);
using var folderPaint = new SKPaint(); using var folderPaint = new SKPaint();
folderCanvas.Clear(); folderCanvas.Clear();
var folderPath = new SKPath(Path);
folderPath.Transform(SKMatrix.MakeTranslation(folderPath.Bounds.Left * -1, folderPath.Bounds.Top * -1)); folderPath.Transform(SKMatrix.MakeTranslation(folderPath.Bounds.Left * -1, folderPath.Bounds.Top * -1));
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled)) foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
@ -112,7 +112,7 @@ namespace Artemis.Core.Models.Profile
canvas.Save(); canvas.Save();
var clipPath = new SKPath(folderPath); using var clipPath = new SKPath(folderPath);
clipPath.Transform(SKMatrix.MakeTranslation(targetLocation.X, targetLocation.Y)); clipPath.Transform(SKMatrix.MakeTranslation(targetLocation.X, targetLocation.Y));
canvas.ClipPath(clipPath); canvas.ClipPath(clipPath);

View File

@ -246,6 +246,7 @@ namespace Artemis.Core.Models.Profile
_layerBitmap = new SKBitmap(new SKImageInfo((int)Path.Bounds.Width, (int)Path.Bounds.Height)); _layerBitmap = new SKBitmap(new SKImageInfo((int)Path.Bounds.Width, (int)Path.Bounds.Height));
} }
using var layerPath = new SKPath(Path);
using var layerCanvas = new SKCanvas(_layerBitmap); using var layerCanvas = new SKCanvas(_layerBitmap);
using var layerPaint = new SKPaint using var layerPaint = new SKPaint
{ {
@ -254,8 +255,7 @@ namespace Artemis.Core.Models.Profile
Color = new SKColor(0, 0, 0, (byte) (Transform.Opacity.CurrentValue * 2.55f)) Color = new SKColor(0, 0, 0, (byte) (Transform.Opacity.CurrentValue * 2.55f))
}; };
layerCanvas.Clear(); layerCanvas.Clear();
var layerPath = new SKPath(Path);
layerPath.Transform(SKMatrix.MakeTranslation(layerPath.Bounds.Left * -1, layerPath.Bounds.Top * -1)); layerPath.Transform(SKMatrix.MakeTranslation(layerPath.Bounds.Left * -1, layerPath.Bounds.Top * -1));
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled)) foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
@ -278,11 +278,13 @@ namespace Artemis.Core.Models.Profile
targetLocation = Path.Bounds.Location - parentFolder.Path.Bounds.Location; targetLocation = Path.Bounds.Location - parentFolder.Path.Bounds.Location;
canvas.DrawBitmap(_layerBitmap, targetLocation, layerPaint); canvas.DrawBitmap(_layerBitmap, targetLocation, layerPaint);
} }
private void SimpleRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint, SKPath layerPath) private void SimpleRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint, SKPath layerPath)
{ {
LayerBrush.InternalRender(canvas, canvasInfo, new SKPath(LayerShape.Path), paint); using var renderPath = new SKPath(LayerShape.Path);
LayerBrush.InternalRender(canvas, canvasInfo, renderPath, paint);
} }
private void StretchRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint, SKPath layerPath) private void StretchRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint, SKPath layerPath)
@ -302,8 +304,9 @@ namespace Artemis.Core.Models.Profile
canvas.RotateDegrees(rotationProperty, anchorPosition.X, anchorPosition.Y); canvas.RotateDegrees(rotationProperty, anchorPosition.X, anchorPosition.Y);
canvas.Scale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, anchorPosition.X, anchorPosition.Y); canvas.Scale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, anchorPosition.X, anchorPosition.Y);
canvas.Translate(x, y); canvas.Translate(x, y);
LayerBrush.InternalRender(canvas, canvasInfo, new SKPath(LayerShape.Path), paint); using var renderPath = new SKPath(LayerShape.Path);
LayerBrush.InternalRender(canvas, canvasInfo, renderPath, paint);
} }
private void ClipRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint, SKPath layerPath) private void ClipRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint, SKPath layerPath)
@ -319,7 +322,7 @@ namespace Artemis.Core.Models.Profile
var x = anchorPosition.X - layerPath.Bounds.MidX - anchorProperty.X * layerPath.Bounds.Width; var x = anchorPosition.X - layerPath.Bounds.MidX - anchorProperty.X * layerPath.Bounds.Width;
var y = anchorPosition.Y - layerPath.Bounds.MidY - anchorProperty.Y * layerPath.Bounds.Height; var y = anchorPosition.Y - layerPath.Bounds.MidY - anchorProperty.Y * layerPath.Bounds.Height;
var clipPath = new SKPath(LayerShape.Path); using var clipPath = new SKPath(LayerShape.Path);
clipPath.Transform(SKMatrix.MakeTranslation(x, y)); clipPath.Transform(SKMatrix.MakeTranslation(x, y));
clipPath.Transform(SKMatrix.MakeScale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, anchorPosition.X, anchorPosition.Y)); clipPath.Transform(SKMatrix.MakeScale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, anchorPosition.X, anchorPosition.Y));
clipPath.Transform(SKMatrix.MakeRotationDegrees(rotationProperty, anchorPosition.X, anchorPosition.Y)); clipPath.Transform(SKMatrix.MakeRotationDegrees(rotationProperty, anchorPosition.X, anchorPosition.Y));
@ -336,7 +339,7 @@ namespace Artemis.Core.Models.Profile
Math.Max(clipPath.Bounds.Right - x, Bounds.Right - x), Math.Max(clipPath.Bounds.Right - x, Bounds.Right - x),
Math.Max(clipPath.Bounds.Bottom - y, Bounds.Bottom - y) Math.Max(clipPath.Bounds.Bottom - y, Bounds.Bottom - y)
); );
var renderPath = new SKPath(); using var renderPath = new SKPath();
renderPath.AddRect(boundsRect); renderPath.AddRect(boundsRect);
LayerBrush.InternalRender(canvas, canvasInfo, renderPath, paint); LayerBrush.InternalRender(canvas, canvasInfo, renderPath, paint);

View File

@ -4,8 +4,6 @@ namespace Artemis.Core.Models.Profile.LayerShapes
{ {
public abstract class LayerShape public abstract class LayerShape
{ {
private SKPath _path;
protected LayerShape(Layer layer) protected LayerShape(Layer layer)
{ {
Layer = layer; Layer = layer;
@ -17,13 +15,9 @@ namespace Artemis.Core.Models.Profile.LayerShapes
public Layer Layer { get; set; } public Layer Layer { get; set; }
/// <summary> /// <summary>
/// Gets a copy of the path outlining the shape /// Gets a the path outlining the shape
/// </summary> /// </summary>
public SKPath Path public SKPath Path { get; protected set; }
{
get => _path != null ? new SKPath(_path) : null;
protected set => _path = value;
}
public abstract void CalculateRenderProperties(); public abstract void CalculateRenderProperties();
} }

View File

@ -11,12 +11,12 @@ namespace Artemis.Core.Models.Profile
private SKPath _path; private SKPath _path;
/// <summary> /// <summary>
/// Gets a copy of the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is /// Gets the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is
/// clipped. /// clipped.
/// </summary> /// </summary>
public SKPath Path public SKPath Path
{ {
get => _path != null ? new SKPath(_path) : null; get => _path;
protected set protected set
{ {
_path = value; _path = value;

View File

@ -98,7 +98,7 @@ namespace Artemis.Core.RGB.NET
var bitmapWidth = Bitmap.Width; var bitmapWidth = Bitmap.Width;
var bitmapHeight = Bitmap.Height; var bitmapHeight = Bitmap.Height;
var pixmap = Bitmap.PeekPixels(); using var pixmap = Bitmap.PeekPixels();
foreach (var renderTarget in renderTargets) foreach (var renderTarget in renderTargets)
{ {
// SKRect has all the good stuff we need // SKRect has all the good stuff we need

View File

@ -1,56 +0,0 @@
using System;
using System.Linq;
using Artemis.Core.Extensions;
using RGB.NET.Core;
using SkiaSharp;
namespace Artemis.Core.RGB.NET
{
public class GraphicsDecorator : AbstractDecorator, IBrushDecorator, IDisposable
{
private readonly double _scale;
public GraphicsDecorator(ILedGroup ledGroup, double scale)
{
_scale = scale;
var leds = ledGroup.GetLeds().ToList();
if (!leds.Any())
return;
var width = Math.Min(leds.Max(l => l.AbsoluteLedRectangle.Location.X + l.AbsoluteLedRectangle.Size.Width) * scale, 4096);
var height = Math.Min(leds.Max(l => l.AbsoluteLedRectangle.Location.Y + l.AbsoluteLedRectangle.Size.Height) * scale, 4096);
Bitmap = new SKBitmap(new SKImageInfo(width.RoundToInt(), height.RoundToInt()));
}
public SKBitmap Bitmap { get; private set; }
public Color ManipulateColor(Rectangle rectangle, BrushRenderTarget renderTarget, Color color)
{
if (Bitmap == null)
return new Color(0, 0, 0);
var x = renderTarget.Led.AbsoluteLedRectangle.Center.X * _scale;
var y = renderTarget.Led.AbsoluteLedRectangle.Center.Y * _scale;
var pixel = Bitmap.GetPixel(RoundToInt(x), RoundToInt(y));
return new Color(pixel.Alpha, pixel.Red, pixel.Green, pixel.Blue);
}
public override void OnDetached(IDecoratable decoratable)
{
Dispose();
}
public void Dispose()
{
Bitmap?.Dispose();
Bitmap = null;
}
private int RoundToInt(double number)
{
return (int) Math.Round(number, MidpointRounding.AwayFromZero);
}
}
}

View File

@ -1,9 +1,15 @@
using System.Windows; using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
namespace Artemis.UI.Behaviors namespace Artemis.UI.Behaviors
{ {
public class InputBindingBehavior public class InputBindingBehavior
{ {
private static List<Tuple<FrameworkElement, Window, InputBinding>> _movedInputBindings = new List<Tuple<FrameworkElement, Window, InputBinding>>();
public static readonly DependencyProperty PropagateInputBindingsToWindowProperty = public static readonly DependencyProperty PropagateInputBindingsToWindowProperty =
DependencyProperty.RegisterAttached("PropagateInputBindingsToWindow", typeof(bool), typeof(InputBindingBehavior), DependencyProperty.RegisterAttached("PropagateInputBindingsToWindow", typeof(bool), typeof(InputBindingBehavior),
new PropertyMetadata(false, OnPropagateInputBindingsToWindowChanged)); new PropertyMetadata(false, OnPropagateInputBindingsToWindowChanged));
@ -21,13 +27,13 @@ namespace Artemis.UI.Behaviors
private static void OnPropagateInputBindingsToWindowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) private static void OnPropagateInputBindingsToWindowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ {
((FrameworkElement) d).Loaded += OnLoaded; ((FrameworkElement) d).Loaded += OnLoaded;
((FrameworkElement) d).Unloaded += OnUnloaded;
} }
private static void OnLoaded(object sender, RoutedEventArgs e) private static void OnLoaded(object sender, RoutedEventArgs e)
{ {
var frameworkElement = (FrameworkElement) sender; var frameworkElement = (FrameworkElement) sender;
frameworkElement.Loaded -= OnLoaded;
var window = Window.GetWindow(frameworkElement); var window = Window.GetWindow(frameworkElement);
if (window == null) return; if (window == null) return;
@ -37,7 +43,23 @@ namespace Artemis.UI.Behaviors
var inputBinding = frameworkElement.InputBindings[i]; var inputBinding = frameworkElement.InputBindings[i];
window.InputBindings.Add(inputBinding); window.InputBindings.Add(inputBinding);
frameworkElement.InputBindings.Remove(inputBinding); frameworkElement.InputBindings.Remove(inputBinding);
_movedInputBindings.Add(new Tuple<FrameworkElement, Window, InputBinding>(frameworkElement, window, inputBinding));
} }
} }
private static void OnUnloaded(object sender, RoutedEventArgs e)
{
var frameworkElement = (FrameworkElement) sender;
var toRemove = _movedInputBindings.Where(m => m.Item1 == frameworkElement).ToList();
foreach (var (_, window, inputBinding) in toRemove)
{
if (window.InputBindings.Contains(inputBinding))
window.InputBindings.Remove(inputBinding);
}
_movedInputBindings = _movedInputBindings.Where(b => b.Item1 != frameworkElement).ToList();
}
} }
} }

View File

@ -86,9 +86,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
ProfileEditorService.CurrentTimeChanged -= ProfileEditorServiceOnCurrentTimeChanged; ProfileEditorService.CurrentTimeChanged -= ProfileEditorServiceOnCurrentTimeChanged;
ProfileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged; ProfileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
PopulateProperties(null);
base.OnClose(); base.OnClose();
} }
protected override void OnActivate()
{
PopulateProperties(ProfileEditorService.SelectedProfileElement as PropertiesProfileElement);
base.OnActivate();
}
protected override void OnDeactivate() protected override void OnDeactivate()
{ {
Pause(); Pause();

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Input;
using Artemis.Core.Models.Profile.LayerProperties; using Artemis.Core.Models.Profile.LayerProperties;
using Artemis.Core.Utilities; using Artemis.Core.Utilities;
using Artemis.UI.Shared.Services.Interfaces; using Artemis.UI.Shared.Services.Interfaces;
@ -13,8 +12,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
{ {
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
public TimelineKeyframeViewModel(IProfileEditorService profileEditorService, TimelineViewModel timelineViewModel, LayerPropertyKeyframe<T> layerPropertyKeyframe) public TimelineKeyframeViewModel(IProfileEditorService profileEditorService, LayerPropertyKeyframe<T> layerPropertyKeyframe)
: base(profileEditorService, timelineViewModel, layerPropertyKeyframe) : base(profileEditorService, layerPropertyKeyframe)
{ {
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
LayerPropertyKeyframe = layerPropertyKeyframe; LayerPropertyKeyframe = layerPropertyKeyframe;
@ -48,13 +47,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
public abstract class TimelineKeyframeViewModel : PropertyChangedBase public abstract class TimelineKeyframeViewModel : PropertyChangedBase
{ {
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private readonly TimelineViewModel _timelineViewModel;
private int _pixelsPerSecond; private int _pixelsPerSecond;
protected TimelineKeyframeViewModel(IProfileEditorService profileEditorService, TimelineViewModel timelineViewModel, BaseLayerPropertyKeyframe baseLayerPropertyKeyframe) protected TimelineKeyframeViewModel(IProfileEditorService profileEditorService, BaseLayerPropertyKeyframe baseLayerPropertyKeyframe)
{ {
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_timelineViewModel = timelineViewModel;
BaseLayerPropertyKeyframe = baseLayerPropertyKeyframe; BaseLayerPropertyKeyframe = baseLayerPropertyKeyframe;
EasingViewModels = new BindableCollection<TimelineEasingViewModel>(); EasingViewModels = new BindableCollection<TimelineEasingViewModel>();
} }
@ -78,64 +75,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
#region Keyframe movement #region Keyframe movement
public void KeyframeMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Released)
return;
((IInputElement) sender).CaptureMouse();
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift) && !IsSelected)
_timelineViewModel.SelectKeyframe(this, true, false);
else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
_timelineViewModel.SelectKeyframe(this, false, true);
else if (!IsSelected)
_timelineViewModel.SelectKeyframe(this, false, false);
e.Handled = true;
}
public void KeyframeMouseUp(object sender, MouseButtonEventArgs e)
{
_profileEditorService.UpdateSelectedProfileElement();
_timelineViewModel.ReleaseSelectedKeyframes();
((IInputElement) sender).ReleaseMouseCapture();
}
public void KeyframeMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
_timelineViewModel.MoveSelectedKeyframes(GetCursorTime(e.GetPosition(ParentView)));
e.Handled = true;
}
private TimeSpan GetCursorTime(Point position)
{
// Get the parent grid, need that for our position
var x = Math.Max(0, position.X);
var time = TimeSpan.FromSeconds(x / _pixelsPerSecond);
// Round the time to something that fits the current zoom level
if (_pixelsPerSecond < 200)
time = TimeSpan.FromMilliseconds(Math.Round(time.TotalMilliseconds / 5.0) * 5.0);
else if (_pixelsPerSecond < 500)
time = TimeSpan.FromMilliseconds(Math.Round(time.TotalMilliseconds / 2.0) * 2.0);
else
time = TimeSpan.FromMilliseconds(Math.Round(time.TotalMilliseconds));
// If shift is held, snap to the current time
// Take a tolerance of 5 pixels (half a keyframe width)
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
{
var tolerance = 1000f / _pixelsPerSecond * 5;
if (Math.Abs(_profileEditorService.CurrentTime.TotalMilliseconds - time.TotalMilliseconds) < tolerance)
time = _profileEditorService.CurrentTime;
}
return time;
}
#endregion #endregion
#region Easing #region Easing

View File

@ -6,7 +6,7 @@ using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
{ {
public class TimelinePropertyGroupViewModel public class TimelinePropertyGroupViewModel : PropertyChangedBase
{ {
public TimelinePropertyGroupViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel) public TimelinePropertyGroupViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel)
{ {
@ -20,7 +20,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
public LayerPropertyGroupViewModel LayerPropertyGroupViewModel { get; } public LayerPropertyGroupViewModel LayerPropertyGroupViewModel { get; }
public BindableCollection<double> TimelineKeyframeViewModels { get; set; } public BindableCollection<double> TimelineKeyframeViewModels { get; set; }
public TimelineViewModel TimelineViewModel { get; set; }
public void UpdateKeyframes() public void UpdateKeyframes()
{ {

View File

@ -33,7 +33,7 @@
Height="10" Height="10"
Margin="-5,6,0,0" Margin="-5,6,0,0"
ToolTip="{Binding Timestamp}" ToolTip="{Binding Timestamp}"
s:View.ActionTarget="{Binding}" s:View.ActionTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:TimelineView}}, Path=DataContext}"
MouseDown="{s:Action KeyframeMouseDown}" MouseDown="{s:Action KeyframeMouseDown}"
MouseUp="{s:Action KeyframeMouseUp}" MouseUp="{s:Action KeyframeMouseUp}"
MouseMove="{s:Action KeyframeMouseMove}" MouseMove="{s:Action KeyframeMouseMove}"

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Linq; using System.Linq;
using Artemis.UI.Exceptions;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract; using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract;
using Artemis.UI.Shared.Services.Interfaces; using Artemis.UI.Shared.Services.Interfaces;
using Stylet; using Stylet;
@ -26,9 +25,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
public override void UpdateKeyframes() public override void UpdateKeyframes()
{ {
if (TimelineViewModel == null)
throw new ArtemisUIException("Timeline view model must be set before keyframes can be updated");
// Only show keyframes if they are enabled // Only show keyframes if they are enabled
if (LayerPropertyViewModel.LayerProperty.KeyframesEnabled) if (LayerPropertyViewModel.LayerProperty.KeyframesEnabled)
{ {
@ -37,7 +33,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
TimelineKeyframeViewModels.RemoveRange(toRemove); TimelineKeyframeViewModels.RemoveRange(toRemove);
TimelineKeyframeViewModels.AddRange( TimelineKeyframeViewModels.AddRange(
keyframes.Where(k => TimelineKeyframeViewModels.All(t => t.BaseLayerPropertyKeyframe != k)) keyframes.Where(k => TimelineKeyframeViewModels.All(t => t.BaseLayerPropertyKeyframe != k))
.Select(k => new TimelineKeyframeViewModel<T>(_profileEditorService, TimelineViewModel, k)) .Select(k => new TimelineKeyframeViewModel<T>(_profileEditorService, k))
); );
} }
else else
@ -67,7 +63,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
} }
} }
public abstract class TimelinePropertyViewModel : IDisposable public abstract class TimelinePropertyViewModel : PropertyChangedBase, IDisposable
{ {
protected TimelinePropertyViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel) protected TimelinePropertyViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel)
{ {
@ -76,7 +72,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
} }
public LayerPropertyBaseViewModel LayerPropertyBaseViewModel { get; } public LayerPropertyBaseViewModel LayerPropertyBaseViewModel { get; }
public TimelineViewModel TimelineViewModel { get; set; }
public BindableCollection<TimelineKeyframeViewModel> TimelineKeyframeViewModels { get; set; } public BindableCollection<TimelineKeyframeViewModel> TimelineKeyframeViewModels { get; set; }
public abstract void Dispose(); public abstract void Dispose();

View File

@ -4,19 +4,24 @@ using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Shapes;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract; using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract;
using Artemis.UI.Shared.Services.Interfaces;
using Artemis.UI.Shared.Utilities; using Artemis.UI.Shared.Utilities;
using Stylet; using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
{ {
public class TimelineViewModel : PropertyChangedBase public class TimelineViewModel : Screen
{ {
private readonly LayerPropertiesViewModel _layerPropertiesViewModel; private readonly LayerPropertiesViewModel _layerPropertiesViewModel;
private readonly IProfileEditorService _profileEditorService;
public TimelineViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups) public TimelineViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups,
IProfileEditorService profileEditorService)
{ {
_layerPropertiesViewModel = layerPropertiesViewModel; _layerPropertiesViewModel = layerPropertiesViewModel;
_profileEditorService = profileEditorService;
LayerPropertyGroups = layerPropertyGroups; LayerPropertyGroups = layerPropertyGroups;
SelectionRectangle = new RectangleGeometry(); SelectionRectangle = new RectangleGeometry();
@ -32,20 +37,82 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
{ {
foreach (var layerPropertyGroupViewModel in LayerPropertyGroups) foreach (var layerPropertyGroupViewModel in LayerPropertyGroups)
{ {
layerPropertyGroupViewModel.TimelinePropertyGroupViewModel.TimelineViewModel = this;
layerPropertyGroupViewModel.TimelinePropertyGroupViewModel.UpdateKeyframes(); layerPropertyGroupViewModel.TimelinePropertyGroupViewModel.UpdateKeyframes();
foreach (var layerPropertyBaseViewModel in layerPropertyGroupViewModel.GetAllChildren()) foreach (var layerPropertyBaseViewModel in layerPropertyGroupViewModel.GetAllChildren())
{ {
if (layerPropertyBaseViewModel is LayerPropertyViewModel layerPropertyViewModel) if (layerPropertyBaseViewModel is LayerPropertyViewModel layerPropertyViewModel)
{
layerPropertyViewModel.TimelinePropertyBaseViewModel.TimelineViewModel = this;
layerPropertyViewModel.TimelinePropertyBaseViewModel.UpdateKeyframes(); layerPropertyViewModel.TimelinePropertyBaseViewModel.UpdateKeyframes();
}
} }
} }
} }
#region Command handlers
public void KeyframeMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Released)
return;
var viewModel = (sender as Ellipse)?.DataContext as TimelineKeyframeViewModel;
if (viewModel == null)
return;
((IInputElement) sender).CaptureMouse();
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift) && !viewModel.IsSelected)
SelectKeyframe(viewModel, true, false);
else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
SelectKeyframe(viewModel, false, true);
else if (!viewModel.IsSelected)
SelectKeyframe(viewModel, false, false);
e.Handled = true;
}
public void KeyframeMouseUp(object sender, MouseButtonEventArgs e)
{
_profileEditorService.UpdateSelectedProfileElement();
ReleaseSelectedKeyframes();
((IInputElement) sender).ReleaseMouseCapture();
}
public void KeyframeMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
MoveSelectedKeyframes(GetCursorTime(e.GetPosition(View)));
e.Handled = true;
}
private TimeSpan GetCursorTime(Point position)
{
// Get the parent grid, need that for our position
var x = Math.Max(0, position.X);
var time = TimeSpan.FromSeconds(x / _profileEditorService.PixelsPerSecond);
// Round the time to something that fits the current zoom level
if (_profileEditorService.PixelsPerSecond < 200)
time = TimeSpan.FromMilliseconds(Math.Round(time.TotalMilliseconds / 5.0) * 5.0);
else if (_profileEditorService.PixelsPerSecond < 500)
time = TimeSpan.FromMilliseconds(Math.Round(time.TotalMilliseconds / 2.0) * 2.0);
else
time = TimeSpan.FromMilliseconds(Math.Round(time.TotalMilliseconds));
// If shift is held, snap to the current time
// Take a tolerance of 5 pixels (half a keyframe width)
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
{
var tolerance = 1000f / _profileEditorService.PixelsPerSecond * 5;
if (Math.Abs(_profileEditorService.CurrentTime.TotalMilliseconds - time.TotalMilliseconds) < tolerance)
time = _profileEditorService.CurrentTime;
}
return time;
}
#endregion
#region Keyframe movement #region Keyframe movement
public void MoveSelectedKeyframes(TimeSpan cursorTime) public void MoveSelectedKeyframes(TimeSpan cursorTime)

View File

@ -4,10 +4,11 @@ using Artemis.Core.Services.Interfaces;
using Artemis.UI.Screens.Module.ProfileEditor.Dialogs; using Artemis.UI.Screens.Module.ProfileEditor.Dialogs;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract; using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract;
using Artemis.UI.Shared.Services.Interfaces; using Artemis.UI.Shared.Services.Interfaces;
using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree
{ {
public class TreePropertyGroupViewModel public class TreePropertyGroupViewModel : PropertyChangedBase
{ {
private readonly IDialogService _dialogService; private readonly IDialogService _dialogService;
private readonly ILayerService _layerService; private readonly ILayerService _layerService;

View File

@ -5,6 +5,7 @@ using Artemis.Core.Utilities;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract; using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract;
using Artemis.UI.Shared.PropertyInput; using Artemis.UI.Shared.PropertyInput;
using Artemis.UI.Shared.Services.Interfaces; using Artemis.UI.Shared.Services.Interfaces;
using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree
{ {
@ -56,7 +57,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree
} }
} }
public abstract class TreePropertyViewModel : IDisposable public abstract class TreePropertyViewModel : PropertyChangedBase, IDisposable
{ {
protected TreePropertyViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel) protected TreePropertyViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel)
{ {

View File

@ -135,6 +135,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree
} }
} }
protected override void OnClose()
{
RootFolder?.Dispose();
RootFolder = null;
base.OnClose();
}
#region Event handlers #region Event handlers
private void OnProfileElementSelected(object sender, EventArgs e) private void OnProfileElementSelected(object sender, EventArgs e)

View File

@ -55,6 +55,7 @@
Width="18" Width="18"
Height="18" Height="18"
IsChecked="{Binding ProfileElement.Enabled}" IsChecked="{Binding ProfileElement.Enabled}"
Command="{s:Action EnableToggled}"
VerticalAlignment="Center" Padding="-25"> VerticalAlignment="Center" Padding="-25">
<materialDesign:PackIcon Kind="Eye" Height="13" Width="13" /> <materialDesign:PackIcon Kind="Eye" Height="13" Width="13" />
</ToggleButton> </ToggleButton>

View File

@ -7,6 +7,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
{ {
public class FolderViewModel : TreeItemViewModel public class FolderViewModel : TreeItemViewModel
{ {
protected override void Dispose(bool disposing)
{
if (disposing)
{
foreach (var treeItemViewModel in Children)
treeItemViewModel.Dispose();
Children.Clear();
}
base.Dispose(disposing);
}
// I hate this about DI, oh well // I hate this about DI, oh well
public FolderViewModel(ProfileElement folder, public FolderViewModel(ProfileElement folder,
IProfileEditorService profileEditorService, IProfileEditorService profileEditorService,

View File

@ -48,6 +48,7 @@
Width="18" Width="18"
Height="18" Height="18"
IsChecked="{Binding ProfileElement.Enabled}" IsChecked="{Binding ProfileElement.Enabled}"
Command="{s:Action EnableToggled}"
VerticalAlignment="Center" Padding="-25"> VerticalAlignment="Center" Padding="-25">
<materialDesign:PackIcon Kind="Eye" Height="13" Width="13" /> <materialDesign:PackIcon Kind="Eye" Height="13" Width="13" />
</ToggleButton> </ToggleButton>

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -12,7 +13,7 @@ using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
{ {
public abstract class TreeItemViewModel : PropertyChangedBase public abstract class TreeItemViewModel : PropertyChangedBase, IDisposable
{ {
private readonly IDialogService _dialogService; private readonly IDialogService _dialogService;
private readonly IFolderVmFactory _folderVmFactory; private readonly IFolderVmFactory _folderVmFactory;
@ -38,16 +39,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
ProfileElement = profileElement; ProfileElement = profileElement;
Children = new BindableCollection<TreeItemViewModel>(); Children = new BindableCollection<TreeItemViewModel>();
ProfileElement.PropertyChanged += HandleProfileElementEnabledChanged;
UpdateProfileElements(); UpdateProfileElements();
} }
private void HandleProfileElementEnabledChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ProfileElement.Enabled))
_profileEditorService.UpdateSelectedProfile();
}
public TreeItemViewModel Parent { get; set; } public TreeItemViewModel Parent { get; set; }
public ProfileElement ProfileElement { get; set; } public ProfileElement ProfileElement { get; set; }
@ -208,5 +202,23 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
foreach (var treeItemViewModel in newChildren) foreach (var treeItemViewModel in newChildren)
treeItemViewModel.UpdateProfileElements(); treeItemViewModel.UpdateProfileElements();
} }
public void EnableToggled()
{
_profileEditorService.UpdateSelectedProfile();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
} }

View File

@ -1,10 +1,24 @@
using Stylet; using System;
using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
{ {
public class CanvasViewModel : PropertyChangedBase public abstract class CanvasViewModel : PropertyChangedBase, IDisposable
{ {
public double X { get; set; } public double X { get; set; }
public double Y { get; set; } public double Y { get; set; }
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
} }

View File

@ -44,14 +44,19 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
public Thickness LayerPosition => new Thickness(ViewportRectangle.Left, ViewportRectangle.Top, 0, 0); public Thickness LayerPosition => new Thickness(ViewportRectangle.Left, ViewportRectangle.Top, 0, 0);
public bool IsSelected { get; set; } public bool IsSelected { get; set; }
public void Dispose() protected override void Dispose(bool disposing)
{ {
Layer.RenderPropertiesUpdated -= LayerOnRenderPropertiesUpdated; if (disposing)
_profileEditorService.ProfileElementSelected -= OnProfileElementSelected; {
_profileEditorService.SelectedProfileElementUpdated -= OnSelectedProfileElementUpdated; Layer.RenderPropertiesUpdated -= LayerOnRenderPropertiesUpdated;
_profileEditorService.ProfilePreviewUpdated -= ProfileEditorServiceOnProfilePreviewUpdated; _profileEditorService.ProfileElementSelected -= OnProfileElementSelected;
} _profileEditorService.SelectedProfileElementUpdated -= OnSelectedProfileElementUpdated;
_profileEditorService.ProfilePreviewUpdated -= ProfileEditorServiceOnProfilePreviewUpdated;
}
base.Dispose(disposing);
}
private void Update() private void Update()
{ {
IsSelected = _profileEditorService.SelectedProfileElement == Layer; IsSelected = _profileEditorService.SelectedProfileElement == Layer;

View File

@ -60,7 +60,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
}); });
ApplySurfaceConfiguration(_surfaceService.ActiveSurface); ApplySurfaceConfiguration(_surfaceService.ActiveSurface);
ApplyActiveProfile();
ActivateToolByIndex(0); ActivateToolByIndex(0);
eventAggregator.Subscribe(this); eventAggregator.Subscribe(this);
@ -151,10 +150,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
_profileEditorService.ProfileSelected -= OnProfileSelected; _profileEditorService.ProfileSelected -= OnProfileSelected;
_profileEditorService.ProfileElementSelected -= OnProfileElementSelected; _profileEditorService.ProfileElementSelected -= OnProfileElementSelected;
_profileEditorService.SelectedProfileElementUpdated -= OnSelectedProfileElementUpdated; _profileEditorService.SelectedProfileElementUpdated -= OnSelectedProfileElementUpdated;
if (_previousSelectedLayer != null)
_previousSelectedLayer.LayerBrushUpdated -= SelectedLayerOnLayerBrushUpdated;
OnlyShowSelectedShape.Save(); OnlyShowSelectedShape.Save();
HighlightSelectedLayer.Save(); HighlightSelectedLayer.Save();
foreach (var canvasViewModel in CanvasViewModels)
canvasViewModel.Dispose();
CanvasViewModels.Clear();
base.OnClose(); base.OnClose();
} }

View File

@ -361,7 +361,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools
private SKPoint[] UnTransformPoints(SKPoint[] skPoints, Layer layer, SKPoint pivot, bool includeScale) private SKPoint[] UnTransformPoints(SKPoint[] skPoints, Layer layer, SKPoint pivot, bool includeScale)
{ {
var counterRotatePath = new SKPath(); using var counterRotatePath = new SKPath();
counterRotatePath.AddPoly(skPoints, false); counterRotatePath.AddPoly(skPoints, false);
counterRotatePath.Transform(SKMatrix.MakeRotationDegrees(layer.Transform.Rotation.CurrentValue * -1, pivot.X, pivot.Y)); counterRotatePath.Transform(SKMatrix.MakeRotationDegrees(layer.Transform.Rotation.CurrentValue * -1, pivot.X, pivot.Y));
if (includeScale) if (includeScale)

View File

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel;
using Artemis.Core.Plugins.LayerBrush.Abstract; using Artemis.Core.Plugins.LayerBrush.Abstract;
using SkiaSharp; using SkiaSharp;
@ -13,14 +14,19 @@ namespace Artemis.Plugins.LayerBrushes.Color
public override void EnableLayerBrush() public override void EnableLayerBrush()
{ {
Layer.RenderPropertiesUpdated += (sender, args) => CreateShader(); Layer.RenderPropertiesUpdated += HandleShaderChange;
Properties.GradientType.BaseValueChanged += (sender, args) => CreateShader(); Properties.GradientType.BaseValueChanged += HandleShaderChange;
Properties.Color.BaseValueChanged += (sender, args) => CreateShader(); Properties.Color.BaseValueChanged += HandleShaderChange;
Properties.Gradient.BaseValue.PropertyChanged += (sender, args) => CreateShader(); Properties.Gradient.BaseValue.PropertyChanged += BaseValueOnPropertyChanged;
} }
public override void DisableLayerBrush() public override void DisableLayerBrush()
{ {
Layer.RenderPropertiesUpdated -= HandleShaderChange;
Properties.GradientType.BaseValueChanged -= HandleShaderChange;
Properties.Color.BaseValueChanged -= HandleShaderChange;
Properties.Gradient.BaseValue.PropertyChanged -= BaseValueOnPropertyChanged;
_paint?.Dispose(); _paint?.Dispose();
_shader?.Dispose(); _shader?.Dispose();
_paint = null; _paint = null;
@ -50,6 +56,16 @@ namespace Artemis.Plugins.LayerBrushes.Color
canvas.DrawPath(path, paint); canvas.DrawPath(path, paint);
} }
private void BaseValueOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
CreateShader();
}
private void HandleShaderChange(object? sender, EventArgs e)
{
CreateShader();
}
private void CreateShader() private void CreateShader()
{ {
var center = new SKPoint(_shaderBounds.MidX, _shaderBounds.MidY); var center = new SKPoint(_shaderBounds.MidX, _shaderBounds.MidY);