mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Profile editor - Ported timeline scaling, timeline header
This commit is contained in:
parent
098c44ebc8
commit
66a2e51979
@ -39,8 +39,7 @@ public interface IProfileEditorService : IArtemisSharedUIService
|
||||
/// <summary>
|
||||
/// Gets an observable of the zoom level.
|
||||
/// </summary>
|
||||
IObservable<double> PixelsPerSecond { get; }
|
||||
|
||||
IObservable<int> PixelsPerSecond { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Changes the selected profile by its <see cref="Core.ProfileConfiguration" />.
|
||||
@ -61,7 +60,14 @@ public interface IProfileEditorService : IArtemisSharedUIService
|
||||
void ChangeTime(TimeSpan time);
|
||||
|
||||
/// <summary>
|
||||
/// Snaps the given time to the closest relevant element in the timeline, this can be the cursor, a keyframe or a segment end.
|
||||
/// Changes the current pixels per second
|
||||
/// </summary>
|
||||
/// <param name="pixelsPerSecond">The new pixels per second.</param>
|
||||
void ChangePixelsPerSecond(int pixelsPerSecond);
|
||||
|
||||
/// <summary>
|
||||
/// Snaps the given time to the closest relevant element in the timeline, this can be the cursor, a keyframe or a
|
||||
/// segment end.
|
||||
/// </summary>
|
||||
/// <param name="time">The time to snap.</param>
|
||||
/// <param name="tolerance">How close the time must be to snap.</param>
|
||||
|
||||
@ -19,7 +19,7 @@ internal class ProfileEditorService : IProfileEditorService
|
||||
private readonly BehaviorSubject<TimeSpan> _timeSubject = new(TimeSpan.Zero);
|
||||
private readonly BehaviorSubject<bool> _playingSubject = new(false);
|
||||
private readonly BehaviorSubject<bool> _suspendedEditingSubject = new(false);
|
||||
private readonly BehaviorSubject<double> _pixelsPerSecondSubject = new(300);
|
||||
private readonly BehaviorSubject<int> _pixelsPerSecondSubject = new(120);
|
||||
private readonly ILogger _logger;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly IModuleService _moduleService;
|
||||
@ -59,7 +59,7 @@ internal class ProfileEditorService : IProfileEditorService
|
||||
public IObservable<TimeSpan> Time { get; }
|
||||
public IObservable<bool> Playing { get; }
|
||||
public IObservable<bool> SuspendedEditing { get; }
|
||||
public IObservable<double> PixelsPerSecond { get; }
|
||||
public IObservable<int> PixelsPerSecond { get; }
|
||||
|
||||
public void ChangeCurrentProfileConfiguration(ProfileConfiguration? profileConfiguration)
|
||||
{
|
||||
@ -147,7 +147,7 @@ internal class ProfileEditorService : IProfileEditorService
|
||||
return time;
|
||||
}
|
||||
|
||||
public void ChangePixelsPerSecond(double pixelsPerSecond)
|
||||
public void ChangePixelsPerSecond(int pixelsPerSecond)
|
||||
{
|
||||
_pixelsPerSecondSubject.OnNext(pixelsPerSecond);
|
||||
}
|
||||
|
||||
186
src/Avalonia/Artemis.UI/Controls/TimelineHeader.cs
Normal file
186
src/Avalonia/Artemis.UI/Controls/TimelineHeader.cs
Normal file
@ -0,0 +1,186 @@
|
||||
using System;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace Artemis.UI.Controls;
|
||||
|
||||
public class TimelineHeader : Control
|
||||
{
|
||||
public static readonly StyledProperty<Brush> ForegroundProperty = AvaloniaProperty.Register<TimelineHeader, Brush>(nameof(Foreground), new SolidColorBrush(Colors.Black));
|
||||
public static readonly StyledProperty<Brush> BackgroundProperty = AvaloniaProperty.Register<TimelineHeader, Brush>(nameof(Background), new SolidColorBrush(Colors.Transparent));
|
||||
public static readonly StyledProperty<FontFamily> FontFamilyProperty = AvaloniaProperty.Register<TimelineHeader, FontFamily>(nameof(FontFamily), FontFamily.Default);
|
||||
public static readonly StyledProperty<int> PixelsPerSecondProperty = AvaloniaProperty.Register<TimelineHeader, int>(nameof(PixelsPerSecond));
|
||||
public static readonly StyledProperty<double> HorizontalOffsetProperty = AvaloniaProperty.Register<TimelineHeader, double>(nameof(HorizontalOffset));
|
||||
public static readonly StyledProperty<double> VisibleWidthProperty = AvaloniaProperty.Register<TimelineHeader, double>(nameof(VisibleWidth));
|
||||
public static readonly StyledProperty<bool> OffsetFirstValueProperty = AvaloniaProperty.Register<TimelineHeader, bool>(nameof(OffsetFirstValue));
|
||||
|
||||
/// <inheritdoc />
|
||||
static TimelineHeader()
|
||||
{
|
||||
AffectsRender<TimelineHeader>(
|
||||
ForegroundProperty,
|
||||
BackgroundProperty,
|
||||
FontFamilyProperty,
|
||||
PixelsPerSecondProperty,
|
||||
HorizontalOffsetProperty,
|
||||
VisibleWidthProperty,
|
||||
OffsetFirstValueProperty
|
||||
);
|
||||
}
|
||||
|
||||
private double _subd1;
|
||||
private double _subd2;
|
||||
private double _subd3;
|
||||
|
||||
public Brush Foreground
|
||||
{
|
||||
get => GetValue(ForegroundProperty);
|
||||
set => SetValue(ForegroundProperty, value);
|
||||
}
|
||||
|
||||
public Brush Background
|
||||
{
|
||||
get => GetValue(BackgroundProperty);
|
||||
set => SetValue(BackgroundProperty, value);
|
||||
}
|
||||
|
||||
public FontFamily FontFamily
|
||||
{
|
||||
get => GetValue(FontFamilyProperty);
|
||||
set => SetValue(FontFamilyProperty, value);
|
||||
}
|
||||
|
||||
public int PixelsPerSecond
|
||||
{
|
||||
get => GetValue(PixelsPerSecondProperty);
|
||||
set => SetValue(PixelsPerSecondProperty, value);
|
||||
}
|
||||
|
||||
public double HorizontalOffset
|
||||
{
|
||||
get => GetValue(HorizontalOffsetProperty);
|
||||
set => SetValue(HorizontalOffsetProperty, value);
|
||||
}
|
||||
|
||||
public double VisibleWidth
|
||||
{
|
||||
get => GetValue(VisibleWidthProperty);
|
||||
set => SetValue(VisibleWidthProperty, value);
|
||||
}
|
||||
|
||||
public bool OffsetFirstValue
|
||||
{
|
||||
get => GetValue(OffsetFirstValueProperty);
|
||||
set => SetValue(OffsetFirstValueProperty, value);
|
||||
}
|
||||
|
||||
public override void Render(DrawingContext drawingContext)
|
||||
{
|
||||
UpdateTimeScale();
|
||||
|
||||
drawingContext.DrawRectangle(Background, null, new Rect(0, 0, Bounds.Width, 30));
|
||||
|
||||
Pen linePen = new(Foreground);
|
||||
double width = HorizontalOffset + VisibleWidth;
|
||||
int frameStart = 0;
|
||||
|
||||
double units = PixelsPerSecond / _subd1;
|
||||
double offsetUnits = frameStart * PixelsPerSecond % units;
|
||||
|
||||
// Labels
|
||||
double count = (width + offsetUnits) / units;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
double x = i * units - offsetUnits;
|
||||
// Add a 100px margin to allow the text to partially render when needed
|
||||
if (x < HorizontalOffset - 100 || x > HorizontalOffset + width)
|
||||
continue;
|
||||
|
||||
TimeSpan t = TimeSpan.FromSeconds((i * units - offsetUnits) / PixelsPerSecond + frameStart);
|
||||
// 0.00 is always formatted as 0.00
|
||||
if (t == TimeSpan.Zero)
|
||||
RenderLabel(drawingContext, "0.00", x);
|
||||
else if (PixelsPerSecond > 200)
|
||||
RenderLabel(drawingContext, $"{Math.Floor(t.TotalSeconds):00}.{t.Milliseconds:000}", x);
|
||||
else if (PixelsPerSecond > 60)
|
||||
RenderLabel(drawingContext, $"{Math.Floor(t.TotalSeconds):00}.{t.Milliseconds:000}", x);
|
||||
else
|
||||
RenderLabel(drawingContext, $"{Math.Floor(t.TotalMinutes):0}:{t.Seconds:00}", x);
|
||||
}
|
||||
|
||||
// Large ticks
|
||||
units = PixelsPerSecond / _subd2;
|
||||
count = (width + offsetUnits) / units;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
double x = i * units - offsetUnits;
|
||||
if (x == 0 && OffsetFirstValue)
|
||||
drawingContext.DrawLine(linePen, new Point(1, 20), new Point(1, 30));
|
||||
else if (x > HorizontalOffset && x < HorizontalOffset + width)
|
||||
drawingContext.DrawLine(linePen, new Point(x, 20), new Point(x, 30));
|
||||
}
|
||||
|
||||
// Small ticks
|
||||
double mul = _subd3 / _subd2;
|
||||
units = PixelsPerSecond / _subd3;
|
||||
count = (width + offsetUnits) / units;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (Math.Abs(i % mul) < 0.001) continue;
|
||||
double x = i * units - offsetUnits;
|
||||
if (x > HorizontalOffset && x < HorizontalOffset + width)
|
||||
drawingContext.DrawLine(linePen, new Point(x, 25), new Point(x, 30));
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderLabel(DrawingContext drawingContext, string text, double x)
|
||||
{
|
||||
Typeface typeFace = new(FontFamily);
|
||||
FormattedText formattedText = new(text, typeFace, 9, TextAlignment.Left, TextWrapping.NoWrap, Bounds.Size);
|
||||
if (x == 0 && OffsetFirstValue)
|
||||
drawingContext.DrawText(Foreground, new Point(2, 5), formattedText);
|
||||
else
|
||||
drawingContext.DrawText(Foreground, new Point(x - formattedText.Bounds.Width / 2, 5), formattedText);
|
||||
}
|
||||
|
||||
private void UpdateTimeScale()
|
||||
{
|
||||
object[] subds;
|
||||
if (PixelsPerSecond > 350)
|
||||
subds = new object[] {12d, 12d, 60d};
|
||||
else if (PixelsPerSecond > 250)
|
||||
subds = new object[] {6d, 12d, 60d};
|
||||
else if (PixelsPerSecond > 200)
|
||||
subds = new object[] {6d, 6d, 30d};
|
||||
else if (PixelsPerSecond > 150)
|
||||
subds = new object[] {4d, 4d, 20d};
|
||||
else if (PixelsPerSecond > 140)
|
||||
subds = new object[] {4d, 4d, 20d};
|
||||
else if (PixelsPerSecond > 90)
|
||||
subds = new object[] {2d, 4d, 20d};
|
||||
else if (PixelsPerSecond > 60)
|
||||
subds = new object[] {2d, 4d, 8d};
|
||||
else if (PixelsPerSecond > 40)
|
||||
subds = new object[] {1d, 2d, 10d};
|
||||
else if (PixelsPerSecond > 30)
|
||||
subds = new object[] {1d, 2d, 10d};
|
||||
else if (PixelsPerSecond > 10)
|
||||
subds = new object[] {1d / 2d, 1d / 2d, 1d / 2d};
|
||||
else if (PixelsPerSecond > 4)
|
||||
subds = new object[] {1d / 5d, 1d / 5d, 1d / 5d};
|
||||
else if (PixelsPerSecond > 3)
|
||||
subds = new object[] {1d / 10d, 1d / 10d, 1d / 5d};
|
||||
else if (PixelsPerSecond > 1)
|
||||
subds = new object[] {1d / 20d, 1d / 20d, 1d / 10d};
|
||||
else if (PixelsPerSecond >= 1)
|
||||
subds = new object[] {1d / 30d, 1d / 30d, 1d / 15d};
|
||||
else
|
||||
// 1s per pixel
|
||||
subds = new object[] {1d / 60d, 1d / 60d, 1d / 15d};
|
||||
|
||||
_subd1 = (double) subds[0]; // big ticks / labels
|
||||
_subd2 = (double) subds[1]; // medium ticks
|
||||
_subd3 = (double) subds[2]; // small ticks
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.ProfileElementProperties"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:controls="clr-namespace:Artemis.UI.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.ProfileEditor.ProfileElementProperties.ProfileElementPropertiesView">
|
||||
<Grid ColumnDefinitions="*,Auto,*" Name="ContainerGrid">
|
||||
@ -32,6 +33,18 @@
|
||||
Margin="0 0 -5 0" />
|
||||
|
||||
<Grid Grid.Column="2" RowDefinitions="48,*">
|
||||
<!-- Timeline header body -->
|
||||
<controls:TimelineHeader Grid.Row="0"
|
||||
Name="TimelineHeader"
|
||||
Margin="0 18 0 0"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
PixelsPerSecond="{Binding PixelsPerSecond}"
|
||||
HorizontalOffset="{Binding #TimelineScrollViewer.Offset.X, Mode=OneWay}"
|
||||
VisibleWidth="{Binding #TimelineScrollViewer.Bounds.Width}"
|
||||
OffsetFirstValue="True"
|
||||
PointerReleased="TimelineHeader_OnPointerReleased"
|
||||
Width="{Binding #TimelineScrollViewer.Viewport.Width}"
|
||||
Cursor="Hand" />
|
||||
|
||||
<Canvas Grid.Row="0" ZIndex="2">
|
||||
<!-- Timeline segments -->
|
||||
@ -39,18 +52,6 @@
|
||||
<ContentControl Canvas.Left="{Binding MainTimelineSegmentViewModel.SegmentStartPosition}" Content="{Binding MainTimelineSegmentViewModel}" />
|
||||
<ContentControl Canvas.Left="{Binding StartTimelineSegmentViewModel.SegmentStartPosition}" Content="{Binding StartTimelineSegmentViewModel}" />
|
||||
|
||||
<!-- Timeline header body -->
|
||||
<!-- <controls:PropertyTimelineHeader Margin="0 18 0 0" -->
|
||||
<!-- Foreground="{DynamicResource MaterialDesignBody}" -->
|
||||
<!-- Background="{DynamicResource MaterialDesignCardBackground}" -->
|
||||
<!-- PixelsPerSecond="{Binding ProfileEditorService.PixelsPerSecond}" -->
|
||||
<!-- HorizontalOffset="{Binding ContentHorizontalOffset, ElementName=TimelineHeaderScrollViewer}" -->
|
||||
<!-- VisibleWidth="{Binding ActualWidth, ElementName=TimelineHeaderScrollViewer}" -->
|
||||
<!-- OffsetFirstValue="True" -->
|
||||
<!-- MouseLeftButtonUp="{s:Action TimelineJump}" -->
|
||||
<!-- Width="{Binding ActualWidth, ElementName=PropertyTimeLine}" -->
|
||||
<!-- Cursor="Hand" /> -->
|
||||
|
||||
<!-- Timeline caret -->
|
||||
<Polygon Name="TimelineCaret"
|
||||
Canvas.Left="{Binding TimelineViewModel.CaretPosition}"
|
||||
|
||||
@ -13,33 +13,33 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileElementProperties;
|
||||
|
||||
public class ProfileElementPropertiesView : ReactiveUserControl<ProfileElementPropertiesViewModel>
|
||||
{
|
||||
private Polygon _timelineCaret;
|
||||
private Line _timelineLine;
|
||||
private readonly Polygon _timelineCaret;
|
||||
private readonly Line _timelineLine;
|
||||
|
||||
public ProfileElementPropertiesView()
|
||||
{
|
||||
InitializeComponent();
|
||||
_timelineCaret = this.Get<Polygon>("TimelineCaret");
|
||||
_timelineLine = this.Get<Line>("TimelineLine");
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
_timelineCaret = this.Get<Polygon>("TimelineCaret");
|
||||
_timelineLine = this.Get<Line>("TimelineLine");
|
||||
}
|
||||
|
||||
private void ApplyTransition(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
((DoubleTransition) _timelineCaret.Transitions![0]).Duration = TimeSpan.FromMilliseconds(50);
|
||||
((DoubleTransition) _timelineLine.Transitions![0]).Duration = TimeSpan.FromMilliseconds(50);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if (enable)
|
||||
// {
|
||||
// ((DoubleTransition) _timelineCaret.Transitions![0]).Duration = TimeSpan.FromMilliseconds(50);
|
||||
// ((DoubleTransition) _timelineLine.Transitions![0]).Duration = TimeSpan.FromMilliseconds(50);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
((DoubleTransition) _timelineCaret.Transitions![0]).Duration = TimeSpan.Zero;
|
||||
((DoubleTransition) _timelineLine.Transitions![0]).Duration = TimeSpan.Zero;
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
private void TimelineCaret_OnPointerPressed(object? sender, PointerPressedEventArgs e)
|
||||
@ -67,14 +67,7 @@ public class ProfileElementPropertiesView : ReactiveUserControl<ProfileElementPr
|
||||
IVisual? parent = senderElement.VisualParent;
|
||||
double x = Math.Max(0, e.GetPosition(parent).X);
|
||||
TimeSpan newTime = TimeSpan.FromSeconds(x / ViewModel.PixelsPerSecond);
|
||||
|
||||
// Round the time to something that fits the current zoom level
|
||||
if (ViewModel.PixelsPerSecond < 200)
|
||||
newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 5.0) * 5.0);
|
||||
else if (ViewModel.PixelsPerSecond < 500)
|
||||
newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 2.0) * 2.0);
|
||||
else
|
||||
newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds));
|
||||
newTime = RoundTime(newTime);
|
||||
|
||||
// If holding down shift, snap to the closest segment or keyframe
|
||||
if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
|
||||
@ -89,4 +82,26 @@ public class ProfileElementPropertiesView : ReactiveUserControl<ProfileElementPr
|
||||
|
||||
ViewModel.TimelineViewModel.ChangeTime(newTime);
|
||||
}
|
||||
|
||||
private void TimelineHeader_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
if (ViewModel == null || sender is not IInputElement senderElement)
|
||||
return;
|
||||
|
||||
// Get the parent grid, need that for our position
|
||||
double x = Math.Max(0, e.GetPosition(senderElement.VisualParent).X);
|
||||
TimeSpan newTime = TimeSpan.FromSeconds(x / ViewModel.PixelsPerSecond);
|
||||
|
||||
ViewModel.TimelineViewModel.ChangeTime(RoundTime(newTime));
|
||||
}
|
||||
|
||||
private TimeSpan RoundTime(TimeSpan time)
|
||||
{
|
||||
// Round the time to something that fits the current zoom level
|
||||
if (ViewModel!.PixelsPerSecond < 200)
|
||||
return TimeSpan.FromMilliseconds(Math.Round(time.TotalMilliseconds / 5.0) * 5.0);
|
||||
if (ViewModel.PixelsPerSecond < 500)
|
||||
return TimeSpan.FromMilliseconds(Math.Round(time.TotalMilliseconds / 2.0) * 2.0);
|
||||
return TimeSpan.FromMilliseconds(Math.Round(time.TotalMilliseconds));
|
||||
}
|
||||
}
|
||||
@ -23,7 +23,7 @@ public class ProfileElementPropertiesViewModel : ActivatableViewModelBase
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private readonly ILayerPropertyVmFactory _layerPropertyVmFactory;
|
||||
private ObservableAsPropertyHelper<RenderProfileElement?>? _profileElement;
|
||||
private ObservableAsPropertyHelper<double>? _pixelsPerSecond;
|
||||
private ObservableAsPropertyHelper<int>? _pixelsPerSecond;
|
||||
private ObservableCollection<ProfileElementPropertyGroupViewModel> _propertyGroupViewModels;
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -63,7 +63,7 @@ public class ProfileElementPropertiesViewModel : ActivatableViewModelBase
|
||||
public TimelineViewModel TimelineViewModel { get; }
|
||||
public RenderProfileElement? ProfileElement => _profileElement?.Value;
|
||||
public Layer? Layer => _profileElement?.Value as Layer;
|
||||
public double PixelsPerSecond => _pixelsPerSecond?.Value ?? 0;
|
||||
public int PixelsPerSecond => _pixelsPerSecond?.Value ?? 0;
|
||||
public IObservable<bool> Playing => _profileEditorService.Playing;
|
||||
|
||||
public ObservableCollection<ProfileElementPropertyGroupViewModel> PropertyGroupViewModels
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
<Setter Property="Opacity" Value="0" />
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
<Grid ColumnDefinitions="Auto, Auto,*" Height="23" Margin="5 0">
|
||||
<Grid ColumnDefinitions="Auto, Auto,*,*" Height="23" Margin="5 0">
|
||||
<ContentControl Grid.Column="0" Content="{Binding ProfileElement}">
|
||||
<ContentControl.DataTemplates>
|
||||
<DataTemplate DataType="core:Folder">
|
||||
@ -40,5 +40,13 @@
|
||||
<Border Grid.Column="2" Classes="status-message-border" Classes.hidden="{Binding !ShowStatusMessage}">
|
||||
<TextBlock Margin="5 0 0 0" Text="{Binding StatusMessage}" />
|
||||
</Border>
|
||||
|
||||
<Slider Grid.Column="3"
|
||||
Margin="0 -11 0 0"
|
||||
Minimum="31"
|
||||
Maximum="350"
|
||||
Width="319"
|
||||
Value="{Binding PixelsPerSecond}"
|
||||
HorizontalAlignment="Right"/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -10,17 +10,21 @@ namespace Artemis.UI.Screens.ProfileEditor.StatusBar;
|
||||
|
||||
public class StatusBarViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private ProfileEditorHistory? _history;
|
||||
private RenderProfileElement? _profileElement;
|
||||
private string? _statusMessage;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private ObservableAsPropertyHelper<ProfileEditorHistory?>? _history;
|
||||
private ObservableAsPropertyHelper<int>? _pixelsPerSecond;
|
||||
private ObservableAsPropertyHelper<RenderProfileElement?>? _profileElement;
|
||||
private bool _showStatusMessage;
|
||||
private string? _statusMessage;
|
||||
|
||||
public StatusBarViewModel(IProfileEditorService profileEditorService)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
profileEditorService.ProfileElement.Subscribe(p => ProfileElement = p).DisposeWith(d);
|
||||
profileEditorService.History.Subscribe(history => History = history).DisposeWith(d);
|
||||
_profileElement = profileEditorService.ProfileElement.ToProperty(this, vm => vm.ProfileElement).DisposeWith(d);
|
||||
_history = profileEditorService.History.ToProperty(this, vm => vm.History).DisposeWith(d);
|
||||
_pixelsPerSecond = profileEditorService.PixelsPerSecond.ToProperty(this, vm => vm.PixelsPerSecond);
|
||||
});
|
||||
|
||||
this.WhenAnyValue(vm => vm.History)
|
||||
@ -36,16 +40,13 @@ public class StatusBarViewModel : ActivatableViewModelBase
|
||||
this.WhenAnyValue(vm => vm.StatusMessage).Throttle(TimeSpan.FromSeconds(3)).Subscribe(_ => ShowStatusMessage = false);
|
||||
}
|
||||
|
||||
public RenderProfileElement? ProfileElement
|
||||
{
|
||||
get => _profileElement;
|
||||
set => this.RaiseAndSetIfChanged(ref _profileElement, value);
|
||||
}
|
||||
public RenderProfileElement? ProfileElement => _profileElement?.Value;
|
||||
public ProfileEditorHistory? History => _history?.Value;
|
||||
|
||||
public ProfileEditorHistory? History
|
||||
public int PixelsPerSecond
|
||||
{
|
||||
get => _history;
|
||||
set => this.RaiseAndSetIfChanged(ref _history, value);
|
||||
get => _pixelsPerSecond?.Value ?? 0;
|
||||
set => _profileEditorService.ChangePixelsPerSecond(value);
|
||||
}
|
||||
|
||||
public string? StatusMessage
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user