diff --git a/src/Artemis.Core/Models/Profile/Conditions/Abstract/DisplayConditionPart.cs b/src/Artemis.Core/Models/Profile/Conditions/Abstract/DisplayConditionPart.cs
index 36a73ea76..534f48926 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Abstract/DisplayConditionPart.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Abstract/DisplayConditionPart.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Linq.Expressions;
using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Entities.Profile.Abstract;
@@ -34,9 +35,10 @@ namespace Artemis.Core.Models.Profile.Conditions.Abstract
}
}
- public abstract DisplayConditionPartEntity GetEntity();
+ public abstract bool Evaluate();
- internal abstract void ApplyToEntity();
internal abstract void Initialize(IDataModelService dataModelService);
+ internal abstract void ApplyToEntity();
+ internal abstract DisplayConditionPartEntity GetEntity();
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionGroup.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionGroup.cs
index 960387384..c6fcbfc97 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionGroup.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionGroup.cs
@@ -33,6 +33,23 @@ namespace Artemis.Core.Models.Profile.Conditions
public BooleanOperator BooleanOperator { get; set; }
public DisplayConditionGroupEntity DisplayConditionGroupEntity { get; set; }
+ public override bool Evaluate()
+ {
+ switch (BooleanOperator)
+ {
+ case BooleanOperator.And:
+ return Children.All(c => c.Evaluate());
+ case BooleanOperator.Or:
+ return Children.Any(c => c.Evaluate());
+ case BooleanOperator.AndNot:
+ return Children.All(c => !c.Evaluate());
+ case BooleanOperator.OrNot:
+ return Children.Any(c => !c.Evaluate());
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
internal override void ApplyToEntity()
{
DisplayConditionGroupEntity.BooleanOperator = (int) BooleanOperator;
@@ -49,7 +66,7 @@ namespace Artemis.Core.Models.Profile.Conditions
child.Initialize(dataModelService);
}
- public override DisplayConditionPartEntity GetEntity()
+ internal override DisplayConditionPartEntity GetEntity()
{
return DisplayConditionGroupEntity;
}
diff --git a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionListPredicate.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionListPredicate.cs
index d5596b0b8..2e96b913a 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionListPredicate.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionListPredicate.cs
@@ -8,18 +8,24 @@ namespace Artemis.Core.Models.Profile.Conditions
{
public ListOperator ListOperator { get; set; }
+ public override bool Evaluate()
+ {
+ return true;
+ }
+
+
internal override void ApplyToEntity()
{
}
- internal override void Initialize(IDataModelService dataModelService)
- {
- }
-
- public override DisplayConditionPartEntity GetEntity()
+ internal override DisplayConditionPartEntity GetEntity()
{
return null;
}
+
+ internal override void Initialize(IDataModelService dataModelService)
+ {
+ }
}
public enum ListOperator
diff --git a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs
index 4b694d69d..04a1af178 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs
@@ -61,6 +61,7 @@ namespace Artemis.Core.Models.Profile.Conditions
ValidateOperator();
ValidateRightSide();
+
CreateExpression();
}
@@ -91,6 +92,7 @@ namespace Artemis.Core.Models.Profile.Conditions
RightPropertyPath = null;
SetStaticValue(staticValue);
+
CreateExpression();
}
@@ -111,14 +113,21 @@ namespace Artemis.Core.Models.Profile.Conditions
var leftType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
if (displayConditionOperator.SupportsType(leftType))
Operator = displayConditionOperator;
+
+ CreateExpression();
}
- public void CreateExpression()
+ private void CreateExpression()
{
+ DynamicConditionLambda = null;
+ CompiledDynamicConditionLambda = null;
+ StaticConditionLambda = null;
+ CompiledStaticConditionLambda = null;
+
if (PredicateType == PredicateType.Dynamic)
CreateDynamicExpression();
- else
- CreateStaticExpression();
+
+ CreateStaticExpression();
}
internal override void ApplyToEntity()
@@ -134,6 +143,16 @@ namespace Artemis.Core.Models.Profile.Conditions
DisplayConditionPredicateEntity.OperatorType = Operator?.GetType().Name;
}
+ public override bool Evaluate()
+ {
+ if (CompiledDynamicConditionLambda != null)
+ return CompiledDynamicConditionLambda(LeftDataModel, RightDataModel);
+ if (CompiledStaticConditionLambda != null)
+ return CompiledStaticConditionLambda(LeftDataModel);
+
+ return false;
+ }
+
internal override void Initialize(IDataModelService dataModelService)
{
// Left side
@@ -184,7 +203,7 @@ namespace Artemis.Core.Models.Profile.Conditions
}
}
- public override DisplayConditionPartEntity GetEntity()
+ internal override DisplayConditionPartEntity GetEntity()
{
return DisplayConditionPredicateEntity;
}
@@ -199,9 +218,6 @@ namespace Artemis.Core.Models.Profile.Conditions
Operator = null;
}
- ///
- /// Validates the right side, ensuring it is still compatible with the current left side
- ///
private void ValidateRightSide()
{
var leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
@@ -223,10 +239,6 @@ namespace Artemis.Core.Models.Profile.Conditions
}
}
- ///
- /// Updates the current static value, ensuring it is a valid type. This assumes the types are compatible if they
- /// differ.
- ///
private void SetStaticValue(object staticValue)
{
// If the left side is empty simply apply the value, any validation will wait
diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs
index f1dd44db7..728848333 100644
--- a/src/Artemis.Core/Models/Profile/Folder.cs
+++ b/src/Artemis.Core/Models/Profile/Folder.cs
@@ -65,6 +65,8 @@ namespace Artemis.Core.Models.Profile
if (!Enabled)
return;
+ UpdateDisplayCondition();
+
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
baseLayerEffect.Update(deltaTime);
diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index a96d9b15a..2aa064252 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -205,6 +205,8 @@ namespace Artemis.Core.Models.Profile
if (LayerBrush?.BaseProperties == null || !LayerBrush.BaseProperties.PropertiesInitialized)
return;
+ UpdateDisplayCondition();
+
// TODO: Remove, this is slow and stupid
// For now, reset all keyframe engines after the last keyframe was hit
// This is a placeholder method of repeating the animation until repeat modes are implemented
diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerPropertyKeyframe.cs b/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerPropertyKeyframe.cs
index d0537d8b1..d54a254a4 100644
--- a/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerPropertyKeyframe.cs
+++ b/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerPropertyKeyframe.cs
@@ -18,6 +18,11 @@ namespace Artemis.Core.Models.Profile.LayerProperties
///
public BaseLayerProperty BaseLayerProperty { get; internal set; }
+ ///
+ /// The timeline this keyframe is contained in
+ ///
+ public abstract Timeline Timeline { get; set; }
+
///
/// The position of this keyframe in the timeline
///
@@ -28,4 +33,11 @@ namespace Artemis.Core.Models.Profile.LayerProperties
///
public Easings.Functions EasingFunction { get; set; }
}
+
+ public enum Timeline
+ {
+ Start,
+ Main,
+ End
+ }
}
\ 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 d5eab15fd..e3f255975 100644
--- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
+++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
@@ -23,10 +23,16 @@ namespace Artemis.Core.Models.Profile.LayerProperties
private T _currentValue;
private bool _isInitialized;
private List> _keyframes;
+ private List> _startKeyframes;
+ private List> _mainKeyframes;
+ private List> _endKeyframes;
protected LayerProperty()
{
_keyframes = new List>();
+ _startKeyframes = new List>();
+ _mainKeyframes = new List>();
+ _endKeyframes = new List>();
}
///
@@ -84,7 +90,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties
/// The value to set.
///
/// An optional time to set the value add, if provided and property is using keyframes the value will be set to an new
- /// or existing keyframe.
+ /// or existing keyframe in the currently active timeline.
///
public void SetCurrentValue(T value, TimeSpan? time)
{
@@ -93,10 +99,10 @@ namespace Artemis.Core.Models.Profile.LayerProperties
else
{
// If on a keyframe, update the keyframe
- var currentKeyframe = Keyframes.FirstOrDefault(k => k.Position == time.Value);
+ var currentKeyframe = Keyframes.FirstOrDefault(k => k.Position == time.Value && k.Timeline == ProfileElement.CurrentTimeline);
// Create a new keyframe if none found
if (currentKeyframe == null)
- AddKeyframe(new LayerPropertyKeyframe(value, time.Value, Easings.Functions.Linear, this));
+ AddKeyframe(new LayerPropertyKeyframe(value, time.Value, ProfileElement.CurrentTimeline, Easings.Functions.Linear, this));
else
currentKeyframe.Value = value;
@@ -132,6 +138,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties
var newKeyframe = new LayerPropertyKeyframe(
keyframe.Value,
keyframe.Position,
+ keyframe.Timeline,
keyframe.EasingFunction,
keyframe.LayerProperty
);
@@ -193,22 +200,42 @@ namespace Artemis.Core.Models.Profile.LayerProperties
if (!KeyframesSupported || !KeyframesEnabled)
return;
+ var keyframeSet = _keyframes;
+ if (ProfileElement.CurrentTimeline == Timeline.Start)
+ keyframeSet = _startKeyframes;
+ else if (ProfileElement.CurrentTimeline == Timeline.Main)
+ keyframeSet = _mainKeyframes;
+ else if (ProfileElement.CurrentTimeline == Timeline.End)
+ keyframeSet = _endKeyframes;
+
// The current keyframe is the last keyframe before the current time
- CurrentKeyframe = _keyframes.LastOrDefault(k => k.Position <= TimelineProgress);
+ CurrentKeyframe = keyframeSet.LastOrDefault(k => k.Position <= TimelineProgress);
+
+ // If the current keyframe is null, try to find it in previous timelines
+ if (CurrentKeyframe == null && ProfileElement.CurrentTimeline == Timeline.Main)
+ CurrentKeyframe = _startKeyframes.LastOrDefault();
+ else if (CurrentKeyframe == null && ProfileElement.CurrentTimeline == Timeline.End)
+ CurrentKeyframe = _mainKeyframes.LastOrDefault() ?? _startKeyframes.LastOrDefault();
+
// Keyframes are sorted by position so we can safely assume the next keyframe's position is after the current
- var nextIndex = _keyframes.IndexOf(CurrentKeyframe) + 1;
- NextKeyframe = _keyframes.Count > nextIndex ? _keyframes[nextIndex] : null;
+ var nextIndex = keyframeSet.IndexOf(CurrentKeyframe) + 1;
+ NextKeyframe = keyframeSet.Count > nextIndex ? keyframeSet[nextIndex] : null;
// No need to update the current value if either of the keyframes are null
if (CurrentKeyframe == null)
- CurrentValue = _keyframes.Any() ? _keyframes[0].Value : BaseValue;
+ CurrentValue = keyframeSet.Any() ? keyframeSet[0].Value : BaseValue;
else if (NextKeyframe == null)
CurrentValue = CurrentKeyframe.Value;
// Only determine progress and current value if both keyframes are present
else
{
- var timeDiff = NextKeyframe.Position - CurrentKeyframe.Position;
- var keyframeProgress = (float) ((TimelineProgress - CurrentKeyframe.Position).TotalMilliseconds / timeDiff.TotalMilliseconds);
+ // If the current keyframe belongs to a previous timeline, consider it starting at 0
+ var currentKeyframePosition = CurrentKeyframe.Position;
+ if (CurrentKeyframe.Timeline != ProfileElement.CurrentTimeline)
+ currentKeyframePosition = TimeSpan.Zero;
+
+ var timeDiff = NextKeyframe.Position - currentKeyframePosition;
+ var keyframeProgress = (float) ((TimelineProgress - currentKeyframePosition).TotalMilliseconds / timeDiff.TotalMilliseconds);
var keyframeProgressEased = (float) Easings.Interpolate(keyframeProgress, CurrentKeyframe.EasingFunction);
UpdateCurrentValue(keyframeProgress, keyframeProgressEased);
}
@@ -227,11 +254,15 @@ namespace Artemis.Core.Models.Profile.LayerProperties
}
///
- /// Sorts the keyframes in ascending order by position
+ /// Sorts the keyframes in ascending order by position and divides the keyframes into different timelines
///
internal void SortKeyframes()
{
_keyframes = _keyframes.OrderBy(k => k.Position).ToList();
+
+ _startKeyframes = _keyframes.Where(k => k.Timeline == Timeline.Start).ToList();
+ _mainKeyframes = _keyframes.Where(k => k.Timeline == Timeline.Main).ToList();
+ _endKeyframes = _keyframes.Where(k => k.Timeline == Timeline.End).ToList();
}
internal override void ApplyToLayerProperty(PropertyEntity entity, LayerPropertyGroup layerPropertyGroup, bool fromStorage)
@@ -258,6 +289,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties
_keyframes.AddRange(entity.KeyframeEntities.Select(k => new LayerPropertyKeyframe(
JsonConvert.DeserializeObject(k.Value),
k.Position,
+ (Timeline) k.Timeline,
(Easings.Functions) k.EasingFunction,
this
)));
@@ -287,6 +319,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties
{
Value = JsonConvert.SerializeObject(k.Value),
Position = k.Position,
+ Timeline = (int) k.Timeline,
EasingFunction = (int) k.EasingFunction
}));
}
diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerPropertyKeyFrame.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerPropertyKeyFrame.cs
index af37209aa..9f6791a80 100644
--- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerPropertyKeyFrame.cs
+++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerPropertyKeyFrame.cs
@@ -6,10 +6,12 @@ namespace Artemis.Core.Models.Profile.LayerProperties
public class LayerPropertyKeyframe : BaseLayerPropertyKeyframe
{
private TimeSpan _position;
+ private Timeline _timeline;
- public LayerPropertyKeyframe(T value, TimeSpan position, Easings.Functions easingFunction, LayerProperty layerProperty) : base(layerProperty)
+ public LayerPropertyKeyframe(T value, TimeSpan position, Timeline timeline, Easings.Functions easingFunction, LayerProperty layerProperty) : base(layerProperty)
{
_position = position;
+ _timeline = timeline;
Value = value;
LayerProperty = layerProperty;
EasingFunction = easingFunction;
@@ -25,6 +27,17 @@ namespace Artemis.Core.Models.Profile.LayerProperties
///
public T Value { get; set; }
+ ///
+ public override Timeline Timeline
+ {
+ get => _timeline;
+ set
+ {
+ _timeline = value;
+ LayerProperty.SortKeyframes();
+ }
+ }
+
///
public override TimeSpan Position
{
diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
index 80338b462..a8aa76848 100644
--- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
+++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
@@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Core.Annotations;
using Artemis.Core.Models.Profile.Conditions;
+using Artemis.Core.Models.Profile.LayerProperties;
using Artemis.Core.Plugins.LayerEffect.Abstract;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
@@ -18,6 +19,11 @@ namespace Artemis.Core.Models.Profile
private SKPath _path;
internal abstract RenderElementEntity RenderElementEntity { get; }
+ ///
+ /// Gets or sets the currently active timeline
+ ///
+ public Timeline CurrentTimeline { get; set; }
+
///
/// Gets the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is
/// clipped.
@@ -142,6 +148,11 @@ namespace Artemis.Core.Models.Profile
set => SetAndNotify(ref _displayConditionGroup, value);
}
+ public void UpdateDisplayCondition()
+ {
+ var rootGroupResult = DisplayConditionGroup.Evaluate();
+ }
+
#endregion
#region Events
diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs
index 3406dabc2..d970852c7 100644
--- a/src/Artemis.Core/Services/Storage/ProfileService.cs
+++ b/src/Artemis.Core/Services/Storage/ProfileService.cs
@@ -19,10 +19,10 @@ namespace Artemis.Core.Services.Storage
///
public class ProfileService : IProfileService
{
- private readonly IRenderElementService _renderElementService;
private readonly ILogger _logger;
private readonly IPluginService _pluginService;
private readonly IProfileRepository _profileRepository;
+ private readonly IRenderElementService _renderElementService;
private readonly ISurfaceService _surfaceService;
internal ProfileService(ILogger logger, IPluginService pluginService, ISurfaceService surfaceService, IRenderElementService renderElementService, IProfileRepository profileRepository)
@@ -39,6 +39,8 @@ namespace Artemis.Core.Services.Storage
_pluginService.PluginDisabled += OnPluginToggled;
}
+ public JsonSerializerSettings MementoSettings { get; set; } = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All};
+
public void ActivateDefaultProfiles()
{
foreach (var profileModule in _pluginService.GetPluginsOfType())
@@ -109,7 +111,7 @@ namespace Artemis.Core.Services.Storage
public void UpdateProfile(Profile profile, bool includeChildren)
{
_logger.Debug("Updating profile " + profile);
- var memento = JsonConvert.SerializeObject(profile.ProfileEntity);
+ var memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings);
profile.RedoStack.Clear();
profile.UndoStack.Push(memento);
@@ -135,9 +137,9 @@ namespace Artemis.Core.Services.Storage
ActivateProfile(module, null);
var top = profile.UndoStack.Pop();
- var memento = JsonConvert.SerializeObject(profile.ProfileEntity);
+ var memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings);
profile.RedoStack.Push(memento);
- profile.ProfileEntity = JsonConvert.DeserializeObject(top);
+ profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings);
profile.ApplyToProfile();
ActivateProfile(module, profile);
@@ -155,9 +157,9 @@ namespace Artemis.Core.Services.Storage
ActivateProfile(module, null);
var top = profile.RedoStack.Pop();
- var memento = JsonConvert.SerializeObject(profile.ProfileEntity);
+ var memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings);
profile.UndoStack.Push(memento);
- profile.ProfileEntity = JsonConvert.DeserializeObject(top);
+ profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings);
profile.ApplyToProfile();
ActivateProfile(module, profile);
diff --git a/src/Artemis.Storage/Entities/Profile/PropertyEntity.cs b/src/Artemis.Storage/Entities/Profile/PropertyEntity.cs
index de95b56b8..610bf0910 100644
--- a/src/Artemis.Storage/Entities/Profile/PropertyEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/PropertyEntity.cs
@@ -22,6 +22,7 @@ namespace Artemis.Storage.Entities.Profile
public class KeyframeEntity
{
public TimeSpan Position { get; set; }
+ public int Timeline { get; set; }
public string Value { get; set; }
public int EasingFunction { get; set; }
}
diff --git a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs
index 4738fe07b..9f627d4ea 100644
--- a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs
+++ b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Artemis.Core.Models.Profile;
+using Artemis.Core.Models.Profile.LayerProperties;
using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Plugins.Models;
using Artemis.UI.Shared.Events;
@@ -14,6 +15,7 @@ namespace Artemis.UI.Shared.Services.Interfaces
Profile SelectedProfile { get; }
RenderProfileElement SelectedProfileElement { get; }
TimeSpan CurrentTime { get; set; }
+ Timeline CurrentTimeline { get; set; }
int PixelsPerSecond { get; set; }
IReadOnlyList RegisteredPropertyEditors { get; }
IKernel Kernel { get; }
@@ -54,6 +56,11 @@ namespace Artemis.UI.Shared.Services.Interfaces
///
event EventHandler CurrentTimeChanged;
+ ///
+ /// Occurs when the current editor timeline is changed
+ ///
+ event EventHandler CurrentTimelineChanged;
+
///
/// Occurs when the pixels per second (zoom level) is changed
///
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs
index 34384bddf..ad64bc88b 100644
--- a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs
+++ b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using Artemis.Core.Models.Profile;
+using Artemis.Core.Models.Profile.LayerProperties;
using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Plugins.Exceptions;
using Artemis.Core.Plugins.Models;
@@ -24,6 +25,7 @@ namespace Artemis.UI.Shared.Services
private TimeSpan _currentTime;
private TimeSpan _lastUpdateTime;
private int _pixelsPerSecond;
+ private Timeline _currentTimeline;
public ProfileEditorService(ICoreService coreService, IProfileService profileService, IKernel kernel, ILogger logger)
{
@@ -46,14 +48,25 @@ namespace Artemis.UI.Shared.Services
get => _currentTime;
set
{
- if (_currentTime.Equals(value))
- return;
+ if (_currentTime.Equals(value)) return;
_currentTime = value;
UpdateProfilePreview();
OnCurrentTimeChanged();
}
}
+ public Timeline CurrentTimeline
+ {
+ get => _currentTimeline;
+ set
+ {
+ if (_currentTimeline.Equals(value)) return;
+ _currentTimeline = value;
+ UpdateProfilePreview();
+ OnCurrentTimelineChanged();
+ }
+ }
+
public int PixelsPerSecond
{
get => _pixelsPerSecond;
@@ -113,12 +126,14 @@ namespace Artemis.UI.Shared.Services
var delta = CurrentTime - _lastUpdateTime;
foreach (var folder in SelectedProfile.GetAllFolders())
{
+ folder.CurrentTimeline = CurrentTimeline;
foreach (var baseLayerEffect in folder.LayerEffects)
baseLayerEffect.Update(delta.TotalSeconds);
}
foreach (var layer in SelectedProfile.GetAllLayers())
{
+ layer.CurrentTimeline = CurrentTimeline;
layer.OverrideProgress(CurrentTime);
layer.LayerBrush?.Update(delta.TotalSeconds);
foreach (var baseLayerEffect in layer.LayerEffects)
@@ -212,6 +227,7 @@ namespace Artemis.UI.Shared.Services
public event EventHandler ProfileElementSelected;
public event EventHandler SelectedProfileElementUpdated;
public event EventHandler CurrentTimeChanged;
+ public event EventHandler CurrentTimelineChanged;
public event EventHandler PixelsPerSecondChanged;
public event EventHandler ProfilePreviewUpdated;
@@ -250,6 +266,11 @@ namespace Artemis.UI.Shared.Services
CurrentTimeChanged?.Invoke(this, EventArgs.Empty);
}
+ protected virtual void OnCurrentTimelineChanged()
+ {
+ CurrentTimelineChanged?.Invoke(this, EventArgs.Empty);
+ }
+
protected virtual void OnPixelsPerSecondChanged()
{
PixelsPerSecondChanged?.Invoke(this, EventArgs.Empty);
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml
index d20afb360..c0b04af58 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionPredicateView.xaml
@@ -148,13 +148,11 @@
HorizontalAlignment="Left">
-
-
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml
index ab6cb5e15..9df02ef19 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml
@@ -9,27 +9,63 @@
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
+
+
+
+
-
+
-
+
Display conditions
-
+
-
-
+
+
-
- Disabled
-
- Enabled
+
+
+ Play once
+
+
+ When conditions no longer met
+
+
+
+
+
+ When conditions are no longer met, finish the timelines and then stop displaying.
+
+
+
+
+
+ WAIT FOR FINISH
+
+
+
+
+
+
+ When conditions are no longer met, stop displaying immediately.
+
+
+
+
+
+ SKIP
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml
index 2e7726bfb..6b14a0e04 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml
@@ -291,7 +291,7 @@
-
+
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
index 8cc81045e..0013113d6 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
@@ -84,6 +84,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
}
}
+ public int CurrentTimelineIndex
+ {
+ get => (int) ProfileEditorService.CurrentTimeline;
+ set
+ {
+ ProfileEditorService.CurrentTimeline = (Core.Models.Profile.LayerProperties.Timeline) value;
+ ProfileEditorService.CurrentTime = TimeSpan.Zero;
+ }
+ }
+
public bool PropertyTreeVisible => PropertyTreeIndex == 0;
public RenderProfileElement SelectedProfileElement
@@ -130,6 +140,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
ProfileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
ProfileEditorService.CurrentTimeChanged += ProfileEditorServiceOnCurrentTimeChanged;
+ ProfileEditorService.CurrentTimelineChanged += ProfileEditorServiceOnCurrentTimelineChanged;
ProfileEditorService.PixelsPerSecondChanged += ProfileEditorServiceOnPixelsPerSecondChanged;
base.OnInitialActivate();
@@ -139,6 +150,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
{
ProfileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
ProfileEditorService.CurrentTimeChanged -= ProfileEditorServiceOnCurrentTimeChanged;
+ ProfileEditorService.CurrentTimelineChanged -= ProfileEditorServiceOnCurrentTimelineChanged;
ProfileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
PopulateProperties(null);
@@ -168,6 +180,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
NotifyOfPropertyChange(nameof(TimeCaretPosition));
}
+ private void ProfileEditorServiceOnCurrentTimelineChanged(object? sender, EventArgs e)
+ {
+ NotifyOfPropertyChange(nameof(CurrentTimelineIndex));
+ TimelineViewModel.UpdateKeyframes();
+ }
+
private void ProfileEditorServiceOnPixelsPerSecondChanged(object sender, EventArgs e)
{
NotifyOfPropertyChange(nameof(TimeCaretPosition));
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 8e320fd77..6273574bf 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs
@@ -27,6 +27,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
var newKeyframe = new LayerPropertyKeyframe(
LayerPropertyKeyframe.Value,
LayerPropertyKeyframe.Position,
+ LayerPropertyKeyframe.Timeline,
LayerPropertyKeyframe.EasingFunction,
LayerPropertyKeyframe.LayerProperty
);
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupView.xaml
index 1fae0fbca..7fa179733 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupView.xaml
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupView.xaml
@@ -23,7 +23,7 @@
Visibility="{Binding LayerPropertyGroupViewModel.IsExpanded, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}"
ItemsSource="{Binding TimelineKeyframeViewModels}"
Background="{DynamicResource MaterialDesignToolBarBackground}"
- HorizontalAlignment="Left">
+ HorizontalAlignment="Stretch">
@@ -49,20 +49,22 @@
+ HorizontalAlignment="Stretch"
+ HorizontalContentAlignment="Stretch">
+
+
+
-
+
-
+
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 2a8a9578f..7d4dab2f6 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyGroupViewModel.cs
@@ -2,16 +2,19 @@
using System.ComponentModel;
using System.Linq;
using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract;
+using Artemis.UI.Shared.Services.Interfaces;
using Stylet;
namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
{
public class TimelinePropertyGroupViewModel : PropertyChangedBase
{
+ private readonly IProfileEditorService _profileEditorService;
private BindableCollection _timelineKeyframeViewModels;
- public TimelinePropertyGroupViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel)
+ public TimelinePropertyGroupViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel, IProfileEditorService profileEditorService)
{
+ _profileEditorService = profileEditorService;
LayerPropertyGroupViewModel = (LayerPropertyGroupViewModel) layerPropertyBaseViewModel;
TimelineKeyframeViewModels = new BindableCollection();
@@ -32,6 +35,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
{
TimelineKeyframeViewModels.Clear();
TimelineKeyframeViewModels.AddRange(LayerPropertyGroupViewModel.GetKeyframes(false)
+ .Where(k => k.Timeline == _profileEditorService.CurrentTimeline)
.Select(k => LayerPropertyGroupViewModel.ProfileEditorService.PixelsPerSecond * k.Position.TotalSeconds));
}
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 87f5eaa42..d20695b20 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyView.xaml
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelinePropertyView.xaml
@@ -9,7 +9,9 @@
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:TimelinePropertyViewModel}"
- Visibility="{Binding LayerPropertyBaseViewModel.IsVisible, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
+ Visibility="{Binding LayerPropertyBaseViewModel.IsVisible, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}"
+ MinWidth="{Binding Width}"
+ HorizontalAlignment="Stretch">
k.Timeline == _profileEditorService.CurrentTimeline)
+ .ToList();
var toRemove = TimelineKeyframeViewModels.Where(t => !keyframes.Contains(t.BaseLayerPropertyKeyframe)).ToList();
TimelineKeyframeViewModels.RemoveRange(toRemove);
TimelineKeyframeViewModels.AddRange(
@@ -60,12 +62,15 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
{
foreach (var timelineKeyframeViewModel in TimelineKeyframeViewModels)
timelineKeyframeViewModel.Update(_profileEditorService.PixelsPerSecond);
+
+ Width = TimelineKeyframeViewModels.Any() ? TimelineKeyframeViewModels.Max(t => t.X) + 25 : 0;
}
}
public abstract class TimelinePropertyViewModel : PropertyChangedBase, IDisposable
{
private BindableCollection _timelineKeyframeViewModels;
+ private double _width;
protected TimelinePropertyViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel)
{
@@ -81,6 +86,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
set => SetAndNotify(ref _timelineKeyframeViewModels, value);
}
+ public double Width
+ {
+ get => _width;
+ set => SetAndNotify(ref _width, value);
+ }
+
public abstract void Dispose();
public abstract void UpdateKeyframes();
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 9645851d1..1e6549ac2 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineView.xaml
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineView.xaml
@@ -12,7 +12,8 @@
+ MouseMove="{s:Action TimelineCanvasMouseMove}"
+ HorizontalAlignment="Stretch">
@@ -31,12 +32,11 @@
-
+
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 f8dd39b8e..ba7135f71 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineViewModel.cs
@@ -18,7 +18,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
private readonly LayerPropertiesViewModel _layerPropertiesViewModel;
private readonly IProfileEditorService _profileEditorService;
private RectangleGeometry _selectionRectangle;
- private double _width;
public TimelineViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection layerPropertyGroups,
IProfileEditorService profileEditorService)
@@ -33,12 +32,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
public BindableCollection LayerPropertyGroups { get; }
- public double Width
- {
- get => _width;
- set => SetAndNotify(ref _width, value);
- }
-
public RectangleGeometry SelectionRectangle
{
get => _selectionRectangle;
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 c0470e579..6e4017456 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs
@@ -50,6 +50,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree
LayerPropertyViewModel.LayerProperty.AddKeyframe(new LayerPropertyKeyframe(
LayerPropertyViewModel.LayerProperty.CurrentValue,
_profileEditorService.CurrentTime,
+ _profileEditorService.CurrentTimeline,
Easings.Functions.Linear,
LayerPropertyViewModel.LayerProperty
));