diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/TimelineSegmentEditView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/TimelineSegmentEditView.axaml
new file mode 100644
index 000000000..23dad425d
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/TimelineSegmentEditView.axaml
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/TimelineSegmentEditView.axaml.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/TimelineSegmentEditView.axaml.cs
new file mode 100644
index 000000000..f46564a8a
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/TimelineSegmentEditView.axaml.cs
@@ -0,0 +1,19 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
+
+namespace Artemis.UI.Screens.ProfileEditor.Properties.Dialogs;
+
+public partial class TimelineSegmentEditView : ReactiveUserControl
+{
+ public TimelineSegmentEditView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/TimelineSegmentEditViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/TimelineSegmentEditViewModel.cs
new file mode 100644
index 000000000..a9a4318e7
--- /dev/null
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Dialogs/TimelineSegmentEditViewModel.cs
@@ -0,0 +1,20 @@
+using System;
+using Artemis.UI.Shared;
+
+namespace Artemis.UI.Screens.ProfileEditor.Properties.Dialogs;
+
+public class TimelineSegmentEditViewModel : ContentDialogViewModelBase
+{
+ private double _segmentLength;
+
+ public TimelineSegmentEditViewModel(TimeSpan segmentLength)
+ {
+ SegmentLength = segmentLength.TotalSeconds;
+ }
+
+ public double SegmentLength
+ {
+ get => _segmentLength;
+ set => _segmentLength = value;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/EndSegmentView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/EndSegmentView.axaml
index f26745983..4b81a53c7 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/EndSegmentView.axaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/EndSegmentView.axaml
@@ -15,7 +15,21 @@
Background="{DynamicResource ControlFillColorDefaultBrush}"
Width="{CompiledBinding Width}"
ColumnDefinitions="Auto, Auto,*,Auto">
-
+
+
+
+
+
+
+
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/EndSegmentViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/EndSegmentViewModel.cs
index ab48333a6..7e10e7999 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/EndSegmentViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/EndSegmentViewModel.cs
@@ -1,6 +1,7 @@
using System;
using System.Reactive.Linq;
using Artemis.Core;
+using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
using Avalonia.Controls.Mixins;
@@ -16,7 +17,7 @@ public class EndSegmentViewModel : TimelineSegmentViewModel
private RenderProfileElement? _profileElement;
private ObservableAsPropertyHelper? _start;
- public EndSegmentViewModel(IProfileEditorService profileEditorService) : base(profileEditorService)
+ public EndSegmentViewModel(IProfileEditorService profileEditorService, IWindowService windowService) : base(profileEditorService, windowService)
{
this.WhenActivated(d =>
{
@@ -57,6 +58,7 @@ public class EndSegmentViewModel : TimelineSegmentViewModel
{
if (_profileElement != null)
_profileElement.Timeline.EndSegmentLength = value;
+ this.RaisePropertyChanged(nameof(Length));
}
}
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/MainSegmentView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/MainSegmentView.axaml
index 222038f88..9325e0d67 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/MainSegmentView.axaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/MainSegmentView.axaml
@@ -15,7 +15,20 @@
Background="{DynamicResource ControlFillColorDefaultBrush}"
Width="{CompiledBinding Width}"
ColumnDefinitions="Auto,Auto,*,Auto,Auto">
-
+
+
+
+
+
+
+ Command="{Binding RemoveSegment}">
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/MainSegmentViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/MainSegmentViewModel.cs
index 3fa9c266f..15ebb371c 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/MainSegmentViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/MainSegmentViewModel.cs
@@ -1,6 +1,7 @@
using System;
using System.Reactive.Linq;
using Artemis.Core;
+using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
using Avalonia.Controls.Mixins;
@@ -16,7 +17,7 @@ public class MainSegmentViewModel : TimelineSegmentViewModel
private RenderProfileElement? _profileElement;
private ObservableAsPropertyHelper? _start;
- public MainSegmentViewModel(IProfileEditorService profileEditorService) : base(profileEditorService)
+ public MainSegmentViewModel(IProfileEditorService profileEditorService, IWindowService windowService) : base(profileEditorService, windowService)
{
this.WhenActivated(d =>
{
@@ -57,6 +58,7 @@ public class MainSegmentViewModel : TimelineSegmentViewModel
{
if (_profileElement != null)
_profileElement.Timeline.MainSegmentLength = value;
+ this.RaisePropertyChanged(nameof(Length));
}
}
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/StartSegmentView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/StartSegmentView.axaml
index 1a9ffedc4..b58b57539 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/StartSegmentView.axaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/StartSegmentView.axaml
@@ -15,6 +15,20 @@
Background="{DynamicResource ControlFillColorDefaultBrush}"
Width="{CompiledBinding Width}"
ColumnDefinitions="*,Auto,Auto">
+
+
+
+
+
+
? _endTimestamp;
private RenderProfileElement? _profileElement;
- public StartSegmentViewModel(IProfileEditorService profileEditorService) : base(profileEditorService)
+ public StartSegmentViewModel(IProfileEditorService profileEditorService, IWindowService windowService) : base(profileEditorService, windowService)
{
this.WhenActivated(d =>
{
@@ -50,6 +51,7 @@ public class StartSegmentViewModel : TimelineSegmentViewModel
{
if (_profileElement != null)
_profileElement.Timeline.StartSegmentLength = value;
+ this.RaisePropertyChanged(nameof(Length));
}
}
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/TimelineSegmentViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/TimelineSegmentViewModel.cs
index 92e9154f4..da58970a6 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/TimelineSegmentViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/TimelineSegmentViewModel.cs
@@ -1,9 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Reactive;
using System.Reactive.Linq;
+using System.Threading.Tasks;
using Artemis.Core;
+using Artemis.UI.Screens.ProfileEditor.Properties.Dialogs;
using Artemis.UI.Shared;
+using Artemis.UI.Shared.Services;
+using Artemis.UI.Shared.Services.Builders;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
using Avalonia.Controls.Mixins;
@@ -15,18 +20,23 @@ public abstract class TimelineSegmentViewModel : ActivatableViewModelBase
{
private static readonly TimeSpan NewSegmentLength = TimeSpan.FromSeconds(2);
private readonly IProfileEditorService _profileEditorService;
+ private readonly IWindowService _windowService;
private TimeSpan _initialLength;
private readonly Dictionary _originalKeyframePositions = new();
private int _pixelsPerSecond;
private RenderProfileElement? _profileElement;
private ObservableAsPropertyHelper? _showAddEnd;
private ObservableAsPropertyHelper? _showAddMain;
-
private ObservableAsPropertyHelper? _showAddStart;
+ private ReactiveCommand _removeSegment;
- protected TimelineSegmentViewModel(IProfileEditorService profileEditorService)
+ protected TimelineSegmentViewModel(IProfileEditorService profileEditorService, IWindowService windowService)
{
_profileEditorService = profileEditorService;
+ _windowService = windowService;
+
+ EditTime = ReactiveCommand.CreateFromTask(ExecuteEditTime);
+
this.WhenActivated(d =>
{
profileEditorService.ProfileElement.Subscribe(p => _profileElement = p).DisposeWith(d);
@@ -50,6 +60,30 @@ public abstract class TimelineSegmentViewModel : ActivatableViewModelBase
.Select(t => t == TimeSpan.Zero)
.ToProperty(this, vm => vm.ShowAddEnd)
.DisposeWith(d);
+
+ if (Type == ResizeTimelineSegment.SegmentType.Start)
+ {
+ RemoveSegment = ReactiveCommand.Create(
+ ExecuteRemoveSegment,
+ this.WhenAnyValue(vm => vm.ShowAddMain).CombineLatest(this.WhenAnyValue(vm => vm.ShowAddEnd)).Select(tuple => !tuple.First || !tuple.Second)
+ );
+ }
+
+ if (Type == ResizeTimelineSegment.SegmentType.Main)
+ {
+ RemoveSegment = ReactiveCommand.Create(
+ ExecuteRemoveSegment,
+ this.WhenAnyValue(vm => vm.ShowAddEnd).CombineLatest(this.WhenAnyValue(vm => vm.ShowAddStart)).Select(tuple => !tuple.First || !tuple.Second)
+ );
+ }
+
+ if (Type == ResizeTimelineSegment.SegmentType.End)
+ {
+ RemoveSegment = ReactiveCommand.Create(
+ ExecuteRemoveSegment,
+ this.WhenAnyValue(vm => vm.ShowAddStart).CombineLatest(this.WhenAnyValue(vm => vm.ShowAddMain)).Select(tuple => !tuple.First || !tuple.Second)
+ );
+ }
});
}
@@ -66,6 +100,14 @@ public abstract class TimelineSegmentViewModel : ActivatableViewModelBase
public abstract string? EndTimestamp { get; }
public abstract ResizeTimelineSegment.SegmentType Type { get; }
+ public ReactiveCommand EditTime { get; }
+
+ public ReactiveCommand RemoveSegment
+ {
+ get => _removeSegment;
+ set => RaiseAndSetIfChanged(ref _removeSegment, value);
+ }
+
public void AddStartSegment()
{
if (_profileElement == null)
@@ -110,10 +152,11 @@ public abstract class TimelineSegmentViewModel : ActivatableViewModelBase
if (_profileElement == null)
return;
- TimeSpan difference = GetTimeFromX(x, snap, round) - Length;
+ TimeSpan time = TimeSpan.FromMilliseconds(Math.Max(GetTimeFromX(x, snap, round).TotalMilliseconds, 100));
+ TimeSpan difference = time - Length;
List keyframes = _profileElement.GetAllLayerProperties().SelectMany(p => p.UntypedKeyframes).ToList();
ShiftKeyframes(keyframes.Where(k => k.Position > End.Add(difference)), difference);
- Length = GetTimeFromX(x, snap, round);
+ Length = time;
}
public void FinishResize(double x, bool snap, bool round)
@@ -121,12 +164,16 @@ public abstract class TimelineSegmentViewModel : ActivatableViewModelBase
if (_profileElement == null)
return;
+ TimeSpan time = TimeSpan.FromMilliseconds(Math.Max(GetTimeFromX(x, snap, round).TotalMilliseconds, 100));
+ if (_initialLength == time)
+ return;
+
using ProfileEditorCommandScope scope = _profileEditorService.CreateCommandScope("Resize segment");
ApplyPendingKeyframeMovement();
- _profileEditorService.ExecuteCommand(new ResizeTimelineSegment(Type, _profileElement, GetTimeFromX(x, snap, round), _initialLength));
+ _profileEditorService.ExecuteCommand(new ResizeTimelineSegment(Type, _profileElement, time, _initialLength));
}
- public void RemoveSegment()
+ private void ExecuteRemoveSegment()
{
if (_profileElement == null)
return;
@@ -148,17 +195,32 @@ public abstract class TimelineSegmentViewModel : ActivatableViewModelBase
_profileEditorService.ExecuteCommand(new ResizeTimelineSegment(Type, _profileElement, TimeSpan.Zero));
}
+ private async Task ExecuteEditTime()
+ {
+ await _windowService.CreateContentDialog()
+ .WithTitle("Edit segment length")
+ .WithViewModel(out TimelineSegmentEditViewModel vm, ("segmentLength", Length))
+ .HavingPrimaryButton(b => b.WithText("Save").WithAction(() =>
+ {
+ if (_profileElement != null)
+ _profileEditorService.ExecuteCommand(new ResizeTimelineSegment(Type, _profileElement, TimeSpan.FromSeconds(vm.SegmentLength)));
+ }))
+ .WithDefaultButton(ContentDialogButton.Primary)
+ .WithCloseButtonText("Cancel")
+ .ShowAsync();
+ }
+
protected TimeSpan GetTimeFromX(double x, bool snap, bool round)
{
TimeSpan time = TimeSpan.FromSeconds(x / _pixelsPerSecond);
if (time < TimeSpan.Zero)
time = TimeSpan.Zero;
-
+
if (round)
time = _profileEditorService.RoundTime(time);
if (snap)
time = SnapToTimeline(time);
-
+
return time;
}
@@ -179,7 +241,7 @@ public abstract class TimelineSegmentViewModel : ActivatableViewModelBase
_originalKeyframePositions.Clear();
}
-
+
private TimeSpan SnapToTimeline(TimeSpan time)
{
TimeSpan tolerance = TimeSpan.FromMilliseconds(1000f / _pixelsPerSecond * 5);