1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Core - Made FloatRange and IntRange readonly structs

ColorGradient - Fixed missing subscribtions when using object list manipulation API
ColorGradient - Fixed editor VM going out of sync
This commit is contained in:
Robert 2022-07-17 23:22:45 +02:00
parent bd26f447c6
commit e385165200
19 changed files with 337 additions and 327 deletions

View File

@ -76,8 +76,4 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="DefaultTypes\DataBindings\" />
</ItemGroup>
</Project>

View File

@ -1,9 +1,8 @@
using System.Collections.Specialized;
using System.ComponentModel;
using SkiaSharp;
namespace Artemis.Core
{
namespace Artemis.Core;
/// <inheritdoc />
public class ColorGradientLayerProperty : LayerProperty<ColorGradient>
{
@ -13,10 +12,59 @@ namespace Artemis.Core
{
KeyframesSupported = false;
DefaultValue = new ColorGradient();
CurrentValueSet += OnCurrentValueSet;
}
/// <summary>
/// Implicitly converts an <see cref="ColorGradientLayerProperty" /> to a <see cref="ColorGradient" />
/// </summary>
public static implicit operator ColorGradient(ColorGradientLayerProperty p)
{
return p.CurrentValue;
}
#region Overrides of LayerProperty<ColorGradient>
/// <inheritdoc />
protected override void OnCurrentValueSet()
{
// Don't allow color gradients to be null
if (BaseValue == null!)
BaseValue = new ColorGradient(DefaultValue);
if (!ReferenceEquals(_subscribedGradient, BaseValue))
{
if (_subscribedGradient != null)
_subscribedGradient.CollectionChanged -= SubscribedGradientOnPropertyChanged;
_subscribedGradient = BaseValue;
_subscribedGradient.CollectionChanged += SubscribedGradientOnPropertyChanged;
}
CreateDataBindingRegistrations();
base.OnCurrentValueSet();
}
#endregion
/// <inheritdoc />
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
{
throw new ArtemisCoreException("Color Gradients do not support keyframes.");
}
#region Overrides of LayerProperty<ColorGradient>
/// <inheritdoc />
protected override void OnInitialize()
{
// Don't allow color gradients to be null
if (BaseValue == null!)
BaseValue = new ColorGradient(DefaultValue);
base.OnInitialize();
}
#endregion
private void CreateDataBindingRegistrations()
{
DataBinding.ClearDataBindingProperties();
@ -36,56 +84,9 @@ namespace Artemis.Core
}
}
/// <summary>
/// Implicitly converts an <see cref="ColorGradientLayerProperty" /> to a <see cref="ColorGradient" />
/// </summary>
public static implicit operator ColorGradient(ColorGradientLayerProperty p)
{
return p.CurrentValue;
}
/// <inheritdoc />
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
{
throw new ArtemisCoreException("Color Gradients do not support keyframes.");
}
private void OnCurrentValueSet(object? sender, LayerPropertyEventArgs e)
{
// Don't allow color gradients to be null
if (BaseValue == null!)
BaseValue = new ColorGradient(DefaultValue);
if (!ReferenceEquals(_subscribedGradient, BaseValue))
{
if (_subscribedGradient != null)
_subscribedGradient.CollectionChanged -= SubscribedGradientOnPropertyChanged;
_subscribedGradient = BaseValue;
_subscribedGradient.CollectionChanged += SubscribedGradientOnPropertyChanged;
}
CreateDataBindingRegistrations();
}
private void SubscribedGradientOnPropertyChanged(object? sender, NotifyCollectionChangedEventArgs args)
{
if (CurrentValue.Count != DataBinding.Properties.Count)
CreateDataBindingRegistrations();
}
#region Overrides of LayerProperty<ColorGradient>
/// <inheritdoc />
protected override void OnInitialize()
{
// Don't allow color gradients to be null
if (BaseValue == null!)
BaseValue = new ColorGradient(DefaultValue);
base.OnInitialize();
}
#endregion
}
}

View File

@ -3,17 +3,11 @@
/// <inheritdoc />
public class FloatRangeLayerProperty : LayerProperty<FloatRange>
{
internal FloatRangeLayerProperty()
{
CurrentValueSet += OnCurrentValueSet;
DefaultValue = new FloatRange();
}
/// <inheritdoc />
protected override void OnInitialize()
{
DataBinding.RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue.Start = value, "Start");
DataBinding.RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue.End = value, "End");
DataBinding.RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue = new FloatRange(value, CurrentValue.End), "Start");
DataBinding.RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue = new FloatRange(CurrentValue.Start, value), "End");
}
/// <inheritdoc />
@ -22,15 +16,9 @@
float startDiff = NextKeyframe!.Value.Start - CurrentKeyframe!.Value.Start;
float endDiff = NextKeyframe!.Value.End - CurrentKeyframe!.Value.End;
CurrentValue = new FloatRange(
(int) (CurrentKeyframe!.Value.Start + startDiff * keyframeProgressEased),
(int) (CurrentKeyframe!.Value.End + endDiff * keyframeProgressEased)
(float) (CurrentKeyframe!.Value.Start + startDiff * keyframeProgressEased),
(float) (CurrentKeyframe!.Value.End + endDiff * keyframeProgressEased)
);
}
private void OnCurrentValueSet(object? sender, LayerPropertyEventArgs e)
{
// Don't allow the int range to be null
BaseValue ??= DefaultValue ?? new FloatRange();
}
}
}

