diff --git a/src/Artemis.Core/Extensions/RgbDeviceExtensions.cs b/src/Artemis.Core/Extensions/RgbDeviceExtensions.cs
index e4a637e37..16ffad995 100644
--- a/src/Artemis.Core/Extensions/RgbDeviceExtensions.cs
+++ b/src/Artemis.Core/Extensions/RgbDeviceExtensions.cs
@@ -1,4 +1,5 @@
-using RGB.NET.Core;
+using System.Text;
+using RGB.NET.Core;
namespace Artemis.Core.Extensions
{
@@ -6,11 +7,17 @@ namespace Artemis.Core.Extensions
{
public static string GetDeviceIdentifier(this IRGBDevice rgbDevice)
{
- return rgbDevice.DeviceInfo.DeviceName +
- "-" + rgbDevice.DeviceInfo.Manufacturer +
- "-" + rgbDevice.DeviceInfo.Model +
- "-" + rgbDevice.DeviceInfo.DeviceType +
- "-" + rgbDevice.DeviceInfo.Lighting;
+ var builder = new StringBuilder();
+ builder.Append(rgbDevice.DeviceInfo.DeviceName);
+ builder.Append('-');
+ builder.Append(rgbDevice.DeviceInfo.Manufacturer);
+ builder.Append('-');
+ builder.Append(rgbDevice.DeviceInfo.Model);
+ builder.Append('-');
+ builder.Append(rgbDevice.DeviceInfo.DeviceType);
+ builder.Append('-');
+ builder.Append(rgbDevice.DeviceInfo.Lighting);
+ return builder.ToString();
}
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs
index 0a3ff7fb8..1ccb64cbc 100644
--- a/src/Artemis.Core/Models/Profile/Folder.cs
+++ b/src/Artemis.Core/Models/Profile/Folder.cs
@@ -80,7 +80,7 @@ namespace Artemis.Core.Models.Profile
{
if (!Enabled || Path == null || !Children.Any(c => c.Enabled))
return;
-
+
if (_folderBitmap == null)
_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)
@@ -89,11 +89,11 @@ namespace Artemis.Core.Models.Profile
_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 folderPaint = new SKPaint();
folderCanvas.Clear();
- var folderPath = new SKPath(Path);
folderPath.Transform(SKMatrix.MakeTranslation(folderPath.Bounds.Left * -1, folderPath.Bounds.Top * -1));
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
@@ -112,7 +112,7 @@ namespace Artemis.Core.Models.Profile
canvas.Save();
- var clipPath = new SKPath(folderPath);
+ using var clipPath = new SKPath(folderPath);
clipPath.Transform(SKMatrix.MakeTranslation(targetLocation.X, targetLocation.Y));
canvas.ClipPath(clipPath);
diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index f05e0130f..ca73d9123 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -246,6 +246,7 @@ namespace Artemis.Core.Models.Profile
_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 layerPaint = new SKPaint
{
@@ -254,8 +255,7 @@ namespace Artemis.Core.Models.Profile
Color = new SKColor(0, 0, 0, (byte) (Transform.Opacity.CurrentValue * 2.55f))
};
layerCanvas.Clear();
-
- var layerPath = new SKPath(Path);
+
layerPath.Transform(SKMatrix.MakeTranslation(layerPath.Bounds.Left * -1, layerPath.Bounds.Top * -1));
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;
canvas.DrawBitmap(_layerBitmap, targetLocation, layerPaint);
+
}
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)
@@ -302,8 +304,9 @@ namespace Artemis.Core.Models.Profile
canvas.RotateDegrees(rotationProperty, anchorPosition.X, anchorPosition.Y);
canvas.Scale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, anchorPosition.X, anchorPosition.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)
@@ -319,7 +322,7 @@ namespace Artemis.Core.Models.Profile
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 clipPath = new SKPath(LayerShape.Path);
+ using var clipPath = new SKPath(LayerShape.Path);
clipPath.Transform(SKMatrix.MakeTranslation(x, y));
clipPath.Transform(SKMatrix.MakeScale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, 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.Bottom - y, Bounds.Bottom - y)
);
- var renderPath = new SKPath();
+ using var renderPath = new SKPath();
renderPath.AddRect(boundsRect);
LayerBrush.InternalRender(canvas, canvasInfo, renderPath, paint);
diff --git a/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs b/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs
index e7c878307..61b15eb95 100644
--- a/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs
+++ b/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs
@@ -4,8 +4,6 @@ namespace Artemis.Core.Models.Profile.LayerShapes
{
public abstract class LayerShape
{
- private SKPath _path;
-
protected LayerShape(Layer layer)
{
Layer = layer;
@@ -17,13 +15,9 @@ namespace Artemis.Core.Models.Profile.LayerShapes
public Layer Layer { get; set; }
///
- /// Gets a copy of the path outlining the shape
+ /// Gets a the path outlining the shape
///
- public SKPath Path
- {
- get => _path != null ? new SKPath(_path) : null;
- protected set => _path = value;
- }
+ public SKPath Path { get; protected set; }
public abstract void CalculateRenderProperties();
}
diff --git a/src/Artemis.Core/Models/Profile/PropertiesProfileElement.cs b/src/Artemis.Core/Models/Profile/PropertiesProfileElement.cs
index fbbaa5b7b..ae1371424 100644
--- a/src/Artemis.Core/Models/Profile/PropertiesProfileElement.cs
+++ b/src/Artemis.Core/Models/Profile/PropertiesProfileElement.cs
@@ -11,12 +11,12 @@ namespace Artemis.Core.Models.Profile
private SKPath _path;
///
- /// 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.
///
public SKPath Path
{
- get => _path != null ? new SKPath(_path) : null;
+ get => _path;
protected set
{
_path = value;
diff --git a/src/Artemis.Core/RGB.NET/BitmapBrush.cs b/src/Artemis.Core/RGB.NET/BitmapBrush.cs
index fc1e82f96..eaf01451a 100644
--- a/src/Artemis.Core/RGB.NET/BitmapBrush.cs
+++ b/src/Artemis.Core/RGB.NET/BitmapBrush.cs
@@ -98,7 +98,7 @@ namespace Artemis.Core.RGB.NET
var bitmapWidth = Bitmap.Width;
var bitmapHeight = Bitmap.Height;
- var pixmap = Bitmap.PeekPixels();
+ using var pixmap = Bitmap.PeekPixels();
foreach (var renderTarget in renderTargets)
{
// SKRect has all the good stuff we need
diff --git a/src/Artemis.Core/RGB.NET/GraphicsDecorator.cs b/src/Artemis.Core/RGB.NET/GraphicsDecorator.cs
deleted file mode 100644
index 3cf61959b..000000000
--- a/src/Artemis.Core/RGB.NET/GraphicsDecorator.cs
+++ /dev/null
@@ -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);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.UI/Behaviors/InputBindingBehavior.cs b/src/Artemis.UI/Behaviors/InputBindingBehavior.cs
index bd9c7f996..81897d101 100644
--- a/src/Artemis.UI/Behaviors/InputBindingBehavior.cs
+++ b/src/Artemis.UI/Behaviors/InputBindingBehavior.cs
@@ -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
{
public class InputBindingBehavior
{
+ private static List> _movedInputBindings = new List>();
+
public static readonly DependencyProperty PropagateInputBindingsToWindowProperty =
DependencyProperty.RegisterAttached("PropagateInputBindingsToWindow", typeof(bool), typeof(InputBindingBehavior),
new PropertyMetadata(false, OnPropagateInputBindingsToWindowChanged));
@@ -21,13 +27,13 @@ namespace Artemis.UI.Behaviors
private static void OnPropagateInputBindingsToWindowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((FrameworkElement) d).Loaded += OnLoaded;
+ ((FrameworkElement) d).Unloaded += OnUnloaded;
}
private static void OnLoaded(object sender, RoutedEventArgs e)
{
var frameworkElement = (FrameworkElement) sender;
- frameworkElement.Loaded -= OnLoaded;
-
+
var window = Window.GetWindow(frameworkElement);
if (window == null) return;
@@ -37,7 +43,23 @@ namespace Artemis.UI.Behaviors
var inputBinding = frameworkElement.InputBindings[i];
window.InputBindings.Add(inputBinding);
frameworkElement.InputBindings.Remove(inputBinding);
+
+ _movedInputBindings.Add(new Tuple(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();
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
index d0ccdaa58..306f53bad 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
@@ -86,9 +86,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
ProfileEditorService.CurrentTimeChanged -= ProfileEditorServiceOnCurrentTimeChanged;
ProfileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
+ PopulateProperties(null);
base.OnClose();
}
+ protected override void OnActivate()
+ {
+ PopulateProperties(ProfileEditorService.SelectedProfileElement as PropertiesProfileElement);
+ base.OnActivate();
+ }
+
protected override void OnDeactivate()
{
Pause();
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs
index 043427405..0b821cefb 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs
@@ -1,7 +1,6 @@
using System;
using System.Linq;
using System.Windows;
-using System.Windows.Input;
using Artemis.Core.Models.Profile.LayerProperties;
using Artemis.Core.Utilities;
using Artemis.UI.Shared.Services.Interfaces;
@@ -13,8 +12,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
{
private readonly IProfileEditorService _profileEditorService;
- public TimelineKeyframeViewModel(IProfileEditorService profileEditorService, TimelineViewModel timelineViewModel, LayerPropertyKeyframe layerPropertyKeyframe)
- : base(profileEditorService, timelineViewModel, layerPropertyKeyframe)
+ public TimelineKeyframeViewModel(IProfileEditorService profileEditorService, LayerPropertyKeyframe layerPropertyKeyframe)
+ : base(profileEditorService, layerPropertyKeyframe)
{
_profileEditorService = profileEditorService;
LayerPropertyKeyframe = layerPropertyKeyframe;
@@ -48,13 +47,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
public abstract class TimelineKeyframeViewModel : PropertyChangedBase
{
private readonly IProfileEditorService _profileEditorService;
- private readonly TimelineViewModel _timelineViewModel;
private int _pixelsPerSecond;
- protected TimelineKeyframeViewModel(IProfileEditorService profileEditorService, TimelineViewModel timelineViewModel, BaseLayerPropertyKeyframe baseLayerPropertyKeyframe)
+ protected TimelineKeyframeViewModel(IProfileEditorService profileEditorService, BaseLayerPropertyKeyframe baseLayerPropertyKeyframe)
{
_profileEditorService = profileEditorService;
- _timelineViewModel = timelineViewModel;
BaseLayerPropertyKeyframe = baseLayerPropertyKeyframe;
EasingViewModels = new BindableCollection();
}
@@ -78,64 +75,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
#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
#region Easing
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupViewModel.cs
index 69e8c5bc9..991f5a9e3 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupViewModel.cs
@@ -6,7 +6,7 @@ using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
{
- public class TimelinePropertyGroupViewModel
+ public class TimelinePropertyGroupViewModel : PropertyChangedBase
{
public TimelinePropertyGroupViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel)
{
@@ -20,7 +20,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
public LayerPropertyGroupViewModel LayerPropertyGroupViewModel { get; }
public BindableCollection TimelineKeyframeViewModels { get; set; }
- public TimelineViewModel TimelineViewModel { get; set; }
public void UpdateKeyframes()
{
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyView.xaml
index e4e8d112e..350b4c634 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyView.xaml
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyView.xaml
@@ -33,7 +33,7 @@
Height="10"
Margin="-5,6,0,0"
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}"
MouseUp="{s:Action KeyframeMouseUp}"
MouseMove="{s:Action KeyframeMouseMove}"
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyViewModel.cs
index a36a04d1c..afe080138 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyViewModel.cs
@@ -1,6 +1,5 @@
using System;
using System.Linq;
-using Artemis.UI.Exceptions;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract;
using Artemis.UI.Shared.Services.Interfaces;
using Stylet;
@@ -26,9 +25,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
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
if (LayerPropertyViewModel.LayerProperty.KeyframesEnabled)
{
@@ -37,7 +33,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
TimelineKeyframeViewModels.RemoveRange(toRemove);
TimelineKeyframeViewModels.AddRange(
keyframes.Where(k => TimelineKeyframeViewModels.All(t => t.BaseLayerPropertyKeyframe != k))
- .Select(k => new TimelineKeyframeViewModel(_profileEditorService, TimelineViewModel, k))
+ .Select(k => new TimelineKeyframeViewModel(_profileEditorService, k))
);
}
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)
{
@@ -76,7 +72,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
}
public LayerPropertyBaseViewModel LayerPropertyBaseViewModel { get; }
- public TimelineViewModel TimelineViewModel { get; set; }
public BindableCollection TimelineKeyframeViewModels { get; set; }
public abstract void Dispose();
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineViewModel.cs
index 91288fed7..959a10457 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineViewModel.cs
@@ -4,19 +4,24 @@ using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
+using System.Windows.Shapes;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract;
+using Artemis.UI.Shared.Services.Interfaces;
using Artemis.UI.Shared.Utilities;
using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
{
- public class TimelineViewModel : PropertyChangedBase
+ public class TimelineViewModel : Screen
{
private readonly LayerPropertiesViewModel _layerPropertiesViewModel;
+ private readonly IProfileEditorService _profileEditorService;
- public TimelineViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection layerPropertyGroups)
+ public TimelineViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection layerPropertyGroups,
+ IProfileEditorService profileEditorService)
{
_layerPropertiesViewModel = layerPropertiesViewModel;
+ _profileEditorService = profileEditorService;
LayerPropertyGroups = layerPropertyGroups;
SelectionRectangle = new RectangleGeometry();
@@ -32,20 +37,82 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
{
foreach (var layerPropertyGroupViewModel in LayerPropertyGroups)
{
- layerPropertyGroupViewModel.TimelinePropertyGroupViewModel.TimelineViewModel = this;
layerPropertyGroupViewModel.TimelinePropertyGroupViewModel.UpdateKeyframes();
foreach (var layerPropertyBaseViewModel in layerPropertyGroupViewModel.GetAllChildren())
{
if (layerPropertyBaseViewModel is LayerPropertyViewModel layerPropertyViewModel)
- {
- layerPropertyViewModel.TimelinePropertyBaseViewModel.TimelineViewModel = this;
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
public void MoveSelectedKeyframes(TimeSpan cursorTime)
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyGroupViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyGroupViewModel.cs
index e47b45076..8aba6b8f6 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyGroupViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyGroupViewModel.cs
@@ -4,10 +4,11 @@ using Artemis.Core.Services.Interfaces;
using Artemis.UI.Screens.Module.ProfileEditor.Dialogs;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract;
using Artemis.UI.Shared.Services.Interfaces;
+using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree
{
- public class TreePropertyGroupViewModel
+ public class TreePropertyGroupViewModel : PropertyChangedBase
{
private readonly IDialogService _dialogService;
private readonly ILayerService _layerService;
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs
index 4162a9a51..b786a992a 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs
@@ -5,6 +5,7 @@ using Artemis.Core.Utilities;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract;
using Artemis.UI.Shared.PropertyInput;
using Artemis.UI.Shared.Services.Interfaces;
+using Stylet;
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)
{
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs
index 272530ad6..e2a4c1145 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs
@@ -135,6 +135,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree
}
}
+ protected override void OnClose()
+ {
+ RootFolder?.Dispose();
+ RootFolder = null;
+ base.OnClose();
+ }
+
#region Event handlers
private void OnProfileElementSelected(object sender, EventArgs e)
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/FolderView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/FolderView.xaml
index f094176b4..35ecdc2ef 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/FolderView.xaml
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/FolderView.xaml
@@ -55,6 +55,7 @@
Width="18"
Height="18"
IsChecked="{Binding ProfileElement.Enabled}"
+ Command="{s:Action EnableToggled}"
VerticalAlignment="Center" Padding="-25">
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/FolderViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/FolderViewModel.cs
index cf4722c41..4dd168414 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/FolderViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/FolderViewModel.cs
@@ -7,6 +7,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
{
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
public FolderViewModel(ProfileElement folder,
IProfileEditorService profileEditorService,
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/LayerView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/LayerView.xaml
index b0ade2a89..1cb6d3db4 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/LayerView.xaml
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/LayerView.xaml
@@ -48,6 +48,7 @@
Width="18"
Height="18"
IsChecked="{Binding ProfileElement.Enabled}"
+ Command="{s:Action EnableToggled}"
VerticalAlignment="Center" Padding="-25">
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/TreeItemViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/TreeItemViewModel.cs
index 863a7e2da..c79e31070 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/TreeItemViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/TreeItem/TreeItemViewModel.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
@@ -12,7 +13,7 @@ using Stylet;
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 IFolderVmFactory _folderVmFactory;
@@ -38,16 +39,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
ProfileElement = profileElement;
Children = new BindableCollection();
- ProfileElement.PropertyChanged += HandleProfileElementEnabledChanged;
UpdateProfileElements();
}
- private void HandleProfileElementEnabledChanged(object sender, PropertyChangedEventArgs e)
- {
- if (e.PropertyName == nameof(ProfileElement.Enabled))
- _profileEditorService.UpdateSelectedProfile();
- }
-
public TreeItemViewModel Parent { get; set; }
public ProfileElement ProfileElement { get; set; }
@@ -208,5 +202,23 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem
foreach (var treeItemViewModel in newChildren)
treeItemViewModel.UpdateProfileElements();
}
+
+ public void EnableToggled()
+ {
+ _profileEditorService.UpdateSelectedProfile();
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/CanvasViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/CanvasViewModel.cs
index 6cdf3689b..85a4686f6 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/CanvasViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/CanvasViewModel.cs
@@ -1,10 +1,24 @@
-using Stylet;
+using System;
+using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
{
- public class CanvasViewModel : PropertyChangedBase
+ public abstract class CanvasViewModel : PropertyChangedBase, IDisposable
{
public double X { get; set; }
public double Y { get; set; }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs
index 652cf7a62..0176879ac 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs
@@ -44,14 +44,19 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
public Thickness LayerPosition => new Thickness(ViewportRectangle.Left, ViewportRectangle.Top, 0, 0);
public bool IsSelected { get; set; }
- public void Dispose()
+ protected override void Dispose(bool disposing)
{
- Layer.RenderPropertiesUpdated -= LayerOnRenderPropertiesUpdated;
- _profileEditorService.ProfileElementSelected -= OnProfileElementSelected;
- _profileEditorService.SelectedProfileElementUpdated -= OnSelectedProfileElementUpdated;
- _profileEditorService.ProfilePreviewUpdated -= ProfileEditorServiceOnProfilePreviewUpdated;
- }
+ if (disposing)
+ {
+ Layer.RenderPropertiesUpdated -= LayerOnRenderPropertiesUpdated;
+ _profileEditorService.ProfileElementSelected -= OnProfileElementSelected;
+ _profileEditorService.SelectedProfileElementUpdated -= OnSelectedProfileElementUpdated;
+ _profileEditorService.ProfilePreviewUpdated -= ProfileEditorServiceOnProfilePreviewUpdated;
+ }
+ base.Dispose(disposing);
+ }
+
private void Update()
{
IsSelected = _profileEditorService.SelectedProfileElement == Layer;
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileViewModel.cs
index b3112afbc..64fc12dd7 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileViewModel.cs
@@ -60,7 +60,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
});
ApplySurfaceConfiguration(_surfaceService.ActiveSurface);
- ApplyActiveProfile();
ActivateToolByIndex(0);
eventAggregator.Subscribe(this);
@@ -151,10 +150,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
_profileEditorService.ProfileSelected -= OnProfileSelected;
_profileEditorService.ProfileElementSelected -= OnProfileElementSelected;
_profileEditorService.SelectedProfileElementUpdated -= OnSelectedProfileElementUpdated;
+ if (_previousSelectedLayer != null)
+ _previousSelectedLayer.LayerBrushUpdated -= SelectedLayerOnLayerBrushUpdated;
OnlyShowSelectedShape.Save();
HighlightSelectedLayer.Save();
+ foreach (var canvasViewModel in CanvasViewModels)
+ canvasViewModel.Dispose();
+ CanvasViewModels.Clear();
+
base.OnClose();
}
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs
index 0d905f5c2..9da3a8a34 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs
@@ -361,7 +361,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools
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.Transform(SKMatrix.MakeRotationDegrees(layer.Transform.Rotation.CurrentValue * -1, pivot.X, pivot.Y));
if (includeScale)
diff --git a/src/Plugins/Artemis.Plugins.LayerBrushes.Color/ColorBrush.cs b/src/Plugins/Artemis.Plugins.LayerBrushes.Color/ColorBrush.cs
index f6e6e9b14..43338f7cd 100644
--- a/src/Plugins/Artemis.Plugins.LayerBrushes.Color/ColorBrush.cs
+++ b/src/Plugins/Artemis.Plugins.LayerBrushes.Color/ColorBrush.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using Artemis.Core.Plugins.LayerBrush.Abstract;
using SkiaSharp;
@@ -13,14 +14,19 @@ namespace Artemis.Plugins.LayerBrushes.Color
public override void EnableLayerBrush()
{
- Layer.RenderPropertiesUpdated += (sender, args) => CreateShader();
- Properties.GradientType.BaseValueChanged += (sender, args) => CreateShader();
- Properties.Color.BaseValueChanged += (sender, args) => CreateShader();
- Properties.Gradient.BaseValue.PropertyChanged += (sender, args) => CreateShader();
+ Layer.RenderPropertiesUpdated += HandleShaderChange;
+ Properties.GradientType.BaseValueChanged += HandleShaderChange;
+ Properties.Color.BaseValueChanged += HandleShaderChange;
+ Properties.Gradient.BaseValue.PropertyChanged += BaseValueOnPropertyChanged;
}
public override void DisableLayerBrush()
{
+ Layer.RenderPropertiesUpdated -= HandleShaderChange;
+ Properties.GradientType.BaseValueChanged -= HandleShaderChange;
+ Properties.Color.BaseValueChanged -= HandleShaderChange;
+ Properties.Gradient.BaseValue.PropertyChanged -= BaseValueOnPropertyChanged;
+
_paint?.Dispose();
_shader?.Dispose();
_paint = null;
@@ -50,6 +56,16 @@ namespace Artemis.Plugins.LayerBrushes.Color
canvas.DrawPath(path, paint);
}
+ private void BaseValueOnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ CreateShader();
+ }
+
+ private void HandleShaderChange(object? sender, EventArgs e)
+ {
+ CreateShader();
+ }
+
private void CreateShader()
{
var center = new SKPoint(_shaderBounds.MidX, _shaderBounds.MidY);