diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs
index 9f26d5d28..acdf15530 100644
--- a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs
+++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs
@@ -103,6 +103,7 @@ namespace Artemis.Core
{
_disposed = true;
+ Registration.DataBinding = null;
DataBindingMode?.Dispose();
}
diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs
index cb495ca70..30062e553 100644
--- a/src/Artemis.Core/Models/Profile/Folder.cs
+++ b/src/Artemis.Core/Models/Profile/Folder.cs
@@ -57,6 +57,20 @@ namespace Artemis.Core
}
internal FolderEntity FolderEntity { get; set; }
+
+ ///
+ public override List GetAllLayerProperties()
+ {
+ var result = new List();
+ foreach (var layerEffect in LayerEffects)
+ {
+ if (layerEffect.BaseProperties != null)
+ result.AddRange(layerEffect.BaseProperties.GetAllLayerProperties());
+ }
+
+ return result;
+ }
+
internal override RenderElementEntity RenderElementEntity => FolderEntity;
public bool IsRootFolder => Parent == Profile;
@@ -88,6 +102,16 @@ namespace Artemis.Core
}
}
+ protected internal override void UpdateTimelineLength()
+ {
+ TimelineLength = !Children.Any() ? TimeSpan.Zero : Children.OfType().Max(c => c.TimelineLength);
+ if (StartSegmentLength + MainSegmentLength + EndSegmentLength > TimelineLength)
+ TimelineLength = StartSegmentLength + MainSegmentLength + EndSegmentLength;
+
+ if (Parent is RenderProfileElement parent)
+ parent.UpdateTimelineLength();
+ }
+
public override void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment)
{
if (_disposed)
@@ -201,6 +225,7 @@ namespace Artemis.Core
throw new ObjectDisposedException("Folder");
base.AddChild(child, order);
+ UpdateTimelineLength();
CalculateRenderProperties();
}
@@ -211,6 +236,7 @@ namespace Artemis.Core
throw new ObjectDisposedException("Folder");
base.RemoveChild(child);
+ UpdateTimelineLength();
CalculateRenderProperties();
}
diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index f1fa2cc3a..914e9de02 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -69,6 +69,24 @@ namespace Artemis.Core
}
internal LayerEntity LayerEntity { get; set; }
+
+ ///
+ public override List GetAllLayerProperties()
+ {
+ var result = new List();
+ result.AddRange(General.GetAllLayerProperties());
+ result.AddRange(Transform.GetAllLayerProperties());
+ if (LayerBrush?.BaseProperties != null)
+ result.AddRange(LayerBrush.BaseProperties.GetAllLayerProperties());
+ foreach (var layerEffect in LayerEffects)
+ {
+ if (layerEffect.BaseProperties != null)
+ result.AddRange(layerEffect.BaseProperties.GetAllLayerProperties());
+ }
+
+ return result;
+ }
+
internal override RenderElementEntity RenderElementEntity => LayerEntity;
///
@@ -269,6 +287,11 @@ namespace Artemis.Core
}
}
+ protected internal override void UpdateTimelineLength()
+ {
+ TimelineLength = StartSegmentLength + MainSegmentLength + EndSegmentLength;
+ }
+
public override void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment)
{
if (_disposed)
diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs
index 685972d67..aaa36796b 100644
--- a/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs
+++ b/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs
@@ -18,6 +18,11 @@ namespace Artemis.Core
///
public PropertyDescriptionAttribute PropertyDescription { get; }
+ ///
+ /// Gets the unique path of the property on the layer
+ ///
+ public string Path { get; }
+
///
/// Initializes the layer property
///
@@ -25,7 +30,7 @@ namespace Artemis.Core
///
///
///
- void Initialize(RenderProfileElement profileElement, LayerPropertyGroup group, PropertyEntity entity, bool fromStorage, PropertyDescriptionAttribute description);
+ void Initialize(RenderProfileElement profileElement, LayerPropertyGroup @group, PropertyEntity entity, bool fromStorage, PropertyDescriptionAttribute description, string path);
///
/// Returns a list off all data binding registrations
diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
index f6a7c03f1..b2403dc20 100644
--- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
+++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs
@@ -30,6 +30,9 @@ namespace Artemis.Core
///
public PropertyDescriptionAttribute PropertyDescription { get; internal set; }
+ ///
+ public string Path { get; private set; }
+
///
/// Updates the property, applying keyframes and data bindings to the current value
///
@@ -384,6 +387,8 @@ namespace Artemis.Core
if (dataBindingRegistration.LayerProperty != this)
throw new ArtemisCoreException("Cannot enable a data binding using a data binding registration of a different layer property");
+ if (dataBindingRegistration.DataBinding != null)
+ throw new ArtemisCoreException("Provided data binding registration already has an enabled data binding");
var dataBinding = new DataBinding(dataBindingRegistration);
_dataBindings.Add(dataBinding);
@@ -431,7 +436,7 @@ namespace Artemis.Core
internal PropertyEntity Entity { get; set; }
///
- public void Initialize(RenderProfileElement profileElement, LayerPropertyGroup group, PropertyEntity entity, bool fromStorage, PropertyDescriptionAttribute description)
+ public void Initialize(RenderProfileElement profileElement, LayerPropertyGroup group, PropertyEntity entity, bool fromStorage, PropertyDescriptionAttribute description, string path)
{
if (_disposed)
throw new ObjectDisposedException("LayerProperty");
@@ -440,6 +445,7 @@ namespace Artemis.Core
ProfileElement = profileElement ?? throw new ArgumentNullException(nameof(profileElement));
LayerPropertyGroup = group ?? throw new ArgumentNullException(nameof(group));
+ Path = path;
Entity = entity ?? throw new ArgumentNullException(nameof(entity));
PropertyDescription = description ?? throw new ArgumentNullException(nameof(description));
IsLoadedFromStorage = fromStorage;
diff --git a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
index b3cc9ac8e..4300b13a9 100644
--- a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
+++ b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
@@ -207,21 +207,21 @@ namespace Artemis.Core
private void InitializeProperty(PropertyInfo propertyInfo, PropertyDescriptionAttribute propertyDescription)
{
- var path = Path + ".";
+ var path = $"{Path}.{propertyInfo.Name}";
if (!typeof(ILayerProperty).IsAssignableFrom(propertyInfo.PropertyType))
- throw new ArtemisPluginException($"Layer property with PropertyDescription attribute must be of type LayerProperty at {path + propertyInfo.Name}");
+ throw new ArtemisPluginException($"Layer property with PropertyDescription attribute must be of type LayerProperty at {path}");
var instance = (ILayerProperty) Activator.CreateInstance(propertyInfo.PropertyType, true);
if (instance == null)
- throw new ArtemisPluginException($"Failed to create instance of layer property at {path + propertyInfo.Name}");
+ throw new ArtemisPluginException($"Failed to create instance of layer property at {path}");
// Ensure the description has a name, if not this is a good point to set it based on the property info
if (string.IsNullOrWhiteSpace(propertyDescription.Name))
propertyDescription.Name = propertyInfo.Name.Humanize();
- var entity = GetPropertyEntity(ProfileElement, path + propertyInfo.Name, out var fromStorage);
- instance.Initialize(ProfileElement, this, entity, fromStorage, propertyDescription);
+ var entity = GetPropertyEntity(ProfileElement, path, out var fromStorage);
+ instance.Initialize(ProfileElement, this, entity, fromStorage, propertyDescription, path);
propertyInfo.SetValue(this, instance);
_layerProperties.Add(instance);
}
diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
index e225f6b57..674126fcd 100644
--- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
+++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
@@ -19,6 +19,23 @@ namespace Artemis.Core
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
}
+ public abstract List GetAllLayerProperties();
+
+ #region IDisposable
+
+ protected override void Dispose(bool disposing)
+ {
+ LayerEffectStore.LayerEffectAdded -= LayerEffectStoreOnLayerEffectAdded;
+ LayerEffectStore.LayerEffectRemoved -= LayerEffectStoreOnLayerEffectRemoved;
+
+ foreach (var baseLayerEffect in LayerEffects)
+ baseLayerEffect.Dispose();
+
+ base.Dispose(disposing);
+ }
+
+ #endregion
+
internal void ApplyRenderElementDefaults()
{
MainSegmentLength = TimeSpan.FromSeconds(5);
@@ -135,7 +152,13 @@ namespace Artemis.Core
public TimeSpan StartSegmentLength
{
get => _startSegmentLength;
- set => SetAndNotify(ref _startSegmentLength, value);
+ set
+ {
+ if (!SetAndNotify(ref _startSegmentLength, value)) return;
+ UpdateTimelineLength();
+ if (Parent is RenderProfileElement renderElement)
+ renderElement.UpdateTimelineLength();
+ }
}
///
@@ -144,7 +167,13 @@ namespace Artemis.Core
public TimeSpan MainSegmentLength
{
get => _mainSegmentLength;
- set => SetAndNotify(ref _mainSegmentLength, value);
+ set
+ {
+ if (!SetAndNotify(ref _mainSegmentLength, value)) return;
+ UpdateTimelineLength();
+ if (Parent is RenderProfileElement renderElement)
+ renderElement.UpdateTimelineLength();
+ }
}
///
@@ -153,7 +182,13 @@ namespace Artemis.Core
public TimeSpan EndSegmentLength
{
get => _endSegmentLength;
- set => SetAndNotify(ref _endSegmentLength, value);
+ set
+ {
+ if (!SetAndNotify(ref _endSegmentLength, value)) return;
+ UpdateTimelineLength();
+ if (Parent is RenderProfileElement renderElement)
+ renderElement.UpdateTimelineLength();
+ }
}
///
@@ -165,11 +200,6 @@ namespace Artemis.Core
protected set => SetAndNotify(ref _timelinePosition, value);
}
- ///
- /// Gets the total combined length of all three segments
- ///
- public TimeSpan TimelineLength => StartSegmentLength + MainSegmentLength + EndSegmentLength;
-
///
/// Gets or sets whether main timeline should repeat itself as long as display conditions are met
///
@@ -188,6 +218,11 @@ namespace Artemis.Core
set => SetAndNotify(ref _alwaysFinishTimeline, value);
}
+ ///
+ /// Gets the max length of this element and any of its children
+ ///
+ public TimeSpan TimelineLength { get; protected set; }
+
protected double UpdateTimeline(double deltaTime)
{
var oldPosition = _timelinePosition;
@@ -195,7 +230,6 @@ namespace Artemis.Core
var mainSegmentEnd = StartSegmentLength + MainSegmentLength;
TimelinePosition += deltaTimeSpan;
-
// Manage segments while the condition is met
if (DisplayConditionMet)
{
@@ -213,6 +247,8 @@ namespace Artemis.Core
return (TimelinePosition - oldPosition).TotalSeconds;
}
+ protected internal abstract void UpdateTimelineLength();
+
///
/// Overrides the progress of the element
///
@@ -380,21 +416,6 @@ namespace Artemis.Core
#endregion
- #region IDisposable
-
- protected override void Dispose(bool disposing)
- {
- LayerEffectStore.LayerEffectAdded -= LayerEffectStoreOnLayerEffectAdded;
- LayerEffectStore.LayerEffectRemoved -= LayerEffectStoreOnLayerEffectRemoved;
-
- foreach (var baseLayerEffect in LayerEffects)
- baseLayerEffect.Dispose();
-
- base.Dispose(disposing);
- }
-
- #endregion
-
#region Events
public event EventHandler LayerEffectsUpdated;
diff --git a/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj
index 4f4a56b56..5c1368815 100644
--- a/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj
+++ b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj
@@ -35,7 +35,7 @@
-
+
diff --git a/src/Artemis.UI.Shared/Controls/ColorPicker.xaml b/src/Artemis.UI.Shared/Controls/ColorPicker.xaml
index 640f202f0..20c03d4e3 100644
--- a/src/Artemis.UI.Shared/Controls/ColorPicker.xaml
+++ b/src/Artemis.UI.Shared/Controls/ColorPicker.xaml
@@ -40,10 +40,9 @@
-
-
+
-
+
diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputView.xaml
index 90f94b311..46c9ade61 100644
--- a/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputView.xaml
+++ b/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputView.xaml
@@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:artemis="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:propertyInput="clr-namespace:Artemis.UI.PropertyInput"
+ xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d"
d:DesignHeight="25" d:DesignWidth="800"
d:DataContext="{d:DesignInstance propertyInput:SKColorPropertyInputViewModel}">
@@ -17,7 +18,9 @@
Margin="0 -2 0 3"
Padding="0 -1"
Color="{Binding InputValue, Converter={StaticResource SKColorToColorConverter}}"
- IsEnabled="{Binding IsEnabled}"/>
+ IsEnabled="{Binding IsEnabled}"
+ DragStarted="{s:Action InputDragStarted}"
+ DragEnded="{s:Action InputDragEnded}"/>
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs
index beccd44bd..72fbefd50 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs
@@ -20,8 +20,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
private DataBindingModeType _selectedDataBindingMode;
private TimelineEasingViewModel _selectedEasingViewModel;
- private TProperty _testInputValue;
- private TProperty _testResultValue;
private bool _updating;
private bool _isDataBindingEnabled;
@@ -45,8 +43,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
TestInputValue = _dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), true);
TestResultValue = _dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), true);
- DataBinding = Registration.DataBinding;
-
Initialize();
}
@@ -103,12 +99,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
}
}
- public DataBinding DataBinding
- {
- get => _dataBinding;
- set => SetAndNotify(ref _dataBinding, value);
- }
-
public void Dispose()
{
_profileEditorService.ProfilePreviewUpdated -= ProfileEditorServiceOnProfilePreviewUpdated;
@@ -125,13 +115,13 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
private void CreateDataBindingModeModeViewModel()
{
- if (DataBinding?.DataBindingMode == null)
+ if (Registration.DataBinding?.DataBindingMode == null)
{
ActiveItem = null;
return;
}
- switch (DataBinding.DataBindingMode)
+ switch (Registration.DataBinding.DataBindingMode)
{
case DirectDataBinding directDataBinding:
ActiveItem = _dataBindingsVmFactory.DirectDataBindingModeViewModel(directDataBinding);
@@ -147,7 +137,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
if (_updating)
return;
- if (DataBinding == null)
+ if (Registration.DataBinding == null)
{
IsEasingTimeEnabled = false;
return;
@@ -156,10 +146,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
_updating = true;
IsDataBindingEnabled = ActiveItem != null;
- EasingTime = (int) DataBinding.EasingTime.TotalMilliseconds;
- SelectedEasingViewModel = EasingViewModels.First(vm => vm.EasingFunction == DataBinding.EasingFunction);
+ EasingTime = (int) Registration.DataBinding.EasingTime.TotalMilliseconds;
+ SelectedEasingViewModel = EasingViewModels.First(vm => vm.EasingFunction == Registration.DataBinding.EasingFunction);
IsEasingTimeEnabled = EasingTime > 0;
- switch (DataBinding.DataBindingMode)
+ switch (Registration.DataBinding.DataBindingMode)
{
case DirectDataBinding _:
SelectedDataBindingMode = DataBindingModeType.Direct;
@@ -182,10 +172,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
if (_updating)
return;
- if (DataBinding != null)
+ if (Registration.DataBinding != null)
{
- DataBinding.EasingTime = TimeSpan.FromMilliseconds(EasingTime);
- DataBinding.EasingFunction = SelectedEasingViewModel?.EasingFunction ?? Easings.Functions.Linear;
+ Registration.DataBinding.EasingTime = TimeSpan.FromMilliseconds(EasingTime);
+ Registration.DataBinding.EasingFunction = SelectedEasingViewModel?.EasingFunction ?? Easings.Functions.Linear;
}
_profileEditorService.UpdateSelectedProfileElement();
@@ -197,17 +187,17 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
if (_updating)
return;
- if (DataBinding != null && SelectedDataBindingMode == DataBindingModeType.None)
+ if (Registration.DataBinding != null && SelectedDataBindingMode == DataBindingModeType.None)
{
RemoveDataBinding();
CreateDataBindingModeModeViewModel();
return;
}
- if (DataBinding == null && SelectedDataBindingMode != DataBindingModeType.None)
+ if (Registration.DataBinding == null && SelectedDataBindingMode != DataBindingModeType.None)
EnableDataBinding();
- DataBinding.ChangeDataBindingMode(SelectedDataBindingMode);
+ Registration.DataBinding.ChangeDataBindingMode(SelectedDataBindingMode);
CreateDataBindingModeModeViewModel();
_profileEditorService.UpdateSelectedProfileElement();
@@ -215,7 +205,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
private void UpdateTestResult()
{
- if (DataBinding == null)
+ if (Registration.DataBinding == null)
{
TestInputValue.UpdateValue(default);
TestResultValue.UpdateValue(default);
@@ -225,26 +215,24 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
var currentValue = Registration.Converter.ConvertFromObject(ActiveItem?.GetTestValue() ?? default(TProperty));
TestInputValue.UpdateValue(currentValue);
- TestResultValue.UpdateValue(DataBinding != null ? DataBinding.GetValue(currentValue) : default);
+ TestResultValue.UpdateValue(Registration.DataBinding != null ? Registration.DataBinding.GetValue(currentValue) : default);
}
private void EnableDataBinding()
{
- if (DataBinding != null)
+ if (Registration.DataBinding != null)
return;
- DataBinding = Registration.LayerProperty.EnableDataBinding(Registration);
+ Registration.LayerProperty.EnableDataBinding(Registration);
_profileEditorService.UpdateSelectedProfileElement();
}
private void RemoveDataBinding()
{
- if (DataBinding == null)
+ if (Registration.DataBinding == null)
return;
- var toDisable = DataBinding;
- DataBinding = null;
- Registration.LayerProperty.DisableDataBinding(toDisable);
+ Registration.LayerProperty.DisableDataBinding(Registration.DataBinding);
Update();
_profileEditorService.UpdateSelectedProfileElement();
diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Dialogs/TimelineSegmentDialogView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Dialogs/TimelineSegmentDialogView.xaml
new file mode 100644
index 000000000..4a4c6da38
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Dialogs/TimelineSegmentDialogView.xaml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Dialogs/TimelineSegmentDialogViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Dialogs/TimelineSegmentDialogViewModel.cs
new file mode 100644
index 000000000..91ee090ee
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Dialogs/TimelineSegmentDialogViewModel.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Globalization;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Artemis.UI.Shared.Services;
+using Castle.Core.Internal;
+using FluentValidation;
+using Stylet;
+
+namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline.Dialogs
+{
+ public class TimelineSegmentDialogViewModel : DialogViewModelBase
+ {
+ private string _inputValue;
+
+ public TimelineSegmentDialogViewModel(IModelValidator validator, TimelineSegmentViewModel segment)
+ : base(validator)
+ {
+ Segment = segment;
+ InputValue = $"{Math.Floor(Segment.SegmentLength.TotalSeconds):00}.{Segment.SegmentLength.Milliseconds:000}";
+ }
+
+ public TimelineSegmentViewModel Segment { get; }
+
+ public string InputValue
+ {
+ get => _inputValue;
+ set => SetAndNotify(ref _inputValue, value);
+ }
+
+ public async Task Accept()
+ {
+ await ValidateAsync();
+
+ if (HasErrors)
+ return;
+
+ Segment.UpdateLength(TimelineSegmentDialogViewModelValidator.CreateTime(InputValue));
+ Session.Close();
+ }
+
+ public void Cancel()
+ {
+ Session.Close();
+ }
+ }
+
+ public class TimelineSegmentDialogViewModelValidator : AbstractValidator
+ {
+ private readonly Regex _inputRegex = new Regex("^[.][-|0-9]+$|^-?[0-9]*[.]{0,1}[0-9]*$");
+
+ public TimelineSegmentDialogViewModelValidator()
+ {
+ CascadeMode = CascadeMode.Stop;
+
+ RuleFor(m => m.InputValue)
+ .NotNull()
+ .WithMessage("A timeline length is required");
+ RuleFor(m => m.InputValue)
+ .Must(ValidateTime)
+ .WithMessage("Input cannot be converted to a time");
+ RuleFor(m => m.InputValue)
+ .Transform(CreateTime)
+ .GreaterThanOrEqualTo(TimeSpan.FromMilliseconds(100))
+ .WithMessage("Minimum timeline length is 100ms");
+ RuleFor(m => m.InputValue)
+ .Transform(CreateTime)
+ .LessThanOrEqualTo(TimeSpan.FromHours(24))
+ .WithMessage("Maximum timeline length is 24 hours");
+ }
+
+ public static TimeSpan CreateTime(string s)
+ {
+ var parts = s.Split(".");
+
+ // Only seconds provided
+ if (parts.Length == 1)
+ return TimeSpan.FromSeconds(double.Parse(parts[0]));
+ // Only milliseconds provided with a leading .
+ if (parts[0].IsNullOrEmpty())
+ {
+ // Add trailing zeros so 2.5 becomes 2.500, can't seem to make double.Parse do that
+ while (parts[0].Length < 3) parts[0] += "0";
+ return TimeSpan.FromMilliseconds(double.Parse(parts[1], CultureInfo.InvariantCulture));
+ }
+
+ // Seconds and milliseconds provided
+ // Add trailing zeros so 2.5 becomes 2.500, can't seem to make double.Parse do that
+ while (parts[1].Length < 3) parts[1] += "0";
+ return TimeSpan.FromSeconds(double.Parse(parts[0])).Add(TimeSpan.FromMilliseconds(double.Parse(parts[1])));
+ }
+
+ private bool ValidateTime(string arg)
+ {
+ return _inputRegex.IsMatch(arg);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineSegmentView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineSegmentView.xaml
index 3b7338326..9c5b5d42d 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineSegmentView.xaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineSegmentView.xaml
@@ -16,11 +16,21 @@
+
-
+
+
diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineSegmentViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineSegmentViewModel.cs
index 06bc9b6be..78e651ac0 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineSegmentViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineSegmentViewModel.cs
@@ -1,10 +1,14 @@
using System;
+using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
+using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Artemis.Core;
+using Artemis.UI.Screens.ProfileEditor.Dialogs;
+using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline.Dialogs;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Stylet;
@@ -13,6 +17,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
{
public class TimelineSegmentViewModel : Screen, IDisposable
{
+ private readonly IDialogService _dialogService;
private bool _draggingSegment;
private bool _showDisableButton;
private bool _showRepeatButton;
@@ -25,8 +30,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
private double _segmentStartPosition;
public TimelineSegmentViewModel(SegmentViewModelType segment, BindableCollection layerPropertyGroups,
- IProfileEditorService profileEditorService)
+ IProfileEditorService profileEditorService, IDialogService dialogService)
{
+ _dialogService = dialogService;
ProfileEditorService = profileEditorService;
Segment = segment;
LayerPropertyGroups = layerPropertyGroups;
@@ -130,6 +136,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
set => SetAndNotify(ref _showDisableButton, value);
}
+ public async Task OpenSettingsDialog()
+ {
+ await _dialogService.ShowDialog(new Dictionary {{"segment", this}});
+ }
+
#region Updating
private void UpdateHeader()
@@ -276,6 +287,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 50.0) * 50.0);
+ UpdateLength(newTime);
+ }
+
+ public void UpdateLength(TimeSpan newTime)
+ {
var oldSegmentLength = SegmentLength;
if (Segment == SegmentViewModelType.Start)
{