View File

@ -3,17 +3,11 @@
/// <inheritdoc />
public class IntRangeLayerProperty : LayerProperty<IntRange>
{
internal IntRangeLayerProperty()
{
CurrentValueSet += OnCurrentValueSet;
DefaultValue = new IntRange();
}
/// <inheritdoc />
protected override void OnInitialize()
{
DataBinding.RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue.Start = value, "Start");
DataBinding.RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue.End = value, "End");
DataBinding.RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue = new IntRange(value, CurrentValue.End), "Start");
DataBinding.RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue = new IntRange(CurrentValue.Start, value), "End");
}
/// <inheritdoc />
@ -26,11 +20,5 @@
(int) (CurrentKeyframe!.Value.End + endDiff * keyframeProgressEased)
);
}
private void OnCurrentValueSet(object? sender, LayerPropertyEventArgs e)
{
// Don't allow the int range to be null
BaseValue ??= DefaultValue ?? new IntRange();
}
}
}

View File

@ -55,6 +55,21 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
}
}
/// <summary>
/// Creates a new instance of the <see cref="ColorGradient" /> class
/// </summary>
/// <param name="stops">The stops to copy</param>
public ColorGradient(List<ColorGradientStop> stops)
{
_stops = new List<ColorGradientStop>();
foreach (ColorGradientStop colorGradientStop in stops)
{
ColorGradientStop stop = new(colorGradientStop.Color, colorGradientStop.Position);
stop.PropertyChanged += ItemOnPropertyChanged;
_stops.Add(stop);
}
}
/// <summary>
/// Gets all the colors in the color gradient
/// </summary>
@ -462,7 +477,7 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
public int Add(object? value)
{
if (value is ColorGradientStop stop)
_stops.Add(stop);
Add(stop);
return IndexOf(value);
}
@ -492,14 +507,14 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
public void Insert(int index, object? value)
{
if (value is ColorGradientStop stop)
_stops.Insert(index, stop);
Insert(index, stop);
}
/// <inheritdoc />
public void Remove(object? value)
{
if (value is ColorGradientStop stop)
_stops.Remove(stop);
Remove(stop);
}
/// <inheritdoc />

View File

