mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Folders - When display mode set to finish, finish all child timelines
Data bindings - Fixed an exception on profile editor undo Timeline - Added segment options dialog where you can enter a time
This commit is contained in:
parent
9f951ca33f
commit
bfc93778a6
@ -103,6 +103,7 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|
||||||
|
Registration.DataBinding = null;
|
||||||
DataBindingMode?.Dispose();
|
DataBindingMode?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -57,6 +57,20 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal FolderEntity FolderEntity { get; set; }
|
internal FolderEntity FolderEntity { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override List<ILayerProperty> GetAllLayerProperties()
|
||||||
|
{
|
||||||
|
var result = new List<ILayerProperty>();
|
||||||
|
foreach (var layerEffect in LayerEffects)
|
||||||
|
{
|
||||||
|
if (layerEffect.BaseProperties != null)
|
||||||
|
result.AddRange(layerEffect.BaseProperties.GetAllLayerProperties());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
internal override RenderElementEntity RenderElementEntity => FolderEntity;
|
internal override RenderElementEntity RenderElementEntity => FolderEntity;
|
||||||
public bool IsRootFolder => Parent == Profile;
|
public bool IsRootFolder => Parent == Profile;
|
||||||
|
|
||||||
@ -88,6 +102,16 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected internal override void UpdateTimelineLength()
|
||||||
|
{
|
||||||
|
TimelineLength = !Children.Any() ? TimeSpan.Zero : Children.OfType<RenderProfileElement>().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)
|
public override void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
@ -201,6 +225,7 @@ namespace Artemis.Core
|
|||||||
throw new ObjectDisposedException("Folder");
|
throw new ObjectDisposedException("Folder");
|
||||||
|
|
||||||
base.AddChild(child, order);
|
base.AddChild(child, order);
|
||||||
|
UpdateTimelineLength();
|
||||||
CalculateRenderProperties();
|
CalculateRenderProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,6 +236,7 @@ namespace Artemis.Core
|
|||||||
throw new ObjectDisposedException("Folder");
|
throw new ObjectDisposedException("Folder");
|
||||||
|
|
||||||
base.RemoveChild(child);
|
base.RemoveChild(child);
|
||||||
|
UpdateTimelineLength();
|
||||||
CalculateRenderProperties();
|
CalculateRenderProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -69,6 +69,24 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal LayerEntity LayerEntity { get; set; }
|
internal LayerEntity LayerEntity { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override List<ILayerProperty> GetAllLayerProperties()
|
||||||
|
{
|
||||||
|
var result = new List<ILayerProperty>();
|
||||||
|
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;
|
internal override RenderElementEntity RenderElementEntity => LayerEntity;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -269,6 +287,11 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected internal override void UpdateTimelineLength()
|
||||||
|
{
|
||||||
|
TimelineLength = StartSegmentLength + MainSegmentLength + EndSegmentLength;
|
||||||
|
}
|
||||||
|
|
||||||
public override void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment)
|
public override void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
|
|||||||
@ -18,6 +18,11 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public PropertyDescriptionAttribute PropertyDescription { get; }
|
public PropertyDescriptionAttribute PropertyDescription { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the unique path of the property on the layer
|
||||||
|
/// </summary>
|
||||||
|
public string Path { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the layer property
|
/// Initializes the layer property
|
||||||
/// <para>
|
/// <para>
|
||||||
@ -25,7 +30,7 @@ namespace Artemis.Core
|
|||||||
/// <see cref="LayerProperty{T}" />
|
/// <see cref="LayerProperty{T}" />
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
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);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a list off all data binding registrations
|
/// Returns a list off all data binding registrations
|
||||||
|
|||||||
@ -30,6 +30,9 @@ namespace Artemis.Core
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public PropertyDescriptionAttribute PropertyDescription { get; internal set; }
|
public PropertyDescriptionAttribute PropertyDescription { get; internal set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Path { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the property, applying keyframes and data bindings to the current value
|
/// Updates the property, applying keyframes and data bindings to the current value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -384,6 +387,8 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
if (dataBindingRegistration.LayerProperty != this)
|
if (dataBindingRegistration.LayerProperty != this)
|
||||||
throw new ArtemisCoreException("Cannot enable a data binding using a data binding registration of a different layer property");
|
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<T, TProperty>(dataBindingRegistration);
|
var dataBinding = new DataBinding<T, TProperty>(dataBindingRegistration);
|
||||||
_dataBindings.Add(dataBinding);
|
_dataBindings.Add(dataBinding);
|
||||||
@ -431,7 +436,7 @@ namespace Artemis.Core
|
|||||||
internal PropertyEntity Entity { get; set; }
|
internal PropertyEntity Entity { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
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)
|
if (_disposed)
|
||||||
throw new ObjectDisposedException("LayerProperty");
|
throw new ObjectDisposedException("LayerProperty");
|
||||||
@ -440,6 +445,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
ProfileElement = profileElement ?? throw new ArgumentNullException(nameof(profileElement));
|
ProfileElement = profileElement ?? throw new ArgumentNullException(nameof(profileElement));
|
||||||
LayerPropertyGroup = group ?? throw new ArgumentNullException(nameof(group));
|
LayerPropertyGroup = group ?? throw new ArgumentNullException(nameof(group));
|
||||||
|
Path = path;
|
||||||
Entity = entity ?? throw new ArgumentNullException(nameof(entity));
|
Entity = entity ?? throw new ArgumentNullException(nameof(entity));
|
||||||
PropertyDescription = description ?? throw new ArgumentNullException(nameof(description));
|
PropertyDescription = description ?? throw new ArgumentNullException(nameof(description));
|
||||||
IsLoadedFromStorage = fromStorage;
|
IsLoadedFromStorage = fromStorage;
|
||||||
|
|||||||
@ -207,21 +207,21 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
private void InitializeProperty(PropertyInfo propertyInfo, PropertyDescriptionAttribute propertyDescription)
|
private void InitializeProperty(PropertyInfo propertyInfo, PropertyDescriptionAttribute propertyDescription)
|
||||||
{
|
{
|
||||||
var path = Path + ".";
|
var path = $"{Path}.{propertyInfo.Name}";
|
||||||
|
|
||||||
if (!typeof(ILayerProperty).IsAssignableFrom(propertyInfo.PropertyType))
|
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);
|
var instance = (ILayerProperty) Activator.CreateInstance(propertyInfo.PropertyType, true);
|
||||||
if (instance == null)
|
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
|
// 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))
|
if (string.IsNullOrWhiteSpace(propertyDescription.Name))
|
||||||
propertyDescription.Name = propertyInfo.Name.Humanize();
|
propertyDescription.Name = propertyInfo.Name.Humanize();
|
||||||
|
|
||||||
var entity = GetPropertyEntity(ProfileElement, path + propertyInfo.Name, out var fromStorage);
|
var entity = GetPropertyEntity(ProfileElement, path, out var fromStorage);
|
||||||
instance.Initialize(ProfileElement, this, entity, fromStorage, propertyDescription);
|
instance.Initialize(ProfileElement, this, entity, fromStorage, propertyDescription, path);
|
||||||
propertyInfo.SetValue(this, instance);
|
propertyInfo.SetValue(this, instance);
|
||||||
_layerProperties.Add(instance);
|
_layerProperties.Add(instance);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,23 @@ namespace Artemis.Core
|
|||||||
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
|
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract List<ILayerProperty> 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()
|
internal void ApplyRenderElementDefaults()
|
||||||
{
|
{
|
||||||
MainSegmentLength = TimeSpan.FromSeconds(5);
|
MainSegmentLength = TimeSpan.FromSeconds(5);
|
||||||
@ -135,7 +152,13 @@ namespace Artemis.Core
|
|||||||
public TimeSpan StartSegmentLength
|
public TimeSpan StartSegmentLength
|
||||||
{
|
{
|
||||||
get => _startSegmentLength;
|
get => _startSegmentLength;
|
||||||
set => SetAndNotify(ref _startSegmentLength, value);
|
set
|
||||||
|
{
|
||||||
|
if (!SetAndNotify(ref _startSegmentLength, value)) return;
|
||||||
|
UpdateTimelineLength();
|
||||||
|
if (Parent is RenderProfileElement renderElement)
|
||||||
|
renderElement.UpdateTimelineLength();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -144,7 +167,13 @@ namespace Artemis.Core
|
|||||||
public TimeSpan MainSegmentLength
|
public TimeSpan MainSegmentLength
|
||||||
{
|
{
|
||||||
get => _mainSegmentLength;
|
get => _mainSegmentLength;
|
||||||
set => SetAndNotify(ref _mainSegmentLength, value);
|
set
|
||||||
|
{
|
||||||
|
if (!SetAndNotify(ref _mainSegmentLength, value)) return;
|
||||||
|
UpdateTimelineLength();
|
||||||
|
if (Parent is RenderProfileElement renderElement)
|
||||||
|
renderElement.UpdateTimelineLength();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -153,7 +182,13 @@ namespace Artemis.Core
|
|||||||
public TimeSpan EndSegmentLength
|
public TimeSpan EndSegmentLength
|
||||||
{
|
{
|
||||||
get => _endSegmentLength;
|
get => _endSegmentLength;
|
||||||
set => SetAndNotify(ref _endSegmentLength, value);
|
set
|
||||||
|
{
|
||||||
|
if (!SetAndNotify(ref _endSegmentLength, value)) return;
|
||||||
|
UpdateTimelineLength();
|
||||||
|
if (Parent is RenderProfileElement renderElement)
|
||||||
|
renderElement.UpdateTimelineLength();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -165,11 +200,6 @@ namespace Artemis.Core
|
|||||||
protected set => SetAndNotify(ref _timelinePosition, value);
|
protected set => SetAndNotify(ref _timelinePosition, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the total combined length of all three segments
|
|
||||||
/// </summary>
|
|
||||||
public TimeSpan TimelineLength => StartSegmentLength + MainSegmentLength + EndSegmentLength;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets whether main timeline should repeat itself as long as display conditions are met
|
/// Gets or sets whether main timeline should repeat itself as long as display conditions are met
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -188,6 +218,11 @@ namespace Artemis.Core
|
|||||||
set => SetAndNotify(ref _alwaysFinishTimeline, value);
|
set => SetAndNotify(ref _alwaysFinishTimeline, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the max length of this element and any of its children
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan TimelineLength { get; protected set; }
|
||||||
|
|
||||||
protected double UpdateTimeline(double deltaTime)
|
protected double UpdateTimeline(double deltaTime)
|
||||||
{
|
{
|
||||||
var oldPosition = _timelinePosition;
|
var oldPosition = _timelinePosition;
|
||||||
@ -195,7 +230,6 @@ namespace Artemis.Core
|
|||||||
var mainSegmentEnd = StartSegmentLength + MainSegmentLength;
|
var mainSegmentEnd = StartSegmentLength + MainSegmentLength;
|
||||||
|
|
||||||
TimelinePosition += deltaTimeSpan;
|
TimelinePosition += deltaTimeSpan;
|
||||||
|
|
||||||
// Manage segments while the condition is met
|
// Manage segments while the condition is met
|
||||||
if (DisplayConditionMet)
|
if (DisplayConditionMet)
|
||||||
{
|
{
|
||||||
@ -213,6 +247,8 @@ namespace Artemis.Core
|
|||||||
return (TimelinePosition - oldPosition).TotalSeconds;
|
return (TimelinePosition - oldPosition).TotalSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected internal abstract void UpdateTimelineLength();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Overrides the progress of the element
|
/// Overrides the progress of the element
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -380,21 +416,6 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
#endregion
|
#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
|
#region Events
|
||||||
|
|
||||||
public event EventHandler LayerEffectsUpdated;
|
public event EventHandler LayerEffectsUpdated;
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AvalonEdit" Version="6.0.1" />
|
<PackageReference Include="AvalonEdit" Version="6.0.1" />
|
||||||
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
|
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
|
||||||
<PackageReference Include="MaterialDesignExtensions" Version="3.1.0" />
|
<PackageReference Include="MaterialDesignExtensions" Version="3.2.0" />
|
||||||
<PackageReference Include="MaterialDesignThemes" Version="3.1.3" />
|
<PackageReference Include="MaterialDesignThemes" Version="3.1.3" />
|
||||||
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.19" />
|
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.19" />
|
||||||
<PackageReference Include="Ninject" Version="3.3.4" />
|
<PackageReference Include="Ninject" Version="3.3.4" />
|
||||||
|
|||||||
@ -40,10 +40,9 @@
|
|||||||
</Border.Background>
|
</Border.Background>
|
||||||
</Border>
|
</Border>
|
||||||
</Border>
|
</Border>
|
||||||
<Track
|
<Track x:Name="PART_Track"
|
||||||
x:Name="PART_Track"
|
Grid.Row="1"
|
||||||
Grid.Row="1"
|
OpacityMask="{x:Null}">
|
||||||
OpacityMask="{x:Null}">
|
|
||||||
<Track.DecreaseRepeatButton>
|
<Track.DecreaseRepeatButton>
|
||||||
<RepeatButton Command="{x:Static Slider.DecreaseLarge}" Style="{DynamicResource MaterialDesignHorizontalColorSliderTrackRepeatButton}" />
|
<RepeatButton Command="{x:Static Slider.DecreaseLarge}" Style="{DynamicResource MaterialDesignHorizontalColorSliderTrackRepeatButton}" />
|
||||||
</Track.DecreaseRepeatButton>
|
</Track.DecreaseRepeatButton>
|
||||||
@ -101,13 +100,19 @@
|
|||||||
<RowDefinition Height="160" />
|
<RowDefinition Height="160" />
|
||||||
<RowDefinition />
|
<RowDefinition />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<materialDesign:ColorPicker Grid.Row="0" Color="{Binding Color, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" MouseUp="ColorGradient_OnMouseUp" />
|
<materialDesign:ColorPicker Grid.Row="0"
|
||||||
|
Color="{Binding Color, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
|
||||||
|
MouseUp="ColorGradient_OnMouseUp"
|
||||||
|
PreviewMouseDown="Slider_OnMouseDown"
|
||||||
|
PreviewMouseUp="Slider_OnMouseUp"/>
|
||||||
<Slider Grid.Row="1" Margin="8"
|
<Slider Grid.Row="1" Margin="8"
|
||||||
IsMoveToPointEnabled="True"
|
IsMoveToPointEnabled="True"
|
||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
Style="{DynamicResource MaterialDesignColorSlider}"
|
Style="{DynamicResource MaterialDesignColorSlider}"
|
||||||
Template="{StaticResource MaterialDesignOpacitySlider}"
|
Template="{StaticResource MaterialDesignOpacitySlider}"
|
||||||
Value="{Binding ColorOpacity, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
|
Value="{Binding ColorOpacity, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
|
||||||
|
PreviewMouseDown="Slider_OnMouseDown"
|
||||||
|
PreviewMouseUp="Slider_OnMouseUp"
|
||||||
Maximum="255" />
|
Maximum="255" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</materialDesign:Card>
|
</materialDesign:Card>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel;
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
@ -134,10 +135,38 @@ namespace Artemis.UI.Shared
|
|||||||
PopupOpen = !PopupOpen;
|
PopupOpen = !PopupOpen;
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ColorGradient_OnMouseUp(object sender, MouseButtonEventArgs e)
|
private void ColorGradient_OnMouseUp(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Slider_OnMouseDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
OnDragStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Slider_OnMouseUp(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
OnDragEnded();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
public event EventHandler DragStarted;
|
||||||
|
public event EventHandler DragEnded;
|
||||||
|
|
||||||
|
protected virtual void OnDragStarted()
|
||||||
|
{
|
||||||
|
DragStarted?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnDragEnded()
|
||||||
|
{
|
||||||
|
DragEnded?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,23 +154,7 @@ namespace Artemis.UI.Shared.Services
|
|||||||
if (!undid)
|
if (!undid)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (SelectedProfileElement is Folder folder)
|
ReloadProfile();
|
||||||
SelectedProfileElement = SelectedProfile.GetAllFolders().FirstOrDefault(f => f.EntityId == folder.EntityId);
|
|
||||||
else if (SelectedProfileElement is Layer layer)
|
|
||||||
SelectedProfileElement = SelectedProfile.GetAllLayers().FirstOrDefault(l => l.EntityId == layer.EntityId);
|
|
||||||
|
|
||||||
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile, SelectedProfile));
|
|
||||||
OnSelectedProfileElementChanged(new RenderProfileElementEventArgs(SelectedProfileElement));
|
|
||||||
|
|
||||||
if (SelectedProfileElement != null)
|
|
||||||
{
|
|
||||||
var elements = SelectedProfile.GetAllLayers().Cast<RenderProfileElement>().ToList();
|
|
||||||
elements.AddRange(SelectedProfile.GetAllFolders());
|
|
||||||
var element = elements.FirstOrDefault(l => l.EntityId == SelectedProfileElement.EntityId);
|
|
||||||
ChangeSelectedProfileElement(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateProfilePreview();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,17 +164,7 @@ namespace Artemis.UI.Shared.Services
|
|||||||
if (!redid)
|
if (!redid)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile, SelectedProfile));
|
ReloadProfile();
|
||||||
|
|
||||||
if (SelectedProfileElement != null)
|
|
||||||
{
|
|
||||||
var elements = SelectedProfile.GetAllLayers().Cast<RenderProfileElement>().ToList();
|
|
||||||
elements.AddRange(SelectedProfile.GetAllFolders());
|
|
||||||
var element = elements.FirstOrDefault(l => l.EntityId == SelectedProfileElement.EntityId);
|
|
||||||
ChangeSelectedProfileElement(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateProfilePreview();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,6 +281,27 @@ namespace Artemis.UI.Shared.Services
|
|||||||
return SelectedProfile?.Module;
|
return SelectedProfile?.Module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ReloadProfile()
|
||||||
|
{
|
||||||
|
// Trigger a profile change
|
||||||
|
OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile, SelectedProfile));
|
||||||
|
// Trigger a selected element change
|
||||||
|
var previousSelectedProfileElement = SelectedProfileElement;
|
||||||
|
if (SelectedProfileElement is Folder folder)
|
||||||
|
SelectedProfileElement = SelectedProfile.GetAllFolders().FirstOrDefault(f => f.EntityId == folder.EntityId);
|
||||||
|
else if (SelectedProfileElement is Layer layer)
|
||||||
|
SelectedProfileElement = SelectedProfile.GetAllLayers().FirstOrDefault(l => l.EntityId == layer.EntityId);
|
||||||
|
OnSelectedProfileElementChanged(new RenderProfileElementEventArgs(SelectedProfileElement, previousSelectedProfileElement));
|
||||||
|
// Trigger selected data binding change
|
||||||
|
if (SelectedDataBinding != null)
|
||||||
|
{
|
||||||
|
SelectedDataBinding = SelectedProfileElement?.GetAllLayerProperties()?.FirstOrDefault(p => p.Path == SelectedDataBinding.Path);
|
||||||
|
OnSelectedDataBindingChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateProfilePreview();
|
||||||
|
}
|
||||||
|
|
||||||
public event EventHandler<ProfileEventArgs> ProfileSelected;
|
public event EventHandler<ProfileEventArgs> ProfileSelected;
|
||||||
public event EventHandler<ProfileEventArgs> SelectedProfileUpdated;
|
public event EventHandler<ProfileEventArgs> SelectedProfileUpdated;
|
||||||
public event EventHandler<RenderProfileElementEventArgs> ProfileElementSelected;
|
public event EventHandler<RenderProfileElementEventArgs> ProfileElementSelected;
|
||||||
|
|||||||
@ -132,11 +132,11 @@
|
|||||||
<Resource Include="Resources\Images\Sidebar\sidebar-header.png" />
|
<Resource Include="Resources\Images\Sidebar\sidebar-header.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentValidation" Version="8.6.2" />
|
<PackageReference Include="FluentValidation" Version="9.2.0" />
|
||||||
<PackageReference Include="gong-wpf-dragdrop" Version="2.2.0" />
|
<PackageReference Include="gong-wpf-dragdrop" Version="2.2.0" />
|
||||||
<PackageReference Include="Hardcodet.NotifyIcon.Wpf.NetCore" Version="1.0.11" />
|
<PackageReference Include="Hardcodet.NotifyIcon.Wpf.NetCore" Version="1.0.11" />
|
||||||
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
|
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
|
||||||
<PackageReference Include="MaterialDesignExtensions" Version="3.1.0" />
|
<PackageReference Include="MaterialDesignExtensions" Version="3.2.0" />
|
||||||
<PackageReference Include="MaterialDesignThemes" Version="3.1.3" />
|
<PackageReference Include="MaterialDesignThemes" Version="3.1.3" />
|
||||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
|
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
|
||||||
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.19" />
|
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.19" />
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:artemis="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
xmlns:artemis="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||||
xmlns:propertyInput="clr-namespace:Artemis.UI.PropertyInput"
|
xmlns:propertyInput="clr-namespace:Artemis.UI.PropertyInput"
|
||||||
|
xmlns:s="https://github.com/canton7/Stylet"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="25" d:DesignWidth="800"
|
d:DesignHeight="25" d:DesignWidth="800"
|
||||||
d:DataContext="{d:DesignInstance propertyInput:SKColorPropertyInputViewModel}">
|
d:DataContext="{d:DesignInstance propertyInput:SKColorPropertyInputViewModel}">
|
||||||
@ -17,7 +18,9 @@
|
|||||||
Margin="0 -2 0 3"
|
Margin="0 -2 0 3"
|
||||||
Padding="0 -1"
|
Padding="0 -1"
|
||||||
Color="{Binding InputValue, Converter={StaticResource SKColorToColorConverter}}"
|
Color="{Binding InputValue, Converter={StaticResource SKColorToColorConverter}}"
|
||||||
IsEnabled="{Binding IsEnabled}"/>
|
IsEnabled="{Binding IsEnabled}"
|
||||||
|
DragStarted="{s:Action InputDragStarted}"
|
||||||
|
DragEnded="{s:Action InputDragEnded}"/>
|
||||||
<TextBlock Width="10" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" />
|
<TextBlock Width="10" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -20,8 +20,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
|||||||
private DataBindingModeType _selectedDataBindingMode;
|
private DataBindingModeType _selectedDataBindingMode;
|
||||||
private TimelineEasingViewModel _selectedEasingViewModel;
|
private TimelineEasingViewModel _selectedEasingViewModel;
|
||||||
|
|
||||||
private TProperty _testInputValue;
|
|
||||||
private TProperty _testResultValue;
|
|
||||||
private bool _updating;
|
private bool _updating;
|
||||||
private bool _isDataBindingEnabled;
|
private bool _isDataBindingEnabled;
|
||||||
|
|
||||||
@ -45,8 +43,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
|||||||
TestInputValue = _dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), true);
|
TestInputValue = _dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), true);
|
||||||
TestResultValue = _dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), true);
|
TestResultValue = _dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), true);
|
||||||
|
|
||||||
DataBinding = Registration.DataBinding;
|
|
||||||
|
|
||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,12 +99,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataBinding<TLayerProperty, TProperty> DataBinding
|
|
||||||
{
|
|
||||||
get => _dataBinding;
|
|
||||||
set => SetAndNotify(ref _dataBinding, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_profileEditorService.ProfilePreviewUpdated -= ProfileEditorServiceOnProfilePreviewUpdated;
|
_profileEditorService.ProfilePreviewUpdated -= ProfileEditorServiceOnProfilePreviewUpdated;
|
||||||
@ -125,13 +115,13 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
|||||||
|
|
||||||
private void CreateDataBindingModeModeViewModel()
|
private void CreateDataBindingModeModeViewModel()
|
||||||
{
|
{
|
||||||
if (DataBinding?.DataBindingMode == null)
|
if (Registration.DataBinding?.DataBindingMode == null)
|
||||||
{
|
{
|
||||||
ActiveItem = null;
|
ActiveItem = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (DataBinding.DataBindingMode)
|
switch (Registration.DataBinding.DataBindingMode)
|
||||||
{
|
{
|
||||||
case DirectDataBinding<TLayerProperty, TProperty> directDataBinding:
|
case DirectDataBinding<TLayerProperty, TProperty> directDataBinding:
|
||||||
ActiveItem = _dataBindingsVmFactory.DirectDataBindingModeViewModel(directDataBinding);
|
ActiveItem = _dataBindingsVmFactory.DirectDataBindingModeViewModel(directDataBinding);
|
||||||
@ -147,7 +137,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
|||||||
if (_updating)
|
if (_updating)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (DataBinding == null)
|
if (Registration.DataBinding == null)
|
||||||
{
|
{
|
||||||
IsEasingTimeEnabled = false;
|
IsEasingTimeEnabled = false;
|
||||||
return;
|
return;
|
||||||
@ -156,10 +146,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
|||||||
_updating = true;
|
_updating = true;
|
||||||
|
|
||||||
IsDataBindingEnabled = ActiveItem != null;
|
IsDataBindingEnabled = ActiveItem != null;
|
||||||
EasingTime = (int) DataBinding.EasingTime.TotalMilliseconds;
|
EasingTime = (int) Registration.DataBinding.EasingTime.TotalMilliseconds;
|
||||||
SelectedEasingViewModel = EasingViewModels.First(vm => vm.EasingFunction == DataBinding.EasingFunction);
|
SelectedEasingViewModel = EasingViewModels.First(vm => vm.EasingFunction == Registration.DataBinding.EasingFunction);
|
||||||
IsEasingTimeEnabled = EasingTime > 0;
|
IsEasingTimeEnabled = EasingTime > 0;
|
||||||
switch (DataBinding.DataBindingMode)
|
switch (Registration.DataBinding.DataBindingMode)
|
||||||
{
|
{
|
||||||
case DirectDataBinding<TLayerProperty, TProperty> _:
|
case DirectDataBinding<TLayerProperty, TProperty> _:
|
||||||
SelectedDataBindingMode = DataBindingModeType.Direct;
|
SelectedDataBindingMode = DataBindingModeType.Direct;
|
||||||
@ -182,10 +172,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
|||||||
if (_updating)
|
if (_updating)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (DataBinding != null)
|
if (Registration.DataBinding != null)
|
||||||
{
|
{
|
||||||
DataBinding.EasingTime = TimeSpan.FromMilliseconds(EasingTime);
|
Registration.DataBinding.EasingTime = TimeSpan.FromMilliseconds(EasingTime);
|
||||||
DataBinding.EasingFunction = SelectedEasingViewModel?.EasingFunction ?? Easings.Functions.Linear;
|
Registration.DataBinding.EasingFunction = SelectedEasingViewModel?.EasingFunction ?? Easings.Functions.Linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
_profileEditorService.UpdateSelectedProfileElement();
|
_profileEditorService.UpdateSelectedProfileElement();
|
||||||
@ -197,17 +187,17 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
|||||||
if (_updating)
|
if (_updating)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (DataBinding != null && SelectedDataBindingMode == DataBindingModeType.None)
|
if (Registration.DataBinding != null && SelectedDataBindingMode == DataBindingModeType.None)
|
||||||
{
|
{
|
||||||
RemoveDataBinding();
|
RemoveDataBinding();
|
||||||
CreateDataBindingModeModeViewModel();
|
CreateDataBindingModeModeViewModel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DataBinding == null && SelectedDataBindingMode != DataBindingModeType.None)
|
if (Registration.DataBinding == null && SelectedDataBindingMode != DataBindingModeType.None)
|
||||||
EnableDataBinding();
|
EnableDataBinding();
|
||||||
|
|
||||||
DataBinding.ChangeDataBindingMode(SelectedDataBindingMode);
|
Registration.DataBinding.ChangeDataBindingMode(SelectedDataBindingMode);
|
||||||
CreateDataBindingModeModeViewModel();
|
CreateDataBindingModeModeViewModel();
|
||||||
|
|
||||||
_profileEditorService.UpdateSelectedProfileElement();
|
_profileEditorService.UpdateSelectedProfileElement();
|
||||||
@ -215,7 +205,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
|||||||
|
|
||||||
private void UpdateTestResult()
|
private void UpdateTestResult()
|
||||||
{
|
{
|
||||||
if (DataBinding == null)
|
if (Registration.DataBinding == null)
|
||||||
{
|
{
|
||||||
TestInputValue.UpdateValue(default);
|
TestInputValue.UpdateValue(default);
|
||||||
TestResultValue.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));
|
var currentValue = Registration.Converter.ConvertFromObject(ActiveItem?.GetTestValue() ?? default(TProperty));
|
||||||
|
|
||||||
TestInputValue.UpdateValue(currentValue);
|
TestInputValue.UpdateValue(currentValue);
|
||||||
TestResultValue.UpdateValue(DataBinding != null ? DataBinding.GetValue(currentValue) : default);
|
TestResultValue.UpdateValue(Registration.DataBinding != null ? Registration.DataBinding.GetValue(currentValue) : default);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnableDataBinding()
|
private void EnableDataBinding()
|
||||||
{
|
{
|
||||||
if (DataBinding != null)
|
if (Registration.DataBinding != null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DataBinding = Registration.LayerProperty.EnableDataBinding(Registration);
|
Registration.LayerProperty.EnableDataBinding(Registration);
|
||||||
_profileEditorService.UpdateSelectedProfileElement();
|
_profileEditorService.UpdateSelectedProfileElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveDataBinding()
|
private void RemoveDataBinding()
|
||||||
{
|
{
|
||||||
if (DataBinding == null)
|
if (Registration.DataBinding == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var toDisable = DataBinding;
|
Registration.LayerProperty.DisableDataBinding(Registration.DataBinding);
|
||||||
DataBinding = null;
|
|
||||||
Registration.LayerProperty.DisableDataBinding(toDisable);
|
|
||||||
Update();
|
Update();
|
||||||
|
|
||||||
_profileEditorService.UpdateSelectedProfileElement();
|
_profileEditorService.UpdateSelectedProfileElement();
|
||||||
|
|||||||
@ -0,0 +1,41 @@
|
|||||||
|
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline.Dialogs.TimelineSegmentDialogView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
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.ProfileEditor.LayerProperties.Timeline.Dialogs"
|
||||||
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
|
xmlns:s="https://github.com/canton7/Stylet"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="450" d:DesignWidth="800"
|
||||||
|
d:DataContext="{d:DesignInstance local:TimelineSegmentDialogViewModel}">
|
||||||
|
<StackPanel Margin="16" Width="240">
|
||||||
|
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}">
|
||||||
|
<Run Text="Update " /><Run Text="{Binding Segment.Segment, Mode=OneWay}" /><Run Text=" segment" />
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<TextBox materialDesign:HintAssist.Hint="Segment length (seconds)"
|
||||||
|
Margin="0 8 0 16"
|
||||||
|
Text="{Binding InputValue, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
Style="{StaticResource MaterialDesignFloatingHintTextBox}" />
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
|
<Button Style="{StaticResource MaterialDesignFlatButton}" IsCancel="True" Margin="0 8 8 0" Command="{s:Action Cancel}">
|
||||||
|
<Button.CommandParameter>
|
||||||
|
<system:Boolean xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||||
|
False
|
||||||
|
</system:Boolean>
|
||||||
|
</Button.CommandParameter>
|
||||||
|
CANCEL
|
||||||
|
</Button>
|
||||||
|
<Button Style="{StaticResource MaterialDesignFlatButton}" IsDefault="True" Margin="0 8 8 0" Command="{s:Action Accept}">
|
||||||
|
<Button.CommandParameter>
|
||||||
|
<system:Boolean xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||||
|
True
|
||||||
|
</system:Boolean>
|
||||||
|
</Button.CommandParameter>
|
||||||
|
ACCEPT
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
||||||
@ -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<TimelineSegmentDialogViewModel> 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<TimelineSegmentDialogViewModel>
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,11 +16,21 @@
|
|||||||
<StackPanel Orientation="Horizontal" Visibility="{Binding SegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">
|
<StackPanel Orientation="Horizontal" Visibility="{Binding SegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">
|
||||||
<StackPanel.ContextMenu>
|
<StackPanel.ContextMenu>
|
||||||
<ContextMenu>
|
<ContextMenu>
|
||||||
|
<MenuItem Header="Open segment settings" Command="{s:Action OpenSettingsDialog}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<materialDesign:PackIcon Kind="Cog" />
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
<MenuItem Header="Repeat main segment"
|
<MenuItem Header="Repeat main segment"
|
||||||
IsCheckable="True"
|
IsCheckable="True"
|
||||||
IsChecked="{Binding Data.RepeatSegment, Source={StaticResource DataContextProxy}}"
|
IsChecked="{Binding Data.RepeatSegment, Source={StaticResource DataContextProxy}}"
|
||||||
Visibility="{Binding Data.IsMainSegment, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Source={StaticResource DataContextProxy}}" />
|
Visibility="{Binding Data.IsMainSegment, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Source={StaticResource DataContextProxy}}" />
|
||||||
<MenuItem Header="Disable segment" Command="{s:Action DisableSegment}" />
|
<MenuItem Header="Disable segment" Command="{s:Action DisableSegment}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<materialDesign:PackIcon Kind="Close" />
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</StackPanel.ContextMenu>
|
</StackPanel.ContextMenu>
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using Artemis.Core;
|
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;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
@ -13,6 +17,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
|||||||
{
|
{
|
||||||
public class TimelineSegmentViewModel : Screen, IDisposable
|
public class TimelineSegmentViewModel : Screen, IDisposable
|
||||||
{
|
{
|
||||||
|
private readonly IDialogService _dialogService;
|
||||||
private bool _draggingSegment;
|
private bool _draggingSegment;
|
||||||
private bool _showDisableButton;
|
private bool _showDisableButton;
|
||||||
private bool _showRepeatButton;
|
private bool _showRepeatButton;
|
||||||
@ -25,8 +30,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
|||||||
private double _segmentStartPosition;
|
private double _segmentStartPosition;
|
||||||
|
|
||||||
public TimelineSegmentViewModel(SegmentViewModelType segment, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups,
|
public TimelineSegmentViewModel(SegmentViewModelType segment, BindableCollection<LayerPropertyGroupViewModel> layerPropertyGroups,
|
||||||
IProfileEditorService profileEditorService)
|
IProfileEditorService profileEditorService, IDialogService dialogService)
|
||||||
{
|
{
|
||||||
|
_dialogService = dialogService;
|
||||||
ProfileEditorService = profileEditorService;
|
ProfileEditorService = profileEditorService;
|
||||||
Segment = segment;
|
Segment = segment;
|
||||||
LayerPropertyGroups = layerPropertyGroups;
|
LayerPropertyGroups = layerPropertyGroups;
|
||||||
@ -130,6 +136,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
|||||||
set => SetAndNotify(ref _showDisableButton, value);
|
set => SetAndNotify(ref _showDisableButton, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task OpenSettingsDialog()
|
||||||
|
{
|
||||||
|
await _dialogService.ShowDialog<TimelineSegmentDialogViewModel>(new Dictionary<string, object> {{"segment", this}});
|
||||||
|
}
|
||||||
|
|
||||||
#region Updating
|
#region Updating
|
||||||
|
|
||||||
private void UpdateHeader()
|
private void UpdateHeader()
|
||||||
@ -276,6 +287,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
|||||||
else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
|
else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
|
||||||
newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 50.0) * 50.0);
|
newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 50.0) * 50.0);
|
||||||
|
|
||||||
|
UpdateLength(newTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateLength(TimeSpan newTime)
|
||||||
|
{
|
||||||
var oldSegmentLength = SegmentLength;
|
var oldSegmentLength = SegmentLength;
|
||||||
if (Segment == SegmentViewModelType.Start)
|
if (Segment == SegmentViewModelType.Start)
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user