mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 19:36:28 +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:
1 parent
9f951ca33f
commit
bfc93778a6
18 files changed
+369
-102
No files matched your search
+18
-30
@@ -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<TLayerProperty, TProperty> 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<TLayerProperty, TProperty> 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<TLayerProperty, TProperty> _:
|
||||
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();
|
||||
|
||||
+41
@@ -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>
|
||||
+98
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-1
@@ -16,11 +16,21 @@
|
||||
<StackPanel Orientation="Horizontal" Visibility="{Binding SegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">
|
||||
<StackPanel.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"
|
||||
IsCheckable="True"
|
||||
IsChecked="{Binding Data.RepeatSegment, 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>
|
||||
</StackPanel.ContextMenu>
|
||||
|
||||
|
||||
+17
-1
@@ -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<LayerPropertyGroupViewModel> 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<TimelineSegmentDialogViewModel>(new Dictionary<string, object> {{"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)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user