@ -633,6 +633,7 @@ namespace Artemis.Core
if (!baseLayerEffect.Suspended)
baseLayerEffect.InternalPreProcess(canvas, bounds, layerPaint);
}
canvas.ClipPath(renderPath);
// Restore the blend mode before doing the actual render
@ -788,10 +789,14 @@ namespace Artemis.Core
/// </summary>
public void ChangeLayerBrush(BaseLayerBrush? layerBrush)
{
BaseLayerBrush? oldLayerBrush = LayerBrush;
General.BrushReference.SetCurrentValue(layerBrush != null ? new LayerBrushReference(layerBrush.Descriptor) : null, null);
LayerBrush = layerBrush;
LayerEntity.LayerBrush = new LayerBrushEntity();
oldLayerBrush?.InternalDisable();
if (LayerBrush != null)
ActivateLayerBrush();
else
@ -815,7 +820,12 @@ namespace Artemis.Core
General.ShapeType.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
General.BlendMode.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
Transform.IsHidden = LayerBrush != null && !LayerBrush.SupportsTransformation;
if (LayerBrush != null)
{
if (!LayerBrush.Enabled)
LayerBrush.InternalEnable();
LayerBrush?.Update(0);
}
OnLayerBrushUpdated();
ClearBrokenState("Failed to initialize layer brush");

View File

@ -5,18 +5,10 @@ namespace Artemis.Core
/// <summary>
/// Represents a range between two single-precision floating point numbers
/// </summary>
public class FloatRange
public readonly struct FloatRange
{
private readonly Random _rand;
/// <summary>
/// Creates a new instance of the <see cref="FloatRange" /> class
/// </summary>
public FloatRange()
{
_rand = new Random();
}
/// <summary>
/// Creates a new instance of the <see cref="FloatRange" /> class
/// </summary>
@ -31,14 +23,14 @@ namespace Artemis.Core
}
/// <summary>
/// Gets or sets the start value of the range
/// Gets the start value of the range
/// </summary>
public float Start { get; set; }
public float Start { get; }
/// <summary>
/// Gets or sets the end value of the range
/// Gets the end value of the range
/// </summary>
public float End { get; set; }
public float End { get; }
/// <summary>
/// Determines whether the given value is in this range

View File

@ -5,18 +5,10 @@ namespace Artemis.Core
/// <summary>
/// Represents a range between two signed integers
/// </summary>
public class IntRange
public readonly struct IntRange
{
private readonly Random _rand;
/// <summary>
/// Creates a new instance of the <see cref="IntRange" /> class
/// </summary>
public IntRange()
{
_rand = new Random();
}
/// <summary>
/// Creates a new instance of the <see cref="IntRange" /> class
/// </summary>
@ -31,14 +23,14 @@ namespace Artemis.Core
}
/// <summary>
/// Gets or sets the start value of the range
/// Gets the start value of the range
/// </summary>
public int Start { get; set; }
public int Start { get; }
/// <summary>
/// Gets or sets the end value of the range
/// Gets the end value of the range
/// </summary>
public int End { get; set; }
public int End { get; }
/// <summary>
/// Determines whether the given value is in this range

View File

@ -261,7 +261,7 @@ namespace Artemis.Core
/// <summary>
/// Gets whether keyframes are supported on this type of property
/// </summary>
public bool KeyframesSupported { get; protected internal set; } = true;
public bool KeyframesSupported { get; protected set; } = true;
/// <summary>
/// Gets or sets whether keyframes are enabled on this property, has no effect if <see cref="KeyframesSupported" /> is

View File

@ -39,8 +39,6 @@ namespace Artemis.Core.LayerBrushes
PropertyGroupDescriptionAttribute groupDescription = new() {Identifier = "Brush", Name = Descriptor.DisplayName, Description = Descriptor.Description};
Properties.Initialize(Layer, null, groupDescription, propertyGroupEntity);
PropertiesInitialized = true;
EnableLayerBrush();
}
}
}

View File

@ -24,7 +24,7 @@ public class GradientPickerButton : TemplatedControl
/// Gets or sets the color gradient.
/// </summary>
public static readonly StyledProperty<ColorGradient?> ColorGradientProperty =
AvaloniaProperty.Register<GradientPickerButton, ColorGradient?>(nameof(ColorGradient), notifying: ColorGradientChanged, defaultValue: ColorGradient.GetUnicornBarf());
AvaloniaProperty.Register<GradientPickerButton, ColorGradient?>(nameof(ColorGradient), notifying: ColorGradientChanged);
/// <summary>
/// Gets or sets a boolean indicating whether the gradient picker should be in compact mode or not.
@ -108,6 +108,7 @@ public class GradientPickerButton : TemplatedControl
private static void ColorGradientChanged(IAvaloniaObject sender, bool before)
{
if (!before)
(sender as GradientPickerButton)?.Subscribe();
}

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
namespace Artemis.UI.Shared.Services.ProfileEditor.Commands;
/// <summary>
/// Represents a profile editor command that can be used to update a layer property of type <typeparamref name="T" />.
/// </summary>
public class UpdateColorGradient : IProfileEditorCommand
{
private readonly ColorGradient _colorGradient;
private readonly List<ColorGradientStop> _stops;
private readonly List<ColorGradientStop> _originalStops;
/// <summary>
/// Creates a new instance of the <see cref="UpdateColorGradient" /> class.
/// </summary>
public UpdateColorGradient(ColorGradient colorGradient, List<ColorGradientStop> stops, List<ColorGradientStop>? originalStops)
{
_colorGradient = colorGradient;
_stops = stops;
_originalStops = originalStops ?? _colorGradient.Select(s => new ColorGradientStop(s.Color, s.Position)).ToList();
}
#region Implementation of IProfileEditorCommand
/// <inheritdoc />
public string DisplayName => "Update color gradient";
/// <inheritdoc />
public void Execute()
{
_colorGradient.Clear();
foreach (ColorGradientStop colorGradientStop in _stops)
_colorGradient.Add(colorGradientStop);
}
/// <inheritdoc />
public void Undo()
{
_colorGradient.Clear();
foreach (ColorGradientStop colorGradientStop in _originalStops)
_colorGradient.Add(colorGradientStop);
}
#endregion
}

View File

@ -9,7 +9,7 @@
x:DataType="propertyInput:ColorGradientPropertyInputViewModel">
<gradientPicker:GradientPickerButton Classes="condensed"
Width="200"
ColorGradient="{CompiledBinding ColorGradient}"
ColorGradient="{CompiledBinding InputValue}"
VerticalAlignment="Center"
FlyoutOpened="GradientPickerButton_OnFlyoutOpened"
FlyoutClosed="GradientPickerButton_OnFlyoutClosed"/>

