diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerPropertyKeyframe.cs b/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerPropertyKeyframe.cs
index b4adf932f..d0537d8b1 100644
--- a/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerPropertyKeyframe.cs
+++ b/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerPropertyKeyframe.cs
@@ -8,10 +8,16 @@ namespace Artemis.Core.Models.Profile.LayerProperties
///
public abstract class BaseLayerPropertyKeyframe
{
- internal BaseLayerPropertyKeyframe()
+ internal BaseLayerPropertyKeyframe(BaseLayerProperty baseLayerProperty)
{
+ BaseLayerProperty = baseLayerProperty;
}
+ ///
+ /// The base class of the layer property this keyframe is applied to
+ ///
+ public BaseLayerProperty BaseLayerProperty { get; internal set; }
+
///
/// The position of this keyframe in the timeline
///
@@ -20,8 +26,6 @@ namespace Artemis.Core.Models.Profile.LayerProperties
///
/// The easing function applied on the value of the keyframe
///
- public abstract Easings.Functions EasingFunction { get; set; }
-
- internal abstract BaseLayerProperty BaseLayerProperty { get; }
+ public Easings.Functions EasingFunction { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
index c33897687..0b284a23c 100644
--- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
+++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
@@ -90,7 +90,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties
var currentKeyframe = Keyframes.FirstOrDefault(k => k.Position == time.Value);
// Create a new keyframe if none found
if (currentKeyframe == null)
- AddKeyframe(new LayerPropertyKeyframe(value, time.Value, Easings.Functions.Linear));
+ AddKeyframe(new LayerPropertyKeyframe(value, time.Value, Easings.Functions.Linear, this));
else
currentKeyframe.Value = value;
@@ -105,20 +105,47 @@ namespace Artemis.Core.Models.Profile.LayerProperties
/// The keyframe to add
public void AddKeyframe(LayerPropertyKeyframe keyframe)
{
+ if (_keyframes.Contains(keyframe))
+ return;
+
+ keyframe.LayerProperty?.RemoveKeyframe(keyframe);
+
keyframe.LayerProperty = this;
+ keyframe.BaseLayerProperty = this;
_keyframes.Add(keyframe);
SortKeyframes();
OnKeyframeAdded();
}
+ ///
+ /// Removes a keyframe from the layer property
+ ///
+ /// The keyframe to remove
+ public LayerPropertyKeyframe CopyKeyframe(LayerPropertyKeyframe keyframe)
+ {
+ var newKeyframe = new LayerPropertyKeyframe(
+ keyframe.Value,
+ keyframe.Position,
+ keyframe.EasingFunction,
+ keyframe.LayerProperty
+ );
+ AddKeyframe(newKeyframe);
+
+ return newKeyframe;
+ }
+
///
/// Removes a keyframe from the layer property
///
/// The keyframe to remove
public void RemoveKeyframe(LayerPropertyKeyframe keyframe)
{
+ if (!_keyframes.Contains(keyframe))
+ return;
+
_keyframes.Remove(keyframe);
keyframe.LayerProperty = null;
+ keyframe.BaseLayerProperty = null;
SortKeyframes();
OnKeyframeRemoved();
}
@@ -213,8 +240,9 @@ namespace Artemis.Core.Models.Profile.LayerProperties
_keyframes.AddRange(entity.KeyframeEntities.Select(k => new LayerPropertyKeyframe(
JsonConvert.DeserializeObject(k.Value),
k.Position,
- (Easings.Functions) k.EasingFunction)
- ));
+ (Easings.Functions) k.EasingFunction,
+ this
+ )));
}
catch (JsonException e)
{
diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerPropertyKeyFrame.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerPropertyKeyFrame.cs
index 7cb1ffcb0..af37209aa 100644
--- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerPropertyKeyFrame.cs
+++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerPropertyKeyFrame.cs
@@ -7,10 +7,11 @@ namespace Artemis.Core.Models.Profile.LayerProperties
{
private TimeSpan _position;
- public LayerPropertyKeyframe(T value, TimeSpan position, Easings.Functions easingFunction)
+ public LayerPropertyKeyframe(T value, TimeSpan position, Easings.Functions easingFunction, LayerProperty layerProperty) : base(layerProperty)
{
_position = position;
Value = value;
+ LayerProperty = layerProperty;
EasingFunction = easingFunction;
}
@@ -34,10 +35,5 @@ namespace Artemis.Core.Models.Profile.LayerProperties
LayerProperty.SortKeyframes();
}
}
-
- ///
- public sealed override Easings.Functions EasingFunction { get; set; }
-
- internal override BaseLayerProperty BaseLayerProperty => LayerProperty;
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Screens/Dialogs/ExceptionDialogView.xaml b/src/Artemis.UI.Shared/Screens/Dialogs/ExceptionDialogView.xaml
index 09e40501e..4a4ad281d 100644
--- a/src/Artemis.UI.Shared/Screens/Dialogs/ExceptionDialogView.xaml
+++ b/src/Artemis.UI.Shared/Screens/Dialogs/ExceptionDialogView.xaml
@@ -10,29 +10,38 @@
mc:Ignorable="d"
d:DesignHeight="163.274" d:DesignWidth="254.425"
d:DataContext="{d:DesignInstance dialogs:ExceptionDialogViewModel}">
-
-
-
+
+
- Exception message
-
-
-
+
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Screens/Dialogs/ExceptionDialogViewModel.cs b/src/Artemis.UI.Shared/Screens/Dialogs/ExceptionDialogViewModel.cs
index 9c4fc4057..7818f2aa3 100644
--- a/src/Artemis.UI.Shared/Screens/Dialogs/ExceptionDialogViewModel.cs
+++ b/src/Artemis.UI.Shared/Screens/Dialogs/ExceptionDialogViewModel.cs
@@ -1,6 +1,6 @@
using System;
+using System.Collections.Generic;
using Artemis.UI.Shared.Services.Dialog;
-using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Document;
namespace Artemis.UI.Shared.Screens.Dialogs
@@ -10,18 +10,35 @@ namespace Artemis.UI.Shared.Screens.Dialogs
public ExceptionDialogViewModel(string message, Exception exception)
{
Header = message;
- Exception = exception;
- Document = new TextDocument(new StringTextSource(exception.StackTrace));
+ Exceptions = new List();
+
+ var currentException = exception;
+ while (currentException != null)
+ {
+ Exceptions.Add(new DialogException(currentException));
+ currentException = currentException.InnerException;
+ }
}
public string Header { get; }
- public Exception Exception { get; }
+ public List Exceptions { get; set; }
- public IDocument Document { get; set; }
public void Close()
{
Session.Close();
}
}
+
+ public class DialogException
+ {
+ public Exception Exception { get; }
+ public IDocument Document { get; set; }
+
+ public DialogException(Exception exception)
+ {
+ Exception = exception;
+ Document = new TextDocument(new StringTextSource($"{exception.Message}\r\n\r\n{exception.StackTrace}"));
+ }
+ }
}
\ 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 e5137d02a..466f08679 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
@@ -20,38 +20,27 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
{
public class LayerPropertiesViewModel : ProfileEditorPanelViewModel
{
- private readonly ICoreService _coreService;
- private readonly IProfileEditorService _profileEditorService;
- private readonly ISettingsService _settingsService;
-
public LayerPropertiesViewModel(IProfileEditorService profileEditorService, ICoreService coreService, ISettingsService settingsService)
{
- _profileEditorService = profileEditorService;
- _coreService = coreService;
- _settingsService = settingsService;
+ ProfileEditorService = profileEditorService;
+ CoreService = coreService;
+ SettingsService = settingsService;
- PixelsPerSecond = 31;
LayerPropertyGroups = new BindableCollection();
}
+ public IProfileEditorService ProfileEditorService { get; }
+ public ICoreService CoreService { get; }
+ public ISettingsService SettingsService { get; }
+
public bool Playing { get; set; }
public bool RepeatAfterLastKeyframe { get; set; }
- public string FormattedCurrentTime => $"{Math.Floor(_profileEditorService.CurrentTime.TotalSeconds):00}.{_profileEditorService.CurrentTime.Milliseconds:000}";
-
- public int PixelsPerSecond
- {
- get => _pixelsPerSecond;
- set
- {
- _pixelsPerSecond = value;
- OnPixelsPerSecondChanged();
- }
- }
+ public string FormattedCurrentTime => $"{Math.Floor(ProfileEditorService.CurrentTime.TotalSeconds):00}.{ProfileEditorService.CurrentTime.Milliseconds:000}";
public Thickness TimeCaretPosition
{
- get => new Thickness(_profileEditorService.CurrentTime.TotalSeconds * PixelsPerSecond, 0, 0, 0);
- set => _profileEditorService.CurrentTime = TimeSpan.FromSeconds(value.Left / PixelsPerSecond);
+ get => new Thickness(ProfileEditorService.CurrentTime.TotalSeconds * ProfileEditorService.PixelsPerSecond, 0, 0, 0);
+ set => ProfileEditorService.CurrentTime = TimeSpan.FromSeconds(value.Left / ProfileEditorService.PixelsPerSecond);
}
public BindableCollection LayerPropertyGroups { get; set; }
@@ -60,18 +49,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
protected override void OnInitialActivate()
{
- PopulateProperties(_profileEditorService.SelectedProfileElement);
+ PopulateProperties(ProfileEditorService.SelectedProfileElement);
- _profileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
- _profileEditorService.CurrentTimeChanged += ProfileEditorServiceOnCurrentTimeChanged;
+ ProfileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
+ ProfileEditorService.CurrentTimeChanged += ProfileEditorServiceOnCurrentTimeChanged;
base.OnInitialActivate();
}
protected override void OnClose()
{
- _profileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
- _profileEditorService.CurrentTimeChanged -= ProfileEditorServiceOnCurrentTimeChanged;
+ ProfileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
+ ProfileEditorService.CurrentTimeChanged -= ProfileEditorServiceOnCurrentTimeChanged;
base.OnClose();
}
@@ -109,23 +98,24 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
layer.GetType().GetProperty(nameof(layer.Transform)),
typeof(PropertyGroupDescriptionAttribute)
);
- LayerPropertyGroups.Add(new LayerPropertyGroupViewModel(_profileEditorService, layer.General, (PropertyGroupDescriptionAttribute) generalAttribute));
- LayerPropertyGroups.Add(new LayerPropertyGroupViewModel(_profileEditorService, layer.Transform, (PropertyGroupDescriptionAttribute) transformAttribute));
+ LayerPropertyGroups.Add(new LayerPropertyGroupViewModel(ProfileEditorService, layer.General, (PropertyGroupDescriptionAttribute) generalAttribute));
+ LayerPropertyGroups.Add(new LayerPropertyGroupViewModel(ProfileEditorService, layer.Transform, (PropertyGroupDescriptionAttribute) transformAttribute));
- if (layer.LayerBrush == null)
- return;
-
- // Add the rout group of the brush
- // The root group of the brush has no attribute so let's pull one out of our sleeve
- var brushDescription = new PropertyGroupDescriptionAttribute
+ if (layer.LayerBrush != null)
{
- Name = layer.LayerBrush.Descriptor.DisplayName,
- Description = layer.LayerBrush.Descriptor.Description
- };
- LayerPropertyGroups.Add(new LayerPropertyGroupViewModel(_profileEditorService, layer.LayerBrush.BaseProperties, brushDescription));
+ // Add the rout group of the brush
+ // The root group of the brush has no attribute so let's pull one out of our sleeve
+ var brushDescription = new PropertyGroupDescriptionAttribute
+ {
+ Name = layer.LayerBrush.Descriptor.DisplayName,
+ Description = layer.LayerBrush.Descriptor.Description
+ };
+ LayerPropertyGroups.Add(new LayerPropertyGroupViewModel(ProfileEditorService, layer.LayerBrush.BaseProperties, brushDescription));
+ }
}
- TreeViewModel = new TreeViewModel(LayerPropertyGroups);
- TimelineViewModel = new TimelineViewModel(LayerPropertyGroups);
+
+ TreeViewModel = new TreeViewModel(this, LayerPropertyGroups);
+ TimelineViewModel = new TimelineViewModel(this, LayerPropertyGroups);
}
#endregion
@@ -135,7 +125,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
public void PlayFromStart()
{
if (!Playing)
- _profileEditorService.CurrentTime = TimeSpan.Zero;
+ ProfileEditorService.CurrentTime = TimeSpan.Zero;
Play();
}
@@ -150,7 +140,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
return;
}
- _coreService.FrameRendering += CoreServiceOnFrameRendering;
+ CoreService.FrameRendering += CoreServiceOnFrameRendering;
Playing = true;
}
@@ -159,39 +149,39 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
if (!Playing)
return;
- _coreService.FrameRendering -= CoreServiceOnFrameRendering;
+ CoreService.FrameRendering -= CoreServiceOnFrameRendering;
Playing = false;
}
public void GoToStart()
{
- _profileEditorService.CurrentTime = TimeSpan.Zero;
+ ProfileEditorService.CurrentTime = TimeSpan.Zero;
}
public void GoToEnd()
{
- _profileEditorService.CurrentTime = CalculateEndTime();
+ ProfileEditorService.CurrentTime = CalculateEndTime();
}
public void GoToPreviousFrame()
{
- var frameTime = 1000.0 / _settingsService.GetSetting("Core.TargetFrameRate", 25).Value;
- var newTime = Math.Max(0, Math.Round((_profileEditorService.CurrentTime.TotalMilliseconds - frameTime) / frameTime) * frameTime);
- _profileEditorService.CurrentTime = TimeSpan.FromMilliseconds(newTime);
+ var frameTime = 1000.0 / SettingsService.GetSetting("Core.TargetFrameRate", 25).Value;
+ var newTime = Math.Max(0, Math.Round((ProfileEditorService.CurrentTime.TotalMilliseconds - frameTime) / frameTime) * frameTime);
+ ProfileEditorService.CurrentTime = TimeSpan.FromMilliseconds(newTime);
}
public void GoToNextFrame()
{
- var frameTime = 1000.0 / _settingsService.GetSetting("Core.TargetFrameRate", 25).Value;
- var newTime = Math.Round((_profileEditorService.CurrentTime.TotalMilliseconds + frameTime) / frameTime) * frameTime;
+ var frameTime = 1000.0 / SettingsService.GetSetting("Core.TargetFrameRate", 25).Value;
+ var newTime = Math.Round((ProfileEditorService.CurrentTime.TotalMilliseconds + frameTime) / frameTime) * frameTime;
newTime = Math.Min(newTime, CalculateEndTime().TotalMilliseconds);
- _profileEditorService.CurrentTime = TimeSpan.FromMilliseconds(newTime);
+ ProfileEditorService.CurrentTime = TimeSpan.FromMilliseconds(newTime);
}
private TimeSpan CalculateEndTime()
{
- if (!(_profileEditorService.SelectedProfileElement is Layer layer))
+ if (!(ProfileEditorService.SelectedProfileElement is Layer layer))
return TimeSpan.MaxValue;
var keyframes = GetKeyframes(false);
@@ -207,7 +197,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
{
Execute.PostToUIThread(() =>
{
- var newTime = _profileEditorService.CurrentTime.Add(TimeSpan.FromSeconds(e.DeltaTime));
+ var newTime = ProfileEditorService.CurrentTime.Add(TimeSpan.FromSeconds(e.DeltaTime));
if (RepeatAfterLastKeyframe)
{
if (newTime > CalculateEndTime().Subtract(TimeSpan.FromSeconds(10)))
@@ -219,7 +209,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
Pause();
}
- _profileEditorService.CurrentTime = newTime;
+ ProfileEditorService.CurrentTime = newTime;
});
}
@@ -227,8 +217,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
#region Caret movement
- private int _pixelsPerSecond;
-
public void TimelineMouseDown(object sender, MouseButtonEventArgs e)
{
((IInputElement) sender).CaptureMouse();
@@ -246,28 +234,28 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
// Get the parent grid, need that for our position
var parent = (IInputElement) VisualTreeHelper.GetParent((DependencyObject) sender);
var x = Math.Max(0, e.GetPosition(parent).X);
- var newTime = TimeSpan.FromSeconds(x / PixelsPerSecond);
+ var newTime = TimeSpan.FromSeconds(x / ProfileEditorService.PixelsPerSecond);
// Round the time to something that fits the current zoom level
- if (PixelsPerSecond < 200)
+ if (ProfileEditorService.PixelsPerSecond < 200)
newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 5.0) * 5.0);
- else if (PixelsPerSecond < 500)
+ else if (ProfileEditorService.PixelsPerSecond < 500)
newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 2.0) * 2.0);
else
newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds));
if (!Keyboard.IsKeyDown(Key.LeftShift) && !Keyboard.IsKeyDown(Key.RightShift))
{
- _profileEditorService.CurrentTime = newTime;
+ ProfileEditorService.CurrentTime = newTime;
return;
}
var visibleKeyframes = GetKeyframes(true);
// Take a tolerance of 5 pixels (half a keyframe width)
- var tolerance = 1000f / PixelsPerSecond * 5;
+ var tolerance = 1000f / ProfileEditorService.PixelsPerSecond * 5;
var closeKeyframe = visibleKeyframes.FirstOrDefault(k => Math.Abs(k.Position.TotalMilliseconds - newTime.TotalMilliseconds) < tolerance);
- _profileEditorService.CurrentTime = closeKeyframe?.Position ?? newTime;
+ ProfileEditorService.CurrentTime = closeKeyframe?.Position ?? newTime;
}
}
@@ -281,16 +269,5 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
}
#endregion
-
- #region Events
-
- public event EventHandler PixelsPerSecondChanged;
-
- protected virtual void OnPixelsPerSecondChanged()
- {
- PixelsPerSecondChanged?.Invoke(this, EventArgs.Empty);
- }
-
- #endregion
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs
index aef93f5c0..8481ba003 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs
@@ -77,5 +77,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
foreach (var layerPropertyBaseViewModel in Children)
layerPropertyBaseViewModel.Dispose();
}
+
+ public List GetAllChildren()
+ {
+ var result = new List();
+ foreach (var layerPropertyBaseViewModel in Children)
+ {
+ result.Add(layerPropertyBaseViewModel);
+ if (layerPropertyBaseViewModel is LayerPropertyGroupViewModel layerPropertyGroupViewModel)
+ result.AddRange(layerPropertyGroupViewModel.GetAllChildren());
+ }
+
+ return result;
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyKeyframeViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyKeyframeViewModel.cs
deleted file mode 100644
index bb60f1d4f..000000000
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyKeyframeViewModel.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Artemis.Core.Models.Profile.LayerProperties;
-
-namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
-{
- public class LayerKeyframeViewModel
- {
- public LayerKeyframeViewModel(LayerPropertyKeyframe keyframe)
- {
- Keyframe = keyframe;
- }
-
- public LayerPropertyKeyframe Keyframe { get; }
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyViewModel.cs
index 7bd296e8a..fb70e24b1 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyViewModel.cs
@@ -19,7 +19,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
PropertyDescription = propertyDescription;
TreePropertyViewModel = ProfileEditorService.CreateTreePropertyViewModel(this);
- TimelinePropertyViewModel = new TimelinePropertyViewModel(this);
+ TimelinePropertyViewModel = new TimelinePropertyViewModel(this, profileEditorService);
TreePropertyBaseViewModel = TreePropertyViewModel;
TimelinePropertyBaseViewModel = TimelinePropertyViewModel;
@@ -50,7 +50,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
public override void Dispose()
{
TreePropertyViewModel.Dispose();
- TimelinePropertyViewModel.Dispose();
}
public void SetCurrentValue(T value, bool saveChanges)
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineEasingViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineEasingViewModel.cs
new file mode 100644
index 000000000..4103779ee
--- /dev/null
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineEasingViewModel.cs
@@ -0,0 +1,50 @@
+using System.Windows;
+using System.Windows.Media;
+using Artemis.Core.Utilities;
+using Humanizer;
+
+namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
+{
+ public class TimelineEasingViewModel
+ {
+ private readonly TimelineKeyframeViewModel _keyframeViewModel;
+ private bool _isEasingModeSelected;
+
+ public TimelineEasingViewModel(TimelineKeyframeViewModel keyframeViewModel, Easings.Functions easingFunction)
+ {
+ _keyframeViewModel = keyframeViewModel;
+ _isEasingModeSelected = keyframeViewModel.BaseLayerPropertyKeyframe.EasingFunction == easingFunction;
+
+ EasingFunction = easingFunction;
+ Description = easingFunction.Humanize();
+
+ CreateGeometry();
+ }
+
+ public Easings.Functions EasingFunction { get; }
+ public PointCollection EasingPoints { get; set; }
+ public string Description { get; set; }
+
+ public bool IsEasingModeSelected
+ {
+ get => _isEasingModeSelected;
+ set
+ {
+ _isEasingModeSelected = value;
+ if (_isEasingModeSelected)
+ _keyframeViewModel.SelectEasingMode(this);
+ }
+ }
+
+ private void CreateGeometry()
+ {
+ EasingPoints = new PointCollection();
+ for (var i = 1; i <= 10; i++)
+ {
+ var x = i;
+ var y = Easings.Interpolate(i / 10.0, EasingFunction) * 10;
+ EasingPoints.Add(new Point(x, y));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs
new file mode 100644
index 000000000..bb2d512f4
--- /dev/null
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs
@@ -0,0 +1,189 @@
+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.Services.Interfaces;
+using Stylet;
+
+namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
+{
+ public class TimelineKeyframeViewModel : TimelineKeyframeViewModel
+ {
+ private readonly IProfileEditorService _profileEditorService;
+
+ public TimelineKeyframeViewModel(IProfileEditorService profileEditorService, TimelineViewModel timelineViewModel, LayerPropertyKeyframe layerPropertyKeyframe)
+ : base(profileEditorService, timelineViewModel, layerPropertyKeyframe)
+ {
+ _profileEditorService = profileEditorService;
+ LayerPropertyKeyframe = layerPropertyKeyframe;
+ }
+
+ public LayerPropertyKeyframe LayerPropertyKeyframe { get; }
+
+ #region Context menu actions
+
+ public void Copy()
+ {
+ var newKeyframe = new LayerPropertyKeyframe(
+ LayerPropertyKeyframe.Value,
+ LayerPropertyKeyframe.Position,
+ LayerPropertyKeyframe.EasingFunction,
+ LayerPropertyKeyframe.LayerProperty
+ );
+ LayerPropertyKeyframe.LayerProperty.AddKeyframe(newKeyframe);
+ _profileEditorService.UpdateSelectedProfileElement();
+ }
+
+ public void Delete()
+ {
+ LayerPropertyKeyframe.LayerProperty.RemoveKeyframe(LayerPropertyKeyframe);
+ _profileEditorService.UpdateSelectedProfileElement();
+ }
+
+ #endregion
+ }
+
+ public abstract class TimelineKeyframeViewModel
+ {
+ private readonly IProfileEditorService _profileEditorService;
+ private readonly TimelineViewModel _timelineViewModel;
+ private int _pixelsPerSecond;
+
+ protected TimelineKeyframeViewModel(IProfileEditorService profileEditorService, TimelineViewModel timelineViewModel, BaseLayerPropertyKeyframe baseLayerPropertyKeyframe)
+ {
+ _profileEditorService = profileEditorService;
+ _timelineViewModel = timelineViewModel;
+ BaseLayerPropertyKeyframe = baseLayerPropertyKeyframe;
+ }
+
+ public BaseLayerPropertyKeyframe BaseLayerPropertyKeyframe { get; }
+ public BindableCollection EasingViewModels { get; set; }
+
+ public bool IsSelected { get; set; }
+ public double X { get; set; }
+ public string Timestamp { get; set; }
+
+ public UIElement ParentView { get; set; }
+
+ public void Update(int pixelsPerSecond)
+ {
+ _pixelsPerSecond = pixelsPerSecond;
+
+ X = pixelsPerSecond * BaseLayerPropertyKeyframe.Position.TotalSeconds;
+ Timestamp = $"{Math.Floor(BaseLayerPropertyKeyframe.Position.TotalSeconds):00}.{BaseLayerPropertyKeyframe.Position.Milliseconds:000}";
+ }
+
+ #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
+
+ private void CreateEasingViewModels()
+ {
+ EasingViewModels.AddRange(Enum.GetValues(typeof(Easings.Functions)).Cast().Select(v => new TimelineEasingViewModel(this, v)));
+ }
+
+ public void SelectEasingMode(TimelineEasingViewModel easingViewModel)
+ {
+ BaseLayerPropertyKeyframe.EasingFunction = easingViewModel.EasingFunction;
+ // Set every selection to false except on the VM that made the change
+ foreach (var propertyTrackEasingViewModel in EasingViewModels.Where(vm => vm != easingViewModel))
+ propertyTrackEasingViewModel.IsEasingModeSelected = false;
+
+
+ _profileEditorService.UpdateSelectedProfileElement();
+ }
+
+ #endregion
+
+ #region Movement
+
+ private bool _movementReleased = true;
+ private TimeSpan _startOffset;
+
+ public void ApplyMovement(TimeSpan cursorTime)
+ {
+ if (_movementReleased)
+ {
+ _movementReleased = false;
+ _startOffset = cursorTime - BaseLayerPropertyKeyframe.Position;
+ }
+ else
+ {
+ BaseLayerPropertyKeyframe.Position = cursorTime - _startOffset;
+ if (BaseLayerPropertyKeyframe.Position < TimeSpan.Zero)
+ BaseLayerPropertyKeyframe.Position = TimeSpan.Zero;
+
+ Update(_pixelsPerSecond);
+ }
+ }
+
+ public void ReleaseMovement()
+ {
+ _movementReleased = true;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupView.xaml
new file mode 100644
index 000000000..68822ff6c
--- /dev/null
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupView.xaml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyView.xaml
new file mode 100644
index 000000000..f780cea81
--- /dev/null
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyView.xaml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 b675b107d..e8d2ff611 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyViewModel.cs
@@ -1,30 +1,49 @@
-using System;
+using System.Linq;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract;
+using Artemis.UI.Services.Interfaces;
+using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
{
public class TimelinePropertyViewModel : TimelinePropertyViewModel
{
- public TimelinePropertyViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel) : base(layerPropertyBaseViewModel)
+ private readonly IProfileEditorService _profileEditorService;
+
+ public TimelinePropertyViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel, IProfileEditorService profileEditorService) : base(layerPropertyBaseViewModel)
{
+ _profileEditorService = profileEditorService;
LayerPropertyViewModel = (LayerPropertyViewModel) layerPropertyBaseViewModel;
}
public LayerPropertyViewModel LayerPropertyViewModel { get; }
- public override void Dispose()
+ public override void UpdateKeyframes(TimelineViewModel timelineViewModel)
{
+ var keyframes = LayerPropertyViewModel.LayerProperty.Keyframes.ToList();
+ TimelineKeyframeViewModels.RemoveRange(
+ TimelineKeyframeViewModels.Where(t => !keyframes.Contains(t.BaseLayerPropertyKeyframe))
+ );
+ TimelineKeyframeViewModels.AddRange(
+ keyframes.Where(k => TimelineKeyframeViewModels.All(t => t.BaseLayerPropertyKeyframe != k))
+ .Select(k => new TimelineKeyframeViewModel(_profileEditorService, timelineViewModel, k))
+ );
+
+ foreach (var timelineKeyframeViewModel in TimelineKeyframeViewModels)
+ timelineKeyframeViewModel.Update(_profileEditorService.PixelsPerSecond);
}
}
- public abstract class TimelinePropertyViewModel : IDisposable
+ public abstract class TimelinePropertyViewModel
{
protected TimelinePropertyViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel)
{
LayerPropertyBaseViewModel = layerPropertyBaseViewModel;
+ TimelineKeyframeViewModels = new BindableCollection();
}
public LayerPropertyBaseViewModel LayerPropertyBaseViewModel { get; }
- public abstract void Dispose();
+ public BindableCollection TimelineKeyframeViewModels { get; set; }
+
+ public abstract void UpdateKeyframes(TimelineViewModel timelineViewModel);
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineView.xaml
index fbef30ca6..1a13599be 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineView.xaml
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineView.xaml
@@ -4,9 +4,52 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline"
- mc:Ignorable="d"
- d:DesignHeight="450" d:DesignWidth="800">
-
-
+ xmlns:s="https://github.com/canton7/Stylet"
+ mc:Ignorable="d"
+ d:DesignHeight="25"
+ d:DesignWidth="800"
+ d:DataContext="{d:DesignInstance local:TimelineViewModel}">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
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 865d49a00..113b26e8a 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineViewModel.cs
@@ -1,14 +1,174 @@
-using Stylet;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media;
+using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract;
+using Artemis.UI.Shared.Utilities;
+using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
{
public class TimelineViewModel
{
- public TimelineViewModel(BindableCollection layerPropertyGroups)
+ private readonly LayerPropertiesViewModel _layerPropertiesViewModel;
+
+ public TimelineViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection layerPropertyGroups)
{
+ _layerPropertiesViewModel = layerPropertiesViewModel;
LayerPropertyGroups = layerPropertyGroups;
+ SelectionRectangle = new RectangleGeometry();
+
+ UpdateKeyframes();
}
public BindableCollection LayerPropertyGroups { get; }
+
+ public double Width { get; set; }
+ public RectangleGeometry SelectionRectangle { get; set; }
+
+ public void UpdateKeyframes()
+ {
+ foreach (var layerPropertyGroupViewModel in LayerPropertyGroups)
+ {
+ foreach (var layerPropertyBaseViewModel in layerPropertyGroupViewModel.GetAllChildren())
+ {
+ if (layerPropertyBaseViewModel is LayerPropertyViewModel layerPropertyViewModel)
+ layerPropertyViewModel.TimelinePropertyBaseViewModel.UpdateKeyframes(this);
+ }
+ }
+ }
+
+ #region Keyframe movement
+
+ public void MoveSelectedKeyframes(TimeSpan cursorTime)
+ {
+ // Ensure the selection rectangle doesn't show, the view isn't aware of different types of dragging
+ SelectionRectangle.Rect = new Rect();
+
+ var keyframeViewModels = GetAllKeyframeViewModels();
+ foreach (var keyframeViewModel in keyframeViewModels.Where(k => k.IsSelected))
+ keyframeViewModel.ApplyMovement(cursorTime);
+
+ _layerPropertiesViewModel.ProfileEditorService.UpdateProfilePreview();
+ }
+
+
+ public void ReleaseSelectedKeyframes()
+ {
+ var keyframeViewModels = GetAllKeyframeViewModels();
+ foreach (var keyframeViewModel in keyframeViewModels.Where(k => k.IsSelected))
+ keyframeViewModel.ReleaseMovement();
+ }
+
+ #endregion
+
+ #region Keyframe selection
+
+ private Point _mouseDragStartPoint;
+ private bool _mouseDragging;
+
+ // ReSharper disable once UnusedMember.Global - Called from view
+ public void TimelineCanvasMouseDown(object sender, MouseButtonEventArgs e)
+ {
+ if (e.LeftButton == MouseButtonState.Released)
+ return;
+
+ ((IInputElement) sender).CaptureMouse();
+
+ SelectionRectangle.Rect = new Rect();
+ _mouseDragStartPoint = e.GetPosition((IInputElement) sender);
+ _mouseDragging = true;
+ e.Handled = true;
+ }
+
+ // ReSharper disable once UnusedMember.Global - Called from view
+ public void TimelineCanvasMouseUp(object sender, MouseEventArgs e)
+ {
+ if (!_mouseDragging)
+ return;
+
+ var position = e.GetPosition((IInputElement) sender);
+ var selectedRect = new Rect(_mouseDragStartPoint, position);
+ SelectionRectangle.Rect = selectedRect;
+
+ var keyframeViewModels = GetAllKeyframeViewModels();
+ var selectedKeyframes = HitTestUtilities.GetHitViewModels((Visual) sender, SelectionRectangle);
+ foreach (var keyframeViewModel in keyframeViewModels)
+ keyframeViewModel.IsSelected = selectedKeyframes.Contains(keyframeViewModel);
+
+ _mouseDragging = false;
+ e.Handled = true;
+ ((IInputElement) sender).ReleaseMouseCapture();
+ }
+
+ public void TimelineCanvasMouseMove(object sender, MouseEventArgs e)
+ {
+ if (_mouseDragging && e.LeftButton == MouseButtonState.Pressed)
+ {
+ var position = e.GetPosition((IInputElement) sender);
+ var selectedRect = new Rect(_mouseDragStartPoint, position);
+ SelectionRectangle.Rect = selectedRect;
+ e.Handled = true;
+ }
+ }
+
+ public void SelectKeyframe(TimelineKeyframeViewModel clicked, bool selectBetween, bool toggle)
+ {
+ var keyframeViewModels = GetAllKeyframeViewModels();
+ if (selectBetween)
+ {
+ var selectedIndex = keyframeViewModels.FindIndex(k => k.IsSelected);
+ // If nothing is selected, select only the clicked
+ if (selectedIndex == -1)
+ {
+ clicked.IsSelected = true;
+ return;
+ }
+
+ foreach (var keyframeViewModel in keyframeViewModels)
+ keyframeViewModel.IsSelected = false;
+
+ var clickedIndex = keyframeViewModels.IndexOf(clicked);
+ if (clickedIndex < selectedIndex)
+ {
+ foreach (var keyframeViewModel in keyframeViewModels.Skip(clickedIndex).Take(selectedIndex - clickedIndex + 1))
+ keyframeViewModel.IsSelected = true;
+ }
+ else
+ {
+ foreach (var keyframeViewModel in keyframeViewModels.Skip(selectedIndex).Take(clickedIndex - selectedIndex + 1))
+ keyframeViewModel.IsSelected = true;
+ }
+ }
+ else if (toggle)
+ {
+ // Toggle only the clicked keyframe, leave others alone
+ clicked.IsSelected = !clicked.IsSelected;
+ }
+ else
+ {
+ // Only select the clicked keyframe
+ foreach (var keyframeViewModel in keyframeViewModels)
+ keyframeViewModel.IsSelected = false;
+ clicked.IsSelected = true;
+ }
+ }
+
+ private List GetAllKeyframeViewModels()
+ {
+ var viewModels = new List();
+ foreach (var layerPropertyGroupViewModel in LayerPropertyGroups)
+ viewModels.AddRange(layerPropertyGroupViewModel.GetAllChildren());
+
+ var keyframes = viewModels.Where(vm => vm is LayerPropertyViewModel)
+ .SelectMany(vm => ((LayerPropertyViewModel) vm).TimelinePropertyBaseViewModel.TimelineKeyframeViewModels)
+ .ToList();
+
+ return keyframes;
+ }
+
+ #endregion
}
}
\ No newline at end of file
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 ec520b534..8f6be3c11 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs
@@ -1,13 +1,18 @@
using System;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput.Abstract;
+using Artemis.UI.Services.Interfaces;
namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree
{
public class TreePropertyViewModel : TreePropertyViewModel
{
- public TreePropertyViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel, PropertyInputViewModel propertyInputViewModel) : base(layerPropertyBaseViewModel)
+ private readonly IProfileEditorService _profileEditorService;
+
+ public TreePropertyViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel, PropertyInputViewModel propertyInputViewModel,
+ IProfileEditorService profileEditorService) : base(layerPropertyBaseViewModel)
{
+ _profileEditorService = profileEditorService;
LayerPropertyViewModel = (LayerPropertyViewModel) layerPropertyBaseViewModel;
PropertyInputViewModel = propertyInputViewModel;
}
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreeView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreeView.xaml
index 95412e80c..02206c91a 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreeView.xaml
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreeView.xaml
@@ -90,7 +90,7 @@
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreeViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreeViewModel.cs
index 1f267e0d9..3835d0b09 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreeViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreeViewModel.cs
@@ -7,8 +7,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree
{
public class TreeViewModel
{
- public TreeViewModel(BindableCollection layerPropertyGroups)
+ private readonly LayerPropertiesViewModel _layerPropertiesViewModel;
+
+ public TreeViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection layerPropertyGroups)
{
+ _layerPropertiesViewModel = layerPropertiesViewModel;
LayerPropertyGroups = layerPropertyGroups;
}
diff --git a/src/Artemis.UI/Services/Interfaces/IProfileEditorService.cs b/src/Artemis.UI/Services/Interfaces/IProfileEditorService.cs
index dbffd48fb..2a393ecc1 100644
--- a/src/Artemis.UI/Services/Interfaces/IProfileEditorService.cs
+++ b/src/Artemis.UI/Services/Interfaces/IProfileEditorService.cs
@@ -7,7 +7,6 @@ using Artemis.UI.Events;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree;
-using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput.Abstract;
namespace Artemis.UI.Services.Interfaces
{
@@ -16,6 +15,7 @@ namespace Artemis.UI.Services.Interfaces
Profile SelectedProfile { get; }
ProfileElement SelectedProfileElement { get; }
TimeSpan CurrentTime { get; set; }
+ int PixelsPerSecond { get; set; }
LayerPropertyBaseViewModel CreateLayerPropertyViewModel(BaseLayerProperty baseLayerProperty, PropertyDescriptionAttribute propertyDescription);
void ChangeSelectedProfile(Profile profile);
@@ -53,6 +53,11 @@ namespace Artemis.UI.Services.Interfaces
///
event EventHandler CurrentTimeChanged;
+ ///
+ /// Occurs when the pixels per second (zoom level) is changed
+ ///
+ event EventHandler PixelsPerSecondChanged;
+
///
/// Occurs when the profile preview has been updated
///
diff --git a/src/Artemis.UI/Services/ProfileEditorService.cs b/src/Artemis.UI/Services/ProfileEditorService.cs
index 0616853d1..cd55e46e7 100644
--- a/src/Artemis.UI/Services/ProfileEditorService.cs
+++ b/src/Artemis.UI/Services/ProfileEditorService.cs
@@ -29,6 +29,7 @@ namespace Artemis.UI.Services
private readonly IKernel _kernel;
private TimeSpan _currentTime;
private TimeSpan _lastUpdateTime;
+ private int _pixelsPerSecond;
public ProfileEditorService(ICoreService coreService, IProfileService profileService, IKernel kernel)
{
@@ -46,6 +47,7 @@ namespace Artemis.UI.Services
{typeof(SKPoint), typeof(SKPointPropertyInputViewModel)},
{typeof(SKSize), typeof(SKSizePropertyInputViewModel)}
};
+ PixelsPerSecond = 31;
}
public Dictionary RegisteredPropertyEditors { get; set; }
@@ -66,6 +68,17 @@ namespace Artemis.UI.Services
}
}
+ public int PixelsPerSecond
+ {
+ get => _pixelsPerSecond;
+ set
+ {
+ _pixelsPerSecond = value;
+ OnPixelsPerSecondChanged();
+ }
+ }
+
+
public LayerPropertyBaseViewModel CreateLayerPropertyViewModel(BaseLayerProperty baseLayerProperty, PropertyDescriptionAttribute propertyDescription)
{
// Go through the pain of instantiating a generic type VM now via reflection to make things a lot simpler down the line
@@ -93,7 +106,7 @@ namespace Artemis.UI.Services
{
new ConstructorArgument("layerPropertyViewModel", layerPropertyViewModel)
};
- return new TreePropertyViewModel(layerPropertyViewModel, (PropertyInputViewModel) _kernel.Get(type, parameters));
+ return new TreePropertyViewModel(layerPropertyViewModel, (PropertyInputViewModel) _kernel.Get(type, parameters), this);
}
public void ChangeSelectedProfile(Profile profile)
@@ -181,8 +194,9 @@ namespace Artemis.UI.Services
public event EventHandler ProfileElementSelected;
public event EventHandler SelectedProfileElementUpdated;
public event EventHandler CurrentTimeChanged;
+ public event EventHandler PixelsPerSecondChanged;
public event EventHandler ProfilePreviewUpdated;
-
+
public void StopRegularRender()
{
_coreService.ModuleUpdatingDisabled = true;
@@ -218,6 +232,11 @@ namespace Artemis.UI.Services
CurrentTimeChanged?.Invoke(this, EventArgs.Empty);
}
+ protected virtual void OnPixelsPerSecondChanged()
+ {
+ PixelsPerSecondChanged?.Invoke(this, EventArgs.Empty);
+ }
+
protected virtual void OnProfilePreviewUpdated()
{
ProfilePreviewUpdated?.Invoke(this, EventArgs.Empty);