Sidebar - Refactored category VM to fix all issues with it I could find

Sidebar - Streamlined design
Sidebar - Increase width on wide window sizes
Profile editor - Implement File and Help menus
This commit is contained in:
Robert committed 2022-05-14 22:57:53 +02:00
1 parent 35f83e58e5
commit 78479c0fa2
79 files changed
+1093 -946

No files matched your search

@@ -27,7 +27,7 @@
HorizontalAlignment="Right"
Margin="10"
Command="{Binding OpenEditor}">
<avalonia:MaterialIcon Kind="OpenInNew"></avalonia:MaterialIcon>
<avalonia:MaterialIcon Kind="OpenInNew" />
</Button>
</Grid>
@@ -42,7 +42,7 @@
<TextBlock TextWrapping="Wrap" HorizontalAlignment="Center" TextAlignment="Center" Foreground="{DynamicResource TextFillColorSecondary}">
When you enable data bindings you can no longer use keyframes or normal values for this property.
</TextBlock>
<controls:HyperlinkButton HorizontalAlignment="Center"
<controls:HyperlinkButton HorizontalAlignment="Center"
NavigateUri="https://wiki.artemis-rgb.com/en/guides/user/profiles/data-bindings"
Margin="0 10">
Learn more
@@ -1,18 +1,17 @@
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.Properties.DataBinding
{
public partial class DataBindingView : ReactiveUserControl<DataBindingViewModel>
{
public DataBindingView()
{
InitializeComponent();
}
namespace Artemis.UI.Screens.ProfileEditor.Properties.DataBinding;
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public class DataBindingView : ReactiveUserControl<DataBindingViewModel>
{
public DataBindingView()
{
InitializeComponent();
}
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
@@ -11,7 +11,7 @@
x:DataType="dialogs:AddEffectViewModel"
Width="500">
<Grid RowDefinitions="Auto,*">
<TextBox Name="SearchBox" Text="{CompiledBinding SearchText}" Margin="0 0 0 15" Watermark="Search"></TextBox>
<TextBox Name="SearchBox" Text="{CompiledBinding SearchText}" Margin="0 0 0 15" Watermark="Search" />
<ListBox Name="EffectDescriptorsList"
Grid.Row="1"
Items="{CompiledBinding LayerEffectDescriptors}"
@@ -21,17 +21,17 @@
<ListBox.DataTemplates>
<DataTemplate DataType="{x:Type layerEffects:LayerEffectDescriptor}">
<Grid RowDefinitions="Auto,*"
ColumnDefinitions="Auto,Auto"
Background="Transparent"
ColumnDefinitions="Auto,Auto"
Background="Transparent"
PointerReleased="InputElement_OnPointerReleased"
Margin="0 4"
VerticalAlignment="Center">
<shared:ArtemisIcon Grid.Column="0"
Grid.RowSpan="2"
<shared:ArtemisIcon Grid.Column="0"
Grid.RowSpan="2"
Icon="{CompiledBinding Icon}"
Width="24"
Height="24"
VerticalAlignment="Center"
Height="24"
VerticalAlignment="Center"
Margin="0 0 15 0" />
<TextBlock Grid.Column="1"
Grid.Row="0"
@@ -51,14 +51,14 @@
</DataTemplate>
</ListBox.DataTemplates>
</ListBox>
<Grid Grid.Row="1" Height="300">
<Grid Grid.Row="1" Height="300">
<StackPanel VerticalAlignment="Center"
Spacing="20"
IsVisible="{CompiledBinding !LayerEffectDescriptors.Count}">
<avalonia:MaterialIcon Kind="CloseCircle" Width="32" Height="32"></avalonia:MaterialIcon>
<avalonia:MaterialIcon Kind="CloseCircle" Width="32" Height="32" />
<TextBlock Classes="h5" TextAlignment="Center">None of the effects match your search</TextBlock>
</StackPanel>
</Grid>
</Grid>
</Grid>
</Grid>
</UserControl>
@@ -14,8 +14,8 @@ namespace Artemis.UI.Screens.ProfileEditor.Properties.Dialogs;
public class AddEffectViewModel : ContentDialogViewModelBase
{
private readonly RenderProfileElement _renderProfileElement;
private readonly IProfileEditorService _profileEditorService;
private readonly RenderProfileElement _renderProfileElement;
private string? _searchText;
public AddEffectViewModel(RenderProfileElement renderProfileElement, IProfileEditorService profileEditorService, ILayerEffectService layerEffectService)
@@ -39,7 +39,7 @@ public class AddEffectViewModel : ContentDialogViewModelBase
public string? SearchText
{
get => _searchText;
set => this.RaiseAndSetIfChanged(ref _searchText, value);
set => RaiseAndSetIfChanged(ref _searchText, value);
}
public void AddLayerEffect(LayerEffectDescriptor descriptor)
@@ -58,5 +58,4 @@ public class AddEffectViewModel : ContentDialogViewModelBase
return data => data.DisplayName.Contains(search, StringComparison.InvariantCultureIgnoreCase) ||
data.Description.Contains(search, StringComparison.InvariantCultureIgnoreCase);
}
}
}
@@ -5,7 +5,6 @@
xmlns:controls="clr-namespace:Artemis.UI.Controls"
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="350"
x:Class="Artemis.UI.Screens.ProfileEditor.Properties.PropertiesView"
x:DataType="local:PropertiesViewModel">
@@ -13,7 +12,7 @@
<StyleInclude Source="/Screens/ProfileEditor/Panels/Properties/Timeline/Segments/Segment.axaml" />
</UserControl.Styles>
<UserControl.Resources>
<converters:DoubleToGridLengthConverter x:Key="DoubleToGridLengthConverter"></converters:DoubleToGridLengthConverter>
<converters:DoubleToGridLengthConverter x:Key="DoubleToGridLengthConverter" />
</UserControl.Resources>
<Grid Name="ContainerGrid">
<Grid.ColumnDefinitions>
@@ -89,8 +88,7 @@
StartPoint="0,0"
EndPoint="{CompiledBinding #ContainerGrid.Bounds.BottomLeft}"
StrokeThickness="2"
Stroke="{DynamicResource SystemAccentColorLight1}">
</Line>
Stroke="{DynamicResource SystemAccentColorLight1}" />
<Line Name="StartSegmentLine"
Canvas.Left="{CompiledBinding TimelineViewModel.StartSegmentViewModel.EndX}"
@@ -100,8 +98,7 @@
StrokeThickness="2"
Stroke="{DynamicResource SystemAccentColorLight1}"
StrokeDashArray="6,2"
Opacity="0.5">
</Line>
Opacity="0.5" />
<Line Name="MainSegmentLine"
Canvas.Left="{CompiledBinding TimelineViewModel.MainSegmentViewModel.EndX}"
IsVisible="{CompiledBinding !TimelineViewModel.MainSegmentViewModel.ShowAddMain}"
@@ -110,8 +107,7 @@
StrokeThickness="2"
Stroke="{DynamicResource SystemAccentColorLight1}"
StrokeDashArray="6,2"
Opacity="0.5">
</Line>
Opacity="0.5" />
<Line Name="EndSegmentLine"
Canvas.Left="{CompiledBinding TimelineViewModel.EndSegmentViewModel.EndX}"
IsVisible="{CompiledBinding !TimelineViewModel.MainSegmentViewModel.ShowAddEnd}"
@@ -120,8 +116,7 @@
StrokeThickness="2"
Stroke="{DynamicResource SystemAccentColorLight1}"
StrokeDashArray="6,2"
Opacity="0.5">
</Line>
Opacity="0.5" />
<!-- Timeline segments -->
<ContentControl Canvas.Left="{CompiledBinding TimelineViewModel.EndSegmentViewModel.StartX}"
@@ -142,8 +137,7 @@
PointerReleased="TimelineCaret_OnPointerReleased"
PointerMoved="TimelineCaret_OnPointerMoved"
Points="-8,0 -8,8 0,20, 8,8 8,0"
Fill="{DynamicResource SystemAccentColorLight1}">
</Polygon>
Fill="{DynamicResource SystemAccentColorLight1}" />
</Canvas>
<ScrollViewer Grid.Row="1"
@@ -15,10 +15,8 @@ using Artemis.UI.Screens.ProfileEditor.Playback;
using Artemis.UI.Screens.ProfileEditor.Properties.DataBinding;
using Artemis.UI.Screens.ProfileEditor.Properties.Dialogs;
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline;
using Artemis.UI.Screens.Sidebar;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
using Artemis.UI.Shared.Services.ProfileEditor;
using ReactiveUI;
@@ -28,11 +26,11 @@ public class PropertiesViewModel : ActivatableViewModelBase
{
private readonly Dictionary<LayerPropertyGroup, PropertyGroupViewModel> _cachedPropertyViewModels;
private readonly IDataBindingVmFactory _dataBindingVmFactory;
private readonly IWindowService _windowService;
private readonly ILayerEffectService _layerEffectService;
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
private readonly IProfileEditorService _profileEditorService;
private readonly ISettingsService _settingsService;
private readonly IWindowService _windowService;
private DataBindingViewModel? _backgroundDataBindingViewModel;
private DataBindingViewModel? _dataBindingViewModel;
private ObservableAsPropertyHelper<ILayerProperty?>? _layerProperty;
@@ -16,4 +16,4 @@
Margin="0 0 10 0" />
<TextBlock Text="{CompiledBinding Description}" />
</StackPanel>
</UserControl>
</UserControl>
@@ -1,18 +1,17 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes
{
public partial class TimelineEasingView : UserControl
{
public TimelineEasingView()
{
InitializeComponent();
}
namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes;
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public class TimelineEasingView : UserControl
{
public TimelineEasingView()
{
InitializeComponent();
}
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
@@ -44,8 +44,8 @@
</Template>
</Setter.Value>
</Setter>
<Setter Property="Command" Value="{Binding $parent[UserControl].DataContext.SelectEasingFunction}"></Setter>
<Setter Property="CommandParameter" Value="{Binding EasingFunction}"></Setter>
<Setter Property="Command" Value="{Binding $parent[UserControl].DataContext.SelectEasingFunction}" />
<Setter Property="CommandParameter" Value="{Binding EasingFunction}" />
</Style>
</MenuItem.Styles>
<MenuItem.Icon>
@@ -8,9 +8,9 @@ namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes;
public class TimelineKeyframeView : ReactiveUserControl<ITimelineKeyframeViewModel>
{
private bool _moved;
private TimelinePropertyView? _timelinePropertyView;
private TimelineView? _timelineView;
private bool _moved;
public TimelineKeyframeView()
{
@@ -70,7 +70,9 @@ public class TimelineKeyframeView : ReactiveUserControl<ITimelineKeyframeViewMod
// Select the keyframe if the user didn't move
if (!_moved)
{
ViewModel.Select(e.KeyModifiers.HasFlag(KeyModifiers.Shift), e.KeyModifiers.HasFlag(KeyModifiers.Control));
}
else
{
TimeSpan time = ViewModel.GetTimeSpanAtPosition(e.GetPosition(_timelinePropertyView).X);
@@ -15,10 +15,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes;
public class TimelineKeyframeViewModel<T> : ActivatableViewModelBase, ITimelineKeyframeViewModel
{
private readonly IProfileEditorService _profileEditorService;
private ObservableAsPropertyHelper<bool>? _isSelected;
private string _timestamp;
private double _x;
private string _timestamp;
private ObservableAsPropertyHelper<bool>? _isSelected;
public TimelineKeyframeViewModel(LayerPropertyKeyframe<T> layerPropertyKeyframe, IProfileEditorService profileEditorService)
{
@@ -56,16 +56,16 @@ public class TimelineKeyframeViewModel<T> : ActivatableViewModelBase, ITimelineK
set => RaiseAndSetIfChanged(ref _timestamp, value);
}
public bool IsSelected => _isSelected?.Value ?? false;
public TimeSpan Position => LayerPropertyKeyframe.Position;
public ILayerPropertyKeyframe Keyframe => LayerPropertyKeyframe;
public void Update()
{
X = _pixelsPerSecond * LayerPropertyKeyframe.Position.TotalSeconds;
Timestamp = $"{Math.Floor(LayerPropertyKeyframe.Position.TotalSeconds):00}.{LayerPropertyKeyframe.Position.Milliseconds:000}";
}
public bool IsSelected => _isSelected?.Value ?? false;
public TimeSpan Position => LayerPropertyKeyframe.Position;
public ILayerPropertyKeyframe Keyframe => LayerPropertyKeyframe;
/// <inheritdoc />
public void Select(bool expand, bool toggle)
{
@@ -157,12 +157,11 @@ public class TimelineKeyframeViewModel<T> : ActivatableViewModelBase, ITimelineK
.Cast<Easings.Functions>()
.Select(e => new TimelineEasingViewModel(e, Keyframe)));
}
public void SelectEasingFunction(Easings.Functions easingFunction)
{
_profileEditorService.ExecuteCommand(new ChangeKeyframeEasing(Keyframe, easingFunction));
}
#endregion
}
}
@@ -28,7 +28,7 @@
IsVisible="{CompiledBinding ShowAddMain}">
<avalonia:MaterialIcon Kind="PlusCircle" />
</Button>
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" ClipToBounds="True">
<TextBlock Name="SegmentTitle"
FontSize="13"
@@ -43,7 +43,7 @@
</Button>
</StackPanel>
<Rectangle Name="KeyframeDragVisualRight"
<Rectangle Name="KeyframeDragVisualRight"
Grid.Column="3"
Classes="resize-visual" />
<Rectangle Name="KeyframeDragAnchor"
@@ -52,7 +52,7 @@
PointerPressed="KeyframeDragAnchor_OnPointerPressed"
PointerMoved="KeyframeDragAnchor_OnPointerMoved"
PointerReleased="KeyframeDragAnchor_OnPointerReleased"
ToolTip.Tip="{CompiledBinding EndTimestamp}"/>
ToolTip.Tip="{CompiledBinding EndTimestamp}" />
</Grid>
</Border>
</UserControl>
@@ -4,47 +4,46 @@ using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Segments
namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Segments;
public class EndSegmentView : ReactiveUserControl<EndSegmentViewModel>
{
public partial class EndSegmentView : ReactiveUserControl<EndSegmentViewModel>
private readonly Rectangle _keyframeDragAnchor;
private double _dragOffset;
public EndSegmentView()
{
private readonly Rectangle _keyframeDragAnchor;
private double _dragOffset;
InitializeComponent();
_keyframeDragAnchor = this.Get<Rectangle>("KeyframeDragAnchor");
}
public EndSegmentView()
{
InitializeComponent();
_keyframeDragAnchor = this.Get<Rectangle>("KeyframeDragAnchor");
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void KeyframeDragAnchor_OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (ViewModel == null || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
return;
e.Pointer.Capture(_keyframeDragAnchor);
private void KeyframeDragAnchor_OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (ViewModel == null || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
return;
e.Pointer.Capture(_keyframeDragAnchor);
_dragOffset = ViewModel.Width - e.GetCurrentPoint(this).Position.X;
ViewModel.StartResize();
}
_dragOffset = ViewModel.Width - e.GetCurrentPoint(this).Position.X;
ViewModel.StartResize();
}
private void KeyframeDragAnchor_OnPointerMoved(object? sender, PointerEventArgs e)
{
if (ViewModel == null || !ReferenceEquals(e.Pointer.Captured, _keyframeDragAnchor))
return;
ViewModel.UpdateResize(e.GetCurrentPoint(this).Position.X + _dragOffset);
}
private void KeyframeDragAnchor_OnPointerMoved(object? sender, PointerEventArgs e)
{
if (ViewModel == null || !ReferenceEquals(e.Pointer.Captured, _keyframeDragAnchor))
return;
ViewModel.UpdateResize(e.GetCurrentPoint(this).Position.X + _dragOffset);
}
private void KeyframeDragAnchor_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (ViewModel == null || !ReferenceEquals(e.Pointer.Captured, _keyframeDragAnchor))
return;
e.Pointer.Capture(null);
ViewModel.FinishResize(e.GetCurrentPoint(this).Position.X + _dragOffset);
}
private void KeyframeDragAnchor_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (ViewModel == null || !ReferenceEquals(e.Pointer.Captured, _keyframeDragAnchor))
return;
e.Pointer.Capture(null);
ViewModel.FinishResize(e.GetCurrentPoint(this).Position.X + _dragOffset);
}
}
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using Artemis.Core;
using Artemis.UI.Shared.Services.ProfileEditor;
@@ -13,14 +11,14 @@ namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Segments;
public class EndSegmentViewModel : TimelineSegmentViewModel
{
private readonly IProfileEditorService _profileEditorService;
private RenderProfileElement? _profileElement;
private int _pixelsPerSecond;
private TimeSpan _time;
private ObservableAsPropertyHelper<double>? _start;
private readonly ObservableAsPropertyHelper<double> _width;
private ObservableAsPropertyHelper<double>? _end;
private ObservableAsPropertyHelper<string?>? _endTimestamp;
private readonly ObservableAsPropertyHelper<double> _width;
private TimeSpan _initialLength;
private int _pixelsPerSecond;
private RenderProfileElement? _profileElement;
private ObservableAsPropertyHelper<double>? _start;
private TimeSpan _time;
public EndSegmentViewModel(IProfileEditorService profileEditorService) : base(profileEditorService)
@@ -59,6 +57,7 @@ public class EndSegmentViewModel : TimelineSegmentViewModel
public override double StartX => _start?.Value ?? 0;
public override TimeSpan End => _profileElement?.Timeline.EndSegmentEndPosition ?? TimeSpan.Zero;
public override double EndX => _end?.Value ?? 0;
public override TimeSpan Length
{
get => _profileElement?.Timeline.EndSegmentLength ?? TimeSpan.Zero;
@@ -70,7 +69,7 @@ public class EndSegmentViewModel : TimelineSegmentViewModel
}
public override double Width => _width.Value;
public override string? EndTimestamp => _endTimestamp?.Value;
public override ResizeTimelineSegment.SegmentType Type => ResizeTimelineSegment.SegmentType.End;
}
@@ -58,7 +58,7 @@
<avalonia:MaterialIcon Kind="PlusCircle" />
</Button>
<Rectangle Name="KeyframeDragVisual"
<Rectangle Name="KeyframeDragVisual"
Grid.Column="4"
Classes="resize-visual" />
<Rectangle Name="KeyframeDragAnchor"
@@ -67,7 +67,7 @@
PointerPressed="KeyframeDragAnchor_OnPointerPressed"
PointerMoved="KeyframeDragAnchor_OnPointerMoved"
PointerReleased="KeyframeDragAnchor_OnPointerReleased"
ToolTip.Tip="{CompiledBinding EndTimestamp}"/>
ToolTip.Tip="{CompiledBinding EndTimestamp}" />
</Grid>
</Border>
</UserControl>
@@ -4,59 +4,58 @@ using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Segments
namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Segments;
public class MainSegmentView : ReactiveUserControl<MainSegmentViewModel>
{
public partial class MainSegmentView : ReactiveUserControl<MainSegmentViewModel>
private readonly Rectangle _keyframeDragAnchor;
private double _dragOffset;
public MainSegmentView()
{
private readonly Rectangle _keyframeDragAnchor;
private double _dragOffset;
InitializeComponent();
_keyframeDragAnchor = this.Get<Rectangle>("KeyframeDragAnchor");
}
public MainSegmentView()
{
InitializeComponent();
_keyframeDragAnchor = this.Get<Rectangle>("KeyframeDragAnchor");
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void KeyframeDragAnchor_OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (ViewModel == null || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
return;
e.Pointer.Capture(_keyframeDragAnchor);
private void KeyframeDragAnchor_OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (ViewModel == null || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
return;
e.Pointer.Capture(_keyframeDragAnchor);
_dragOffset = ViewModel.Width - e.GetCurrentPoint(this).Position.X;
ViewModel.StartResize();
}
_dragOffset = ViewModel.Width - e.GetCurrentPoint(this).Position.X;
ViewModel.StartResize();
}
private void KeyframeDragAnchor_OnPointerMoved(object? sender, PointerEventArgs e)
{
if (ViewModel == null || !ReferenceEquals(e.Pointer.Captured, _keyframeDragAnchor))
return;
ViewModel.UpdateResize(e.GetCurrentPoint(this).Position.X + _dragOffset);
}
private void KeyframeDragAnchor_OnPointerMoved(object? sender, PointerEventArgs e)
{
if (ViewModel == null || !ReferenceEquals(e.Pointer.Captured, _keyframeDragAnchor))
return;
ViewModel.UpdateResize(e.GetCurrentPoint(this).Position.X + _dragOffset);
}
private void KeyframeDragAnchor_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (ViewModel == null || !ReferenceEquals(e.Pointer.Captured, _keyframeDragAnchor))
return;
e.Pointer.Capture(null);
ViewModel.FinishResize(e.GetCurrentPoint(this).Position.X + _dragOffset);
}
private void KeyframeDragAnchor_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (ViewModel == null || !ReferenceEquals(e.Pointer.Captured, _keyframeDragAnchor))
return;
e.Pointer.Capture(null);
ViewModel.FinishResize(e.GetCurrentPoint(this).Position.X + _dragOffset);
}
private void KeyframeDragStartAnchor_OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
}
private void KeyframeDragStartAnchor_OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
}
private void KeyframeDragStartAnchor_OnPointerMoved(object? sender, PointerEventArgs e)
{
}
private void KeyframeDragStartAnchor_OnPointerMoved(object? sender, PointerEventArgs e)
{
}
private void KeyframeDragStartAnchor_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
}
private void KeyframeDragStartAnchor_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
}
}
@@ -6,23 +6,22 @@
</Border>
</Design.PreviewWith>
<Style Selector="ContentControl.segment-content-control">
</Style>
<Style Selector="ContentControl.segment-content-control" />
<Style Selector="Border.segment-container">
<Setter Property="Height" Value="18"></Setter>
<Setter Property="Height" Value="18" />
</Style>
<Style Selector="Rectangle.resize-visual">
<Setter Property="Fill" Value="{DynamicResource SystemAccentColorLight2}"/>
<Setter Property="Width" Value="4"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="Fill" Value="{DynamicResource SystemAccentColorLight2}" />
<Setter Property="Width" Value="4" />
<Setter Property="HorizontalAlignment" Value="Right" />
</Style>
<Style Selector="Rectangle.resize-anchor">
<Setter Property="Fill" Value="Transparent"/>
<Setter Property="Width" Value="10"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="Cursor" Value="SizeWestEast"/>
<Setter Property="Fill" Value="Transparent" />
<Setter Property="Width" Value="10" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="Cursor" Value="SizeWestEast" />
</Style>
</Styles>
</Styles>
@@ -2,7 +2,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:segments="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Segments"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="18"
@@ -49,7 +48,7 @@
PointerPressed="KeyframeDragAnchor_OnPointerPressed"
PointerMoved="KeyframeDragAnchor_OnPointerMoved"
PointerReleased="KeyframeDragAnchor_OnPointerReleased"
ToolTip.Tip="{CompiledBinding EndTimestamp}"/>
ToolTip.Tip="{CompiledBinding EndTimestamp}" />
</Grid>
</Border>
@@ -4,47 +4,46 @@ using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Segments
namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Segments;
public class StartSegmentView : ReactiveUserControl<StartSegmentViewModel>
{
public partial class StartSegmentView : ReactiveUserControl<StartSegmentViewModel>
private readonly Rectangle _keyframeDragAnchor;
private double _dragOffset;
public StartSegmentView()
{
private readonly Rectangle _keyframeDragAnchor;
private double _dragOffset;
InitializeComponent();
_keyframeDragAnchor = this.Get<Rectangle>("KeyframeDragAnchor");
}
public StartSegmentView()
{
InitializeComponent();
_keyframeDragAnchor = this.Get<Rectangle>("KeyframeDragAnchor");
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void KeyframeDragAnchor_OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (ViewModel == null || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
return;
e.Pointer.Capture(_keyframeDragAnchor);
private void KeyframeDragAnchor_OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (ViewModel == null || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
return;
e.Pointer.Capture(_keyframeDragAnchor);
_dragOffset = ViewModel.Width - e.GetCurrentPoint(this).Position.X;
ViewModel.StartResize();
}
_dragOffset = ViewModel.Width - e.GetCurrentPoint(this).Position.X;
ViewModel.StartResize();
}
private void KeyframeDragAnchor_OnPointerMoved(object? sender, PointerEventArgs e)
{
if (ViewModel == null || !ReferenceEquals(e.Pointer.Captured, _keyframeDragAnchor))
return;
ViewModel.UpdateResize(e.GetCurrentPoint(this).Position.X + _dragOffset);
}
private void KeyframeDragAnchor_OnPointerMoved(object? sender, PointerEventArgs e)
{
if (ViewModel == null || !ReferenceEquals(e.Pointer.Captured, _keyframeDragAnchor))
return;
ViewModel.UpdateResize(e.GetCurrentPoint(this).Position.X + _dragOffset);
}
private void KeyframeDragAnchor_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (ViewModel == null || !ReferenceEquals(e.Pointer.Captured, _keyframeDragAnchor))
return;
e.Pointer.Capture(null);
ViewModel.FinishResize(e.GetCurrentPoint(this).Position.X + _dragOffset);
}
private void KeyframeDragAnchor_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (ViewModel == null || !ReferenceEquals(e.Pointer.Captured, _keyframeDragAnchor))
return;
e.Pointer.Capture(null);
ViewModel.FinishResize(e.GetCurrentPoint(this).Position.X + _dragOffset);
}
}
@@ -1,12 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using Artemis.Core;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
using Avalonia.Controls.Mixins;
using Castle.DynamicProxy.Generators;
using ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Segments;
@@ -14,13 +11,13 @@ namespace Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Segments;
public class StartSegmentViewModel : TimelineSegmentViewModel
{
private readonly IProfileEditorService _profileEditorService;
private RenderProfileElement? _profileElement;
private int _pixelsPerSecond;
private TimeSpan _time;
private readonly ObservableAsPropertyHelper<double> _width;
private ObservableAsPropertyHelper<double>? _end;
private ObservableAsPropertyHelper<string?>? _endTimestamp;
private readonly ObservableAsPropertyHelper<double> _width;
private TimeSpan _initialLength;
private int _pixelsPerSecond;
private RenderProfileElement? _profileElement;
private TimeSpan _time;
public StartSegmentViewModel(IProfileEditorService profileEditorService) : base(profileEditorService)
{
@@ -52,6 +49,7 @@ public class StartSegmentViewModel : TimelineSegmentViewModel
public override double StartX => 0;
public override TimeSpan End => _profileElement?.Timeline.StartSegmentEndPosition ?? TimeSpan.Zero;
public override double EndX => _end?.Value ?? 0;
public override TimeSpan Length
{
get => _profileElement?.Timeline.StartSegmentLength ?? TimeSpan.Zero;
@@ -15,14 +15,14 @@ public abstract class TimelineSegmentViewModel : ActivatableViewModelBase
{
private static readonly TimeSpan NewSegmentLength = TimeSpan.FromSeconds(2);
private readonly IProfileEditorService _profileEditorService;
private RenderProfileElement? _profileElement;
private TimeSpan _initialLength;
private readonly Dictionary<ILayerPropertyKeyframe, TimeSpan> _originalKeyframePositions = new();
private int _pixelsPerSecond;
private Dictionary<ILayerPropertyKeyframe, TimeSpan> _originalKeyframePositions = new();
private RenderProfileElement? _profileElement;
private ObservableAsPropertyHelper<bool>? _showAddEnd;
private ObservableAsPropertyHelper<bool>? _showAddMain;
private ObservableAsPropertyHelper<bool>? _showAddStart;
private ObservableAsPropertyHelper<bool>? _showAddMain;
private ObservableAsPropertyHelper<bool>? _showAddEnd;
private TimeSpan _initialLength;
protected TimelineSegmentViewModel(IProfileEditorService profileEditorService)
{
@@ -136,8 +136,10 @@ public abstract class TimelineSegmentViewModel : ActivatableViewModelBase
// Delete keyframes in the segment
foreach (ILayerPropertyKeyframe layerPropertyKeyframe in keyframes)
{
if (layerPropertyKeyframe.Position > Start && layerPropertyKeyframe.Position <= End)
_profileEditorService.ExecuteCommand(new DeleteKeyframe(layerPropertyKeyframe));
}
// Move keyframes after the segment forwards
ShiftKeyframes(keyframes.Where(s => s.Position > End), new TimeSpan(Length.Ticks * -1));
@@ -8,13 +8,11 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Timeline.TimelineView"
x:DataType="timeline:TimelineViewModel">
<UserControl.Resources>
<x:Double x:Key="RailsHeight">28</x:Double>
<x:Double x:Key="RailsBorderHeight">29</x:Double>
</UserControl.Resources>
<UserControl.Resources>
</UserControl.Resources>
<Grid Background="Transparent" PointerReleased="InputElement_OnPointerReleased" Focusable="True" MinWidth="{Binding MinWidth}">
<Grid.KeyBindings>
<KeyBinding Command="{Binding DeleteKeyframes}" Gesture="Delete"/>
<KeyBinding Command="{Binding DeleteKeyframes}" Gesture="Delete" />
</Grid.KeyBindings>
<ItemsControl Items="{CompiledBinding PropertyGroupViewModels}">
<ItemsControl.ItemTemplate>
@@ -23,8 +21,8 @@
</TreeDataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<shared:SelectionRectangle Name="SelectionRectangle" InputElement="{CompiledBinding $parent}" SelectionFinished="SelectionRectangle_OnSelectionFinished"></shared:SelectionRectangle>
</Grid>
<shared:SelectionRectangle Name="SelectionRectangle" InputElement="{CompiledBinding $parent}" SelectionFinished="SelectionRectangle_OnSelectionFinished" />
</Grid>
</UserControl>
@@ -3,12 +3,10 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Keyframes;
using Artemis.UI.Screens.ProfileEditor.Properties.Timeline.Segments;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
using Avalonia.Controls.Mixins;
using ReactiveUI;
@@ -18,13 +16,13 @@ public class TimelineViewModel : ActivatableViewModelBase
{
private readonly IProfileEditorService _profileEditorService;
private ObservableAsPropertyHelper<double>? _caretPosition;
private ObservableAsPropertyHelper<int>? _pixelsPerSecond;
private List<ITimelineKeyframeViewModel>? _moveKeyframes;
private ObservableAsPropertyHelper<double> _minWidth;
private List<ITimelineKeyframeViewModel>? _moveKeyframes;
private ObservableAsPropertyHelper<int>? _pixelsPerSecond;
public TimelineViewModel(ObservableCollection<PropertyGroupViewModel> propertyGroupViewModels,
StartSegmentViewModel startSegmentViewModel,
MainSegmentViewModel mainSegmentViewModel,
MainSegmentViewModel mainSegmentViewModel,
EndSegmentViewModel endSegmentViewModel,
IProfileEditorService profileEditorService)
{
@@ -141,8 +139,10 @@ public class TimelineViewModel : ActivatableViewModelBase
public void DuplicateKeyframes(ITimelineKeyframeViewModel? source = null)
{
if (source is { IsSelected: false })
if (source is {IsSelected: false})
{
source.Delete();
}
else
{
List<ITimelineKeyframeViewModel> keyframes = PropertyGroupViewModels.SelectMany(g => g.GetAllKeyframeViewModels(true)).Where(k => k.IsSelected).ToList();
@@ -154,8 +154,10 @@ public class TimelineViewModel : ActivatableViewModelBase
public void CopyKeyframes(ITimelineKeyframeViewModel? source = null)
{
if (source is { IsSelected: false })
if (source is {IsSelected: false})
{
source.Copy();
}
else
{
List<ITimelineKeyframeViewModel> keyframes = PropertyGroupViewModels.SelectMany(g => g.GetAllKeyframeViewModels(true)).Where(k => k.IsSelected).ToList();
@@ -167,8 +169,10 @@ public class TimelineViewModel : ActivatableViewModelBase
public void PasteKeyframes(ITimelineKeyframeViewModel? source = null)
{
if (source is { IsSelected: false })
if (source is {IsSelected: false})
{
source.Paste();
}
else
{
List<ITimelineKeyframeViewModel> keyframes = PropertyGroupViewModels.SelectMany(g => g.GetAllKeyframeViewModels(true)).Where(k => k.IsSelected).ToList();
@@ -181,7 +185,9 @@ public class TimelineViewModel : ActivatableViewModelBase
public void DeleteKeyframes(ITimelineKeyframeViewModel? source = null)
{
if (source is {IsSelected: false})
{
source.Delete();
}
else
{
List<ITimelineKeyframeViewModel> keyframes = PropertyGroupViewModels.SelectMany(g => g.GetAllKeyframeViewModels(true)).Where(k => k.IsSelected).ToList();
@@ -10,6 +10,6 @@
<StackPanel.KeyBindings>
<KeyBinding Gesture="Enter" Command="{CompiledBinding Confirm}" />
</StackPanel.KeyBindings>
<TextBox Name="NameTextBox" Text="{CompiledBinding LayerEffectName}" Watermark="Layer effect name"/>
<TextBox Name="NameTextBox" Text="{CompiledBinding LayerEffectName}" Watermark="Layer effect name" />
</StackPanel>
</UserControl>
@@ -7,39 +7,38 @@ using FluentAvalonia.UI.Controls;
using ReactiveUI;
using ReactiveUI.Validation.Extensions;
namespace Artemis.UI.Screens.ProfileEditor.Properties.Tree.ContentDialogs
namespace Artemis.UI.Screens.ProfileEditor.Properties.Tree.ContentDialogs;
public class LayerEffectRenameViewModel : ContentDialogViewModelBase
{
public class LayerEffectRenameViewModel : ContentDialogViewModelBase
private readonly BaseLayerEffect _layerEffect;
private readonly IProfileEditorService _profileEditorService;
private string? _layerEffectName;
public LayerEffectRenameViewModel(IProfileEditorService profileEditorService, BaseLayerEffect layerEffect)
{
private readonly IProfileEditorService _profileEditorService;
private readonly BaseLayerEffect _layerEffect;
private string? _layerEffectName;
_profileEditorService = profileEditorService;
_layerEffect = layerEffect;
_layerEffectName = layerEffect.Name;
public LayerEffectRenameViewModel(IProfileEditorService profileEditorService, BaseLayerEffect layerEffect)
{
_profileEditorService = profileEditorService;
_layerEffect = layerEffect;
_layerEffectName = layerEffect.Name;
Confirm = ReactiveCommand.Create(ExecuteConfirm, ValidationContext.Valid);
this.ValidationRule(vm => vm.LayerEffectName, categoryName => !string.IsNullOrWhiteSpace(categoryName), "You must specify a valid name");
}
Confirm = ReactiveCommand.Create(ExecuteConfirm, ValidationContext.Valid);
this.ValidationRule(vm => vm.LayerEffectName, categoryName => !string.IsNullOrWhiteSpace(categoryName), "You must specify a valid name");
}
public string? LayerEffectName
{
get => _layerEffectName;
set => RaiseAndSetIfChanged(ref _layerEffectName, value);
}
public string? LayerEffectName
{
get => _layerEffectName;
set => this.RaiseAndSetIfChanged(ref _layerEffectName, value);
}
public ReactiveCommand<Unit, Unit> Confirm { get; }
public ReactiveCommand<Unit, Unit> Confirm { get; }
private void ExecuteConfirm()
{
if (LayerEffectName == null)
return;
private void ExecuteConfirm()
{
if (LayerEffectName == null)
return;
_profileEditorService.ExecuteCommand(new RenameLayerEffect(_layerEffect, LayerEffectName));
ContentDialog?.Hide(ContentDialogResult.Primary);
}
_profileEditorService.ExecuteCommand(new RenameLayerEffect(_layerEffect, LayerEffectName));
ContentDialog?.Hide(ContentDialogResult.Primary);
}
}
@@ -1,5 +1,4 @@
using System.Reactive;
using Artemis.Core;
using Artemis.Core;
using ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.Properties.Tree;
@@ -71,14 +71,14 @@
Height="29"
ColumnDefinitions="Auto,Auto,Auto,*">
<shared:ArtemisIcon Grid.Column="0"
Icon="{CompiledBinding LayerBrush.Descriptor.Icon}"
Width="16"
Height="16"
Margin="0 0 5 0" />
Icon="{CompiledBinding LayerBrush.Descriptor.Icon}"
Width="16"
Height="16"
Margin="0 0 5 0" />
<TextBlock Grid.Column="1"
ToolTip.Tip="{CompiledBinding LayerBrush.Descriptor.Description}"
Margin="0 5 5 0">
Brush -
Brush -
</TextBlock>
<TextBlock Grid.Column="2"
Text="{CompiledBinding LayerBrush.Descriptor.DisplayName}"
@@ -57,12 +57,25 @@ public class TreeGroupViewModel : ActivatableViewModelBase
public BaseLayerEffect? LayerEffect => PropertyGroupViewModel.LayerEffect;
public LayerPropertyGroupType GroupType { get; private set; }
public ObservableCollection<ViewModelBase>? Children => PropertyGroupViewModel.IsExpanded ? PropertyGroupViewModel.Children : null;
public ReactiveCommand<Unit, Unit> OpenBrushSettings { get; }
public ReactiveCommand<Unit, Unit> OpenEffectSettings { get; }
public ReactiveCommand<Unit, Unit> RenameEffect { get; }
public ReactiveCommand<Unit, Unit> DeleteEffect { get; }
public double GetDepth()
{
int depth = 0;
LayerPropertyGroup? current = LayerPropertyGroup.Parent;
while (current != null)
{
depth++;
current = current.Parent;
}
return depth;
}
private async Task ExecuteOpenBrushSettings()
{
if (LayerBrush?.ConfigurationDialog is not LayerBrushConfigurationDialog configurationViewModel)
@@ -145,19 +158,6 @@ public class TreeGroupViewModel : ActivatableViewModelBase
_profileEditorService.ExecuteCommand(new RemoveLayerEffect(LayerEffect));
}
public double GetDepth()
{
int depth = 0;
LayerPropertyGroup? current = LayerPropertyGroup.Parent;
while (current != null)
{
depth++;
current = current.Parent;
}
return depth;
}
private void CloseViewModels()
{
_effectConfigurationWindowViewModel?.Close(null);
@@ -4,7 +4,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:sharedConverters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
xmlns:tree="clr-namespace:Artemis.UI.Screens.ProfileEditor.Properties.Tree"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.ProfileEditor.Properties.Tree.TreePropertyView"
@@ -39,7 +38,7 @@
<ContentControl Grid.Column="2"
Margin="5 0"
Content="{Binding PropertyInputViewModel}"/>
Content="{Binding PropertyInputViewModel}" />
<Button Grid.Column="3"
Margin="0 0 2 0"
@@ -51,7 +50,7 @@
<avalonia:MaterialIcon Kind="BackupRestore" />
</Button>
<ToggleButton Grid.Column="4"
<ToggleButton Grid.Column="4"
Name="DataBindingToggleButton"
Classes="icon-button"
Margin="2 0 0 0"
@@ -15,9 +15,9 @@ namespace Artemis.UI.Screens.ProfileEditor.Properties.Tree;
internal class TreePropertyViewModel<T> : ActivatableViewModelBase, ITreePropertyViewModel
{
private readonly IProfileEditorService _profileEditorService;
private TimeSpan _time;
private ObservableAsPropertyHelper<bool>? _isCurrentlySelected;
private ObservableAsPropertyHelper<bool>? _dataBindingEnabled;
private ObservableAsPropertyHelper<bool>? _isCurrentlySelected;
private TimeSpan _time;
public TreePropertyViewModel(LayerProperty<T> layerProperty, PropertyViewModel propertyViewModel, IProfileEditorService profileEditorService, IPropertyInputService propertyInputService)
{
@@ -40,7 +40,6 @@ internal class TreePropertyViewModel<T> : ActivatableViewModelBase, ITreePropert
}
public bool IsCurrentlySelected => _isCurrentlySelected?.Value ?? false;
public bool DataBindingEnabled => _dataBindingEnabled?.Value ?? false;
public LayerProperty<T> LayerProperty { get; }
public PropertyViewModel PropertyViewModel { get; }
public PropertyInputViewModel<T>? PropertyInputViewModel { get; }
@@ -65,6 +64,8 @@ internal class TreePropertyViewModel<T> : ActivatableViewModelBase, ITreePropert
_profileEditorService.ExecuteCommand(new ResetLayerProperty<T>(LayerProperty));
}
public bool DataBindingEnabled => _dataBindingEnabled?.Value ?? false;
public ILayerProperty BaseLayerProperty => LayerProperty;
public double GetDepth()
@@ -2,7 +2,6 @@
using System.Threading.Tasks;
using Artemis.UI.Shared;
using Artemis.UI.Shared.LayerBrushes;
using Avalonia.Threading;
namespace Artemis.UI.Screens.ProfileEditor.Properties.Windows;
@@ -2,7 +2,6 @@
using System.Threading.Tasks;
using Artemis.UI.Shared;
using Artemis.UI.Shared.LayerEffects;
using Avalonia.Threading;
namespace Artemis.UI.Screens.ProfileEditor.Properties.Windows;