mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 19:36:28 +00:00
Profiles - Added module activation requirements
Editor - Refactored tools and selected keyframes management Timeline - Added keyframe duplicating, copying and pasting Windows UI - Added logging of fatal exceptions
This commit is contained in:
1 parent
d563d17270
commit
dd40bdd544
31 files changed
+719
-178
No files matched your search
+7
-4
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Reactive;
|
||||
using Artemis.Core;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes;
|
||||
|
||||
@@ -22,10 +24,11 @@ public interface ITimelineKeyframeViewModel
|
||||
#region Context menu actions
|
||||
|
||||
void PopulateEasingViewModels();
|
||||
void Duplicate();
|
||||
void Copy();
|
||||
void Paste();
|
||||
void Delete();
|
||||
|
||||
ReactiveCommand<Unit, Unit> Duplicate { get; }
|
||||
ReactiveCommand<Unit, Unit> Copy { get; }
|
||||
ReactiveCommand<Unit, Unit> Paste { get; }
|
||||
ReactiveCommand<Unit, Unit> Delete { get; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
+4
-4
@@ -52,23 +52,23 @@
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="-" />
|
||||
<MenuItem Header="Duplicate" Command="{Binding $parent[timeline:TimelineView].DataContext.DuplicateKeyframes}" CommandParameter="{Binding}" InputGesture="Ctrl+D">
|
||||
<MenuItem Header="Duplicate" Command="{Binding Duplicate}" InputGesture="Ctrl+D">
|
||||
<MenuItem.Icon>
|
||||
<avalonia:MaterialIcon Kind="ContentDuplicate" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Copy" Command="{Binding $parent[timeline:TimelineView].DataContext.CopyKeyframes}" CommandParameter="{Binding}" InputGesture="Ctrl+C">
|
||||
<MenuItem Header="Copy" Command="{Binding Copy}" InputGesture="Ctrl+C">
|
||||
<MenuItem.Icon>
|
||||
<avalonia:MaterialIcon Kind="ContentCopy" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Paste" Command="{Binding $parent[timeline:TimelineView].DataContext.PasteKeyframes}" CommandParameter="{Binding}" InputGesture="Ctrl+V">
|
||||
<MenuItem Header="Paste" Command="{Binding Paste}" InputGesture="Ctrl+V">
|
||||
<MenuItem.Icon>
|
||||
<avalonia:MaterialIcon Kind="ContentPaste" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="-" />
|
||||
<MenuItem Header="Delete" Command="{Binding $parent[timeline:TimelineView].DataContext.DeleteKeyframes}" CommandParameter="{Binding}" InputGesture="Delete">
|
||||
<MenuItem Header="Delete" Command="{Binding Delete}" InputGesture="Delete">
|
||||
<MenuItem.Icon>
|
||||
<avalonia:MaterialIcon Kind="Delete" />
|
||||
</MenuItem.Icon>
|
||||
|
||||
+124
-13
@@ -1,13 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Models;
|
||||
using Artemis.UI.Services.ProfileEditor.Commands;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Extensions;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.Mixins;
|
||||
using Avalonia.Input;
|
||||
using DynamicData;
|
||||
using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes;
|
||||
@@ -17,8 +26,9 @@ public class TimelineKeyframeViewModel<T> : ActivatableViewModelBase, ITimelineK
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private ObservableAsPropertyHelper<bool>? _isSelected;
|
||||
private string _timestamp;
|
||||
|
||||
private double _x;
|
||||
private bool _canPaste;
|
||||
private bool _isFlyoutOpen;
|
||||
|
||||
public TimelineKeyframeViewModel(LayerPropertyKeyframe<T> layerPropertyKeyframe, IProfileEditorService profileEditorService)
|
||||
{
|
||||
@@ -29,20 +39,29 @@ public class TimelineKeyframeViewModel<T> : ActivatableViewModelBase, ITimelineK
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
_isSelected = profileEditorService.ConnectToKeyframes()
|
||||
.ToCollection()
|
||||
.Select(keyframes => keyframes.Contains(LayerPropertyKeyframe))
|
||||
_isSelected = profileEditorService.SelectedKeyframes
|
||||
.ToObservableChangeSet()
|
||||
.Select(_ => profileEditorService.SelectedKeyframes.Contains(LayerPropertyKeyframe))
|
||||
.ToProperty(this, vm => vm.IsSelected)
|
||||
.DisposeWith(d);
|
||||
profileEditorService.ConnectToKeyframes();
|
||||
profileEditorService.PixelsPerSecond.Subscribe(p => _pixelsPerSecond = p).DisposeWith(d);
|
||||
profileEditorService.PixelsPerSecond.Subscribe(_ => Update()).DisposeWith(d);
|
||||
this.WhenAnyValue(vm => vm.LayerPropertyKeyframe.Position).Subscribe(_ => Update()).DisposeWith(d);
|
||||
});
|
||||
this.WhenAnyValue(vm => vm.IsFlyoutOpen).Subscribe(UpdateCanPaste);
|
||||
|
||||
Duplicate = ReactiveCommand.Create(ExecuteDuplicate);
|
||||
Copy = ReactiveCommand.CreateFromTask(ExecuteCopy);
|
||||
Paste = ReactiveCommand.CreateFromTask(ExecutePaste);
|
||||
Delete = ReactiveCommand.Create(ExecuteDelete);
|
||||
}
|
||||
|
||||
public LayerPropertyKeyframe<T> LayerPropertyKeyframe { get; }
|
||||
public ObservableCollection<TimelineEasingViewModel> EasingViewModels { get; }
|
||||
public ReactiveCommand<Unit, Unit> Duplicate { get; }
|
||||
public ReactiveCommand<Unit, Unit> Copy { get; }
|
||||
public ReactiveCommand<Unit, Unit> Paste { get; }
|
||||
public ReactiveCommand<Unit, Unit> Delete { get; }
|
||||
|
||||
public double X
|
||||
{
|
||||
@@ -56,6 +75,18 @@ public class TimelineKeyframeViewModel<T> : ActivatableViewModelBase, ITimelineK
|
||||
set => RaiseAndSetIfChanged(ref _timestamp, value);
|
||||
}
|
||||
|
||||
public bool IsFlyoutOpen
|
||||
{
|
||||
get => _isFlyoutOpen;
|
||||
set => RaiseAndSetIfChanged(ref _isFlyoutOpen, value);
|
||||
}
|
||||
|
||||
public bool CanPaste
|
||||
{
|
||||
get => _canPaste;
|
||||
set => RaiseAndSetIfChanged(ref _canPaste, value);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
X = _pixelsPerSecond * LayerPropertyKeyframe.Position.TotalSeconds;
|
||||
@@ -79,24 +110,104 @@ public class TimelineKeyframeViewModel<T> : ActivatableViewModelBase, ITimelineK
|
||||
|
||||
#region Context menu actions
|
||||
|
||||
public void Duplicate()
|
||||
private void ExecuteDelete()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (!IsSelected)
|
||||
{
|
||||
_profileEditorService.ExecuteCommand(new DeleteKeyframe(Keyframe));
|
||||
}
|
||||
else
|
||||
{
|
||||
List<ILayerPropertyKeyframe> keyframes = _profileEditorService.SelectedKeyframes.ToList();
|
||||
using ProfileEditorCommandScope scope = _profileEditorService.CreateCommandScope("Delete keyframes");
|
||||
foreach (ILayerPropertyKeyframe keyframe in keyframes)
|
||||
_profileEditorService.ExecuteCommand(new DeleteKeyframe(keyframe));
|
||||
}
|
||||
}
|
||||
|
||||
public void Copy()
|
||||
private void ExecuteDuplicate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (!IsSelected)
|
||||
{
|
||||
DuplicateKeyframe command = new(Keyframe, FindKeyframeDuplicationPosition(Keyframe));
|
||||
_profileEditorService.ExecuteCommand(command);
|
||||
_profileEditorService.SelectKeyframe(command.Duplication, false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
List<ILayerPropertyKeyframe> keyframes = _profileEditorService.SelectedKeyframes.ToList();
|
||||
_profileEditorService.SelectKeyframe(null, false, false);
|
||||
using ProfileEditorCommandScope scope = _profileEditorService.CreateCommandScope("Duplicate keyframes");
|
||||
foreach (ILayerPropertyKeyframe keyframe in keyframes)
|
||||
{
|
||||
DuplicateKeyframe command = new(keyframe, FindKeyframeDuplicationPosition(keyframe));
|
||||
_profileEditorService.ExecuteCommand(command);
|
||||
_profileEditorService.SelectKeyframe(command.Duplication, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Paste()
|
||||
private async Task ExecuteCopy()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (Application.Current?.Clipboard == null)
|
||||
return;
|
||||
|
||||
List<KeyframeClipboardModel> keyframes = new();
|
||||
if (!IsSelected)
|
||||
keyframes.Add(new KeyframeClipboardModel(Keyframe));
|
||||
else
|
||||
keyframes.AddRange(_profileEditorService.SelectedKeyframes.Select(k => new KeyframeClipboardModel(k)));
|
||||
|
||||
string copy = CoreJson.SerializeObject(keyframes, true);
|
||||
DataObject dataObject = new();
|
||||
dataObject.Set(KeyframeClipboardModel.ClipboardDataFormat, copy);
|
||||
await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
private async Task ExecutePaste()
|
||||
{
|
||||
_profileEditorService.ExecuteCommand(new DeleteKeyframe(LayerPropertyKeyframe));
|
||||
if (Application.Current?.Clipboard == null)
|
||||
return;
|
||||
|
||||
List<KeyframeClipboardModel>? keyframes = await Application.Current.Clipboard.GetJsonAsync<List<KeyframeClipboardModel>>(KeyframeClipboardModel.ClipboardDataFormat);
|
||||
if (keyframes == null)
|
||||
return;
|
||||
|
||||
PasteKeyframes command = new(Keyframe.UntypedLayerProperty.ProfileElement, keyframes, FindKeyframeDuplicationPosition(Keyframe));
|
||||
_profileEditorService.ExecuteCommand(command);
|
||||
if (command.PastedKeyframes != null && command.PastedKeyframes.Any())
|
||||
_profileEditorService.SelectKeyframes(command.PastedKeyframes, false);
|
||||
}
|
||||
|
||||
private TimeSpan FindKeyframeDuplicationPosition(ILayerPropertyKeyframe keyframe)
|
||||
{
|
||||
TimeSpan position;
|
||||
TimeSpan distance = TimeSpan.FromSeconds(15 / _pixelsPerSecond);
|
||||
TimeSpan maxRight = keyframe.UntypedLayerProperty.ProfileElement.Timeline.Length;
|
||||
|
||||
// Pick the side that has the most available space
|
||||
// Prefer right side
|
||||
if (keyframe.Position + distance <= maxRight)
|
||||
// Put the keyframe as far to the right as possible within the max
|
||||
position = TimeSpan.FromSeconds(Math.Min(maxRight.TotalSeconds, (keyframe.Position + distance).TotalSeconds));
|
||||
// Fall back to left side
|
||||
else
|
||||
// Put the keyframe as far to the left as possible withing the max
|
||||
position = TimeSpan.FromSeconds(Math.Max(0, (keyframe.Position - distance).TotalSeconds));
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
private async void UpdateCanPaste(bool isFlyoutOpen)
|
||||
{
|
||||
if (Application.Current?.Clipboard == null)
|
||||
{
|
||||
CanPaste = false;
|
||||
return;
|
||||
}
|
||||
|
||||
string[] formats = await Application.Current.Clipboard.GetFormatsAsync();
|
||||
CanPaste = formats.Contains("Artemis.Keyframes");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
</UserControl.Resources>
|
||||
<Grid Background="Transparent" PointerReleased="InputElement_OnPointerReleased" Focusable="True" MinWidth="{Binding MinWidth}">
|
||||
<Grid.KeyBindings>
|
||||
<KeyBinding Command="{Binding DeleteKeyframes}" Gesture="Delete" />
|
||||
<KeyBinding Command="{CompiledBinding CopySelectedKeyframes}" Gesture="Ctrl+C" />
|
||||
<KeyBinding Command="{CompiledBinding DuplicateSelectedKeyframes}" Gesture="Ctrl+D" />
|
||||
<KeyBinding Command="{CompiledBinding PasteKeyframes}" Gesture="Ctrl+V" />
|
||||
<KeyBinding Command="{CompiledBinding DeleteSelectedKeyframes}" Gesture="Delete" />
|
||||
</Grid.KeyBindings>
|
||||
<ItemsControl Items="{CompiledBinding PropertyGroupViewModels}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
|
||||
+38
-48
@@ -2,11 +2,18 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Models;
|
||||
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes;
|
||||
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Segments;
|
||||
using Artemis.UI.Services.ProfileEditor.Commands;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Extensions;
|
||||
using Artemis.UI.Shared.Services.ProfileEditor;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.Mixins;
|
||||
using ReactiveUI;
|
||||
|
||||
@@ -19,6 +26,8 @@ public class TimelineViewModel : ActivatableViewModelBase
|
||||
private ObservableAsPropertyHelper<double> _minWidth;
|
||||
private List<ITimelineKeyframeViewModel>? _moveKeyframes;
|
||||
private ObservableAsPropertyHelper<int>? _pixelsPerSecond;
|
||||
private RenderProfileElement? _profileElement;
|
||||
private TimeSpan _time;
|
||||
|
||||
public TimelineViewModel(ObservableCollection<PropertyGroupViewModel> propertyGroupViewModels,
|
||||
StartSegmentViewModel startSegmentViewModel,
|
||||
@@ -34,6 +43,8 @@ public class TimelineViewModel : ActivatableViewModelBase
|
||||
_profileEditorService = profileEditorService;
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
_profileEditorService.ProfileElement.Subscribe(p => _profileElement = p).DisposeWith(d);
|
||||
_profileEditorService.Time.Subscribe(t => _time = t).DisposeWith(d);
|
||||
_caretPosition = _profileEditorService.Time
|
||||
.CombineLatest(_profileEditorService.PixelsPerSecond, (t, p) => t.TotalSeconds * p)
|
||||
.ToProperty(this, vm => vm.CaretPosition)
|
||||
@@ -46,12 +57,21 @@ public class TimelineViewModel : ActivatableViewModelBase
|
||||
.ToProperty(this, vm => vm.MinWidth)
|
||||
.DisposeWith(d);
|
||||
});
|
||||
|
||||
DuplicateSelectedKeyframes = ReactiveCommand.Create(ExecuteDuplicateSelectedKeyframes);
|
||||
CopySelectedKeyframes = ReactiveCommand.Create(ExecuteCopySelectedKeyframes);
|
||||
PasteKeyframes = ReactiveCommand.CreateFromTask(ExecutePasteKeyframes);
|
||||
DeleteSelectedKeyframes = ReactiveCommand.Create(ExecuteDeleteSelectedKeyframes);
|
||||
}
|
||||
|
||||
public ObservableCollection<PropertyGroupViewModel> PropertyGroupViewModels { get; }
|
||||
public StartSegmentViewModel StartSegmentViewModel { get; }
|
||||
public MainSegmentViewModel MainSegmentViewModel { get; }
|
||||
public EndSegmentViewModel EndSegmentViewModel { get; }
|
||||
public ReactiveCommand<Unit, Unit> DuplicateSelectedKeyframes { get; }
|
||||
public ReactiveCommand<Unit, Unit> CopySelectedKeyframes { get; }
|
||||
public ReactiveCommand<Unit, Unit> PasteKeyframes { get; }
|
||||
public ReactiveCommand<Unit, Unit> DeleteSelectedKeyframes { get; }
|
||||
|
||||
public double CaretPosition => _caretPosition?.Value ?? 0.0;
|
||||
public int PixelsPerSecond => _pixelsPerSecond?.Value ?? 0;
|
||||
@@ -137,64 +157,34 @@ public class TimelineViewModel : ActivatableViewModelBase
|
||||
|
||||
#region Keyframe actions
|
||||
|
||||
public void DuplicateKeyframes(ITimelineKeyframeViewModel? source = null)
|
||||
private void ExecuteDuplicateSelectedKeyframes()
|
||||
{
|
||||
if (source is {IsSelected: false})
|
||||
{
|
||||
source.Delete();
|
||||
}
|
||||
else
|
||||
{
|
||||
List<ITimelineKeyframeViewModel> keyframes = PropertyGroupViewModels.SelectMany(g => g.GetAllKeyframeViewModels(true)).Where(k => k.IsSelected).ToList();
|
||||
using ProfileEditorCommandScope scope = _profileEditorService.CreateCommandScope($"Duplicate {keyframes.Count} keyframes.");
|
||||
foreach (ITimelineKeyframeViewModel timelineKeyframeViewModel in keyframes)
|
||||
timelineKeyframeViewModel.Duplicate();
|
||||
}
|
||||
PropertyGroupViewModels.SelectMany(g => g.GetAllKeyframeViewModels(true)).FirstOrDefault(k => k.IsSelected)?.Duplicate.Execute().Subscribe();
|
||||
}
|
||||
|
||||
public void CopyKeyframes(ITimelineKeyframeViewModel? source = null)
|
||||
private void ExecuteCopySelectedKeyframes()
|
||||
{
|
||||
if (source is {IsSelected: false})
|
||||
{
|
||||
source.Copy();
|
||||
}
|
||||
else
|
||||
{
|
||||
List<ITimelineKeyframeViewModel> keyframes = PropertyGroupViewModels.SelectMany(g => g.GetAllKeyframeViewModels(true)).Where(k => k.IsSelected).ToList();
|
||||
using ProfileEditorCommandScope scope = _profileEditorService.CreateCommandScope($"Copy {keyframes.Count} keyframes.");
|
||||
foreach (ITimelineKeyframeViewModel timelineKeyframeViewModel in keyframes)
|
||||
timelineKeyframeViewModel.Copy();
|
||||
}
|
||||
PropertyGroupViewModels.SelectMany(g => g.GetAllKeyframeViewModels(true)).FirstOrDefault(k => k.IsSelected)?.Copy.Execute().Subscribe();
|
||||
}
|
||||
|
||||
public void PasteKeyframes(ITimelineKeyframeViewModel? source = null)
|
||||
private async Task ExecutePasteKeyframes()
|
||||
{
|
||||
if (source is {IsSelected: false})
|
||||
{
|
||||
source.Paste();
|
||||
}
|
||||
else
|
||||
{
|
||||
List<ITimelineKeyframeViewModel> keyframes = PropertyGroupViewModels.SelectMany(g => g.GetAllKeyframeViewModels(true)).Where(k => k.IsSelected).ToList();
|
||||
using ProfileEditorCommandScope scope = _profileEditorService.CreateCommandScope($"Paste {keyframes.Count} keyframes.");
|
||||
foreach (ITimelineKeyframeViewModel timelineKeyframeViewModel in keyframes)
|
||||
timelineKeyframeViewModel.Paste();
|
||||
}
|
||||
if (_profileElement == null || Application.Current?.Clipboard == null)
|
||||
return;
|
||||
|
||||
List<KeyframeClipboardModel>? keyframes = await Application.Current.Clipboard.GetJsonAsync<List<KeyframeClipboardModel>>(KeyframeClipboardModel.ClipboardDataFormat);
|
||||
if (keyframes == null)
|
||||
return;
|
||||
|
||||
PasteKeyframes command = new(_profileElement, keyframes, _time);
|
||||
_profileEditorService.ExecuteCommand(command);
|
||||
if (command.PastedKeyframes != null && command.PastedKeyframes.Any())
|
||||
_profileEditorService.SelectKeyframes(command.PastedKeyframes, false);
|
||||
}
|
||||
|
||||
public void DeleteKeyframes(ITimelineKeyframeViewModel? source = null)
|
||||
private void ExecuteDeleteSelectedKeyframes()
|
||||
{
|
||||
if (source is {IsSelected: false})
|
||||
{
|
||||
source.Delete();
|
||||
}
|
||||
else
|
||||
{
|
||||
List<ITimelineKeyframeViewModel> keyframes = PropertyGroupViewModels.SelectMany(g => g.GetAllKeyframeViewModels(true)).Where(k => k.IsSelected).ToList();
|
||||
using ProfileEditorCommandScope scope = _profileEditorService.CreateCommandScope($"Delete {keyframes.Count} keyframes.");
|
||||
foreach (ITimelineKeyframeViewModel timelineKeyframeViewModel in keyframes)
|
||||
timelineKeyframeViewModel.Delete();
|
||||
}
|
||||
PropertyGroupViewModels.SelectMany(g => g.GetAllKeyframeViewModels(true)).FirstOrDefault(k => k.IsSelected)?.Delete.Execute().Subscribe();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -44,8 +44,10 @@ public class VisualEditorViewModel : ActivatableViewModelBase
|
||||
profileEditorService.ProfileConfiguration.Subscribe(CreateVisualizers).DisposeWith(d);
|
||||
|
||||
profileEditorService.Tools
|
||||
.Connect()
|
||||
.AutoRefreshOnObservable(t => t.WhenAnyValue(vm => vm.IsSelected)).Filter(t => t.IsSelected).Bind(out ReadOnlyObservableCollection<IToolViewModel> tools)
|
||||
.ToObservableChangeSet()
|
||||
.AutoRefreshOnObservable(t => t.WhenAnyValue(vm => vm.IsSelected))
|
||||
.Filter(t => t.IsSelected)
|
||||
.Bind(out ReadOnlyObservableCollection<IToolViewModel> tools)
|
||||
.Subscribe()
|
||||
.DisposeWith(d);
|
||||
Tools = tools;
|
||||
|
||||
@@ -51,7 +51,8 @@ public class ProfileEditorViewModel : MainScreenViewModel
|
||||
_profileConfiguration = profileEditorService.ProfileConfiguration.ToProperty(this, vm => vm.ProfileConfiguration).DisposeWith(d);
|
||||
_history = profileEditorService.History.ToProperty(this, vm => vm.History).DisposeWith(d);
|
||||
_suspendedEditing = profileEditorService.SuspendedEditing.ToProperty(this, vm => vm.SuspendedEditing).DisposeWith(d);
|
||||
profileEditorService.Tools.Connect()
|
||||
profileEditorService.Tools
|
||||
.ToObservableChangeSet()
|
||||
.Filter(t => t.ShowInToolbar)
|
||||
.Sort(SortExpressionComparer<IToolViewModel>.Ascending(vm => vm.Order))
|
||||
.Bind(out ReadOnlyObservableCollection<IToolViewModel> tools)
|
||||
|
||||
Reference in new issue
Block a user