View File

@ -1,4 +1,6 @@
using Artemis.Core;
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
using Artemis.UI.Shared.Services.PropertyInput;
@ -9,37 +11,19 @@ namespace Artemis.UI.DefaultTypes.PropertyInput;
public class ColorGradientPropertyInputViewModel : PropertyInputViewModel<ColorGradient>
{
private ColorGradient _colorGradient = null!;
private ColorGradient? _originalGradient;
private List<ColorGradientStop>? _originalStops;
public ColorGradientPropertyInputViewModel(LayerProperty<ColorGradient> layerProperty, IProfileEditorService profileEditorService, IPropertyInputService propertyInputService)
: base(layerProperty, profileEditorService, propertyInputService)
{
}
public ColorGradient ColorGradient
{
get => _colorGradient;
set => this.RaiseAndSetIfChanged(ref _colorGradient, value);
}
protected override void OnInputValueChanged()
{
ColorGradient = new ColorGradient(InputValue);
}
#region Overrides of PropertyInputViewModel<ColorGradient>
/// <inheritdoc />
public override void StartPreview()
{
_originalGradient = LayerProperty.CurrentValue;
// Set the property value to the gradient being edited by the picker, this will cause any updates to show right away because
// ColorGradient is a reference type
LayerProperty.CurrentValue = ColorGradient;
// This won't fly if we ever support keyframes but at that point ColorGradient would have to be a value type anyway and this
// whole VM no longer makes sense
_originalStops = InputValue?.Select(s => new ColorGradientStop(s.Color, s.Position)).ToList();
}
/// <inheritdoc />
@ -51,28 +35,40 @@ public class ColorGradientPropertyInputViewModel : PropertyInputViewModel<ColorG
/// <inheritdoc />
public override void ApplyPreview()
{
if (_originalGradient == null)
// If the new stops are equal to the old ones, nothing changes
if (InputValue == null || _originalStops == null || !HasPreviewChanges())
return;
// Make sure something actually changed
if (Equals(ColorGradient, _originalGradient))
LayerProperty.CurrentValue = _originalGradient;
else
// Update the gradient for realsies, giving the command a reference to the old gradient
ProfileEditorService.ExecuteCommand(new UpdateLayerProperty<ColorGradient>(LayerProperty, ColorGradient, _originalGradient, Time));
_originalGradient = null;
ProfileEditorService.ExecuteCommand(new UpdateColorGradient(InputValue, InputValue.ToList(), _originalStops));
_originalStops = null;
}
/// <inheritdoc />
public override void DiscardPreview()
{
if (_originalGradient == null)
if (InputValue == null || _originalStops == null)
return;
// Put the old gradient back
InputValue = _originalGradient;
ColorGradient = new ColorGradient(InputValue);
InputValue.Clear();
foreach (ColorGradientStop colorGradientStop in _originalStops)
InputValue.Add(colorGradientStop);
_originalStops = null;
}
private bool HasPreviewChanges()
{
if (InputValue == null || _originalStops == null)
return false;
if (InputValue.Count != _originalStops.Count)
return true;
for (int i = 0; i < InputValue.Count; i++)
if (!Equals(InputValue[i], _originalStops[i]))
return true;
return false;
}
#endregion

View File

@ -30,28 +30,20 @@ public class FloatRangePropertyInputViewModel : PropertyInputViewModel<FloatRang
public float Start
{
get => InputValue?.Start ?? 0;
get => InputValue.Start;
set
{
if (InputValue == null)
InputValue = new FloatRange(value, value + 1);
else
InputValue.Start = value;
InputValue = new FloatRange(value, InputValue.End);
this.RaisePropertyChanged(nameof(Start));
}
}
public float End
{
get => InputValue?.End ?? 0;
get => InputValue.End;
set
{
if (InputValue == null)
InputValue = new FloatRange(value - 1, value);
else
InputValue.End = value;
InputValue = new FloatRange(InputValue.Start, value);
this.RaisePropertyChanged(nameof(End));
}
}

View File

@ -30,28 +30,20 @@ public class IntRangePropertyInputViewModel : PropertyInputViewModel<IntRange>
public int Start
{
get => InputValue?.Start ?? 0;
get => InputValue.Start;
set
{
if (InputValue == null)
InputValue = new IntRange(value, value + 1);
else
InputValue.Start = value;
InputValue = new IntRange(value, InputValue.End);
this.RaisePropertyChanged(nameof(Start));
}
}
public int End
{
get => InputValue?.End ?? 0;
get => InputValue.End;
set
{
if (InputValue == null)
InputValue = new IntRange(value - 1, value);
else
InputValue.End = value;
InputValue = new IntRange(InputValue.Start, value);
this.RaisePropertyChanged(nameof(End));
}
}