mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Merge remote-tracking branch 'origin/development' into development
This commit is contained in:
commit
b342eabe4d
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel;
|
using System.Collections.Specialized;
|
||||||
|
using System.ComponentModel;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
@ -23,17 +24,16 @@ namespace Artemis.Core
|
|||||||
if (CurrentValue == null)
|
if (CurrentValue == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (int index = 0; index < CurrentValue.Stops.Count; index++)
|
for (int index = 0; index < CurrentValue.Count; index++)
|
||||||
{
|
{
|
||||||
int stopIndex = index;
|
int stopIndex = index;
|
||||||
|
|
||||||
void Setter(SKColor value)
|
void Setter(SKColor value)
|
||||||
{
|
{
|
||||||
CurrentValue.Stops[stopIndex].Color = value;
|
CurrentValue[stopIndex].Color = value;
|
||||||
CurrentValue.OnColorValuesUpdated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterDataBindingProperty(() => CurrentValue.Stops[stopIndex].Color, Setter, new ColorStopDataBindingConverter(), $"Color #{stopIndex + 1}");
|
RegisterDataBindingProperty(() => CurrentValue[stopIndex].Color, Setter, new ColorStopDataBindingConverter(), $"Color #{stopIndex + 1}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,17 +61,17 @@ namespace Artemis.Core
|
|||||||
if (_subscribedGradient != BaseValue)
|
if (_subscribedGradient != BaseValue)
|
||||||
{
|
{
|
||||||
if (_subscribedGradient != null)
|
if (_subscribedGradient != null)
|
||||||
_subscribedGradient.PropertyChanged -= SubscribedGradientOnPropertyChanged;
|
_subscribedGradient.CollectionChanged -= SubscribedGradientOnPropertyChanged;
|
||||||
_subscribedGradient = BaseValue;
|
_subscribedGradient = BaseValue;
|
||||||
_subscribedGradient.PropertyChanged += SubscribedGradientOnPropertyChanged;
|
_subscribedGradient.CollectionChanged += SubscribedGradientOnPropertyChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateDataBindingRegistrations();
|
CreateDataBindingRegistrations();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SubscribedGradientOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
private void SubscribedGradientOnPropertyChanged(object? sender, NotifyCollectionChangedEventArgs args)
|
||||||
{
|
{
|
||||||
if (CurrentValue.Stops.Count != GetAllDataBindingRegistrations().Count)
|
if (CurrentValue.Count != GetAllDataBindingRegistrations().Count)
|
||||||
CreateDataBindingRegistrations();
|
CreateDataBindingRegistrations();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
@ -8,7 +11,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A gradient containing a list of <see cref="ColorGradientStop" />s
|
/// A gradient containing a list of <see cref="ColorGradientStop" />s
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ColorGradient : CorePropertyChanged
|
public class ColorGradient : IList<ColorGradientStop>, INotifyCollectionChanged
|
||||||
{
|
{
|
||||||
private static readonly SKColor[] FastLedRainbow =
|
private static readonly SKColor[] FastLedRainbow =
|
||||||
{
|
{
|
||||||
@ -23,19 +26,16 @@ namespace Artemis.Core
|
|||||||
new(0xFFFF0000) // and back to Red
|
new(0xFFFF0000) // and back to Red
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly List<ColorGradientStop> _stops;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the <see cref="ColorGradient" /> class
|
/// Creates a new instance of the <see cref="ColorGradient" /> class
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ColorGradient()
|
public ColorGradient()
|
||||||
{
|
{
|
||||||
Stops = new List<ColorGradientStop>();
|
_stops = new List<ColorGradientStop>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a list of all the <see cref="ColorGradientStop" />s in the gradient
|
|
||||||
/// </summary>
|
|
||||||
public List<ColorGradientStop> Stops { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all the colors in the color gradient
|
/// Gets all the colors in the color gradient
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -49,13 +49,16 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
List<SKColor> result = new();
|
List<SKColor> result = new();
|
||||||
if (timesToRepeat == 0)
|
if (timesToRepeat == 0)
|
||||||
result = Stops.Select(c => c.Color).ToList();
|
{
|
||||||
|
result = this.Select(c => c.Color).ToList();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
List<SKColor> colors = Stops.Select(c => c.Color).ToList();
|
List<SKColor> colors = this.Select(c => c.Color).ToList();
|
||||||
for (int i = 0; i <= timesToRepeat; i++)
|
for (int i = 0; i <= timesToRepeat; i++)
|
||||||
result.AddRange(colors);
|
result.AddRange(colors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seamless && !IsSeamless())
|
if (seamless && !IsSeamless())
|
||||||
result.Add(result[0]);
|
result.Add(result[0]);
|
||||||
|
|
||||||
@ -77,11 +80,13 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
List<float> result = new();
|
List<float> result = new();
|
||||||
if (timesToRepeat == 0)
|
if (timesToRepeat == 0)
|
||||||
result = Stops.Select(c => c.Position).ToList();
|
{
|
||||||
|
result = this.Select(c => c.Position).ToList();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Create stops and a list of divided stops
|
// Create stops and a list of divided stops
|
||||||
List<float> stops = Stops.Select(c => c.Position / (timesToRepeat + 1)).ToList();
|
List<float> stops = this.Select(c => c.Position / (timesToRepeat + 1)).ToList();
|
||||||
|
|
||||||
// For each repeat cycle, add the base stops to the end result
|
// For each repeat cycle, add the base stops to the end result
|
||||||
for (int i = 0; i <= timesToRepeat; i++)
|
for (int i = 0; i <= timesToRepeat; i++)
|
||||||
@ -104,25 +109,16 @@ namespace Artemis.Core
|
|||||||
return result.ToArray();
|
return result.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Triggers a property changed event of the <see cref="Stops" /> collection
|
|
||||||
/// </summary>
|
|
||||||
public void OnColorValuesUpdated()
|
|
||||||
{
|
|
||||||
Stops.Sort((a, b) => a.Position.CompareTo(b.Position));
|
|
||||||
OnPropertyChanged(nameof(Stops));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a color at any position between 0.0 and 1.0 using interpolation
|
/// Gets a color at any position between 0.0 and 1.0 using interpolation
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="position">A position between 0.0 and 1.0</param>
|
/// <param name="position">A position between 0.0 and 1.0</param>
|
||||||
public SKColor GetColor(float position)
|
public SKColor GetColor(float position)
|
||||||
{
|
{
|
||||||
if (!Stops.Any())
|
if (!this.Any())
|
||||||
return SKColor.Empty;
|
return SKColor.Empty;
|
||||||
|
|
||||||
ColorGradientStop[] stops = Stops.ToArray();
|
ColorGradientStop[] stops = this.ToArray();
|
||||||
if (position <= 0) return stops[0].Color;
|
if (position <= 0) return stops[0].Color;
|
||||||
if (position >= 1) return stops[^1].Color;
|
if (position >= 1) return stops[^1].Color;
|
||||||
ColorGradientStop left = stops[0];
|
ColorGradientStop left = stops[0];
|
||||||
@ -160,7 +156,7 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
SKColor skColor = FastLedRainbow[index];
|
SKColor skColor = FastLedRainbow[index];
|
||||||
float position = 1f / (FastLedRainbow.Length - 1f) * index;
|
float position = 1f / (FastLedRainbow.Length - 1f) * index;
|
||||||
gradient.Stops.Add(new ColorGradientStop(skColor, position));
|
gradient.Add(new ColorGradientStop(skColor, position));
|
||||||
}
|
}
|
||||||
|
|
||||||
return gradient;
|
return gradient;
|
||||||
@ -172,7 +168,141 @@ namespace Artemis.Core
|
|||||||
/// <returns><see langword="true" /> if the gradient is seamless; <see langword="false" /> otherwise</returns>
|
/// <returns><see langword="true" /> if the gradient is seamless; <see langword="false" /> otherwise</returns>
|
||||||
public bool IsSeamless()
|
public bool IsSeamless()
|
||||||
{
|
{
|
||||||
return Stops.Count == 0 || Stops.First().Color.Equals(Stops.Last().Color);
|
return Count == 0 || this.First().Color.Equals(this.Last().Color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void Sort()
|
||||||
|
{
|
||||||
|
_stops.Sort((a, b) => a.Position.CompareTo(b.Position));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ItemOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
Sort();
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Implementation of IEnumerable
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerator<ColorGradientStop> GetEnumerator()
|
||||||
|
{
|
||||||
|
return _stops.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of ICollection<ColorGradientStop>
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Add(ColorGradientStop item)
|
||||||
|
{
|
||||||
|
_stops.Add(item);
|
||||||
|
item.PropertyChanged += ItemOnPropertyChanged;
|
||||||
|
Sort();
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, _stops.IndexOf(item)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
foreach (ColorGradientStop item in _stops)
|
||||||
|
item.PropertyChanged -= ItemOnPropertyChanged;
|
||||||
|
_stops.Clear();
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Contains(ColorGradientStop item)
|
||||||
|
{
|
||||||
|
return _stops.Contains(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void CopyTo(ColorGradientStop[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
_stops.CopyTo(array, arrayIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Remove(ColorGradientStop item)
|
||||||
|
{
|
||||||
|
item.PropertyChanged -= ItemOnPropertyChanged;
|
||||||
|
int index = _stops.IndexOf(item);
|
||||||
|
bool removed = _stops.Remove(item);
|
||||||
|
if (removed)
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
|
||||||
|
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int Count => _stops.Count;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of IList<ColorGradientStop>
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int IndexOf(ColorGradientStop item)
|
||||||
|
{
|
||||||
|
return _stops.IndexOf(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Insert(int index, ColorGradientStop item)
|
||||||
|
{
|
||||||
|
_stops.Insert(index, item);
|
||||||
|
item.PropertyChanged += ItemOnPropertyChanged;
|
||||||
|
Sort();
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, _stops.IndexOf(item)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void RemoveAt(int index)
|
||||||
|
{
|
||||||
|
_stops[index].PropertyChanged -= ItemOnPropertyChanged;
|
||||||
|
_stops.RemoveAt(index);
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, index));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ColorGradientStop this[int index]
|
||||||
|
{
|
||||||
|
get => _stops[index];
|
||||||
|
set
|
||||||
|
{
|
||||||
|
ColorGradientStop? oldValue = _stops[index];
|
||||||
|
oldValue.PropertyChanged -= ItemOnPropertyChanged;
|
||||||
|
_stops[index] = value;
|
||||||
|
_stops[index].PropertyChanged += ItemOnPropertyChanged;
|
||||||
|
Sort();
|
||||||
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of INotifyCollectionChanged
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public event NotifyCollectionChangedEventHandler? CollectionChanged;
|
||||||
|
|
||||||
|
private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
CollectionChanged?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -297,6 +297,9 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
private void ApplyTimeline(Timeline timeline)
|
private void ApplyTimeline(Timeline timeline)
|
||||||
{
|
{
|
||||||
|
if (timeline.Delta == TimeSpan.Zero)
|
||||||
|
return;
|
||||||
|
|
||||||
General.Update(timeline);
|
General.Update(timeline);
|
||||||
Transform.Update(timeline);
|
Transform.Update(timeline);
|
||||||
if (LayerBrush != null)
|
if (LayerBrush != null)
|
||||||
|
|||||||
@ -33,6 +33,11 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Type PropertyType { get; }
|
Type PropertyType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the BaseValue was loaded from storage, useful to check whether a default value must be applied
|
||||||
|
/// </summary>
|
||||||
|
bool IsLoadedFromStorage { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the layer property
|
/// Initializes the layer property
|
||||||
/// <para>
|
/// <para>
|
||||||
|
|||||||
28
src/Artemis.Core/Plugins/LayerBrushes/ILayerBrushPreset.cs
Normal file
28
src/Artemis.Core/Plugins/LayerBrushes/ILayerBrushPreset.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
namespace Artemis.Core.LayerBrushes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a brush preset for a brush.
|
||||||
|
/// </summary>
|
||||||
|
public interface ILayerBrushPreset
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the preset
|
||||||
|
/// </summary>
|
||||||
|
string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the description of the preset
|
||||||
|
/// </summary>
|
||||||
|
string Description { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the icon of the preset
|
||||||
|
/// </summary>
|
||||||
|
string Icon { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies the preset to the layer brush
|
||||||
|
/// </summary>
|
||||||
|
void Apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace Artemis.Core.LayerBrushes
|
namespace Artemis.Core.LayerBrushes
|
||||||
@ -70,6 +72,16 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual LayerPropertyGroup? BaseProperties => null;
|
public virtual LayerPropertyGroup? BaseProperties => null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of presets available to this layer brush
|
||||||
|
/// </summary>
|
||||||
|
public virtual List<ILayerBrushPreset>? Presets => null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the default preset used for new instances of this layer brush
|
||||||
|
/// </summary>
|
||||||
|
public virtual ILayerBrushPreset? DefaultPreset => Presets?.FirstOrDefault();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets whether the brush supports transformations
|
/// Gets or sets whether the brush supports transformations
|
||||||
/// <para>Note: RGB.NET brushes can never be transformed and setting this to true will throw an exception</para>
|
/// <para>Note: RGB.NET brushes can never be transformed and setting this to true will throw an exception</para>
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Artemis.Core.LayerBrushes
|
namespace Artemis.Core.LayerBrushes
|
||||||
{
|
{
|
||||||
|
|||||||
@ -38,7 +38,7 @@
|
|||||||
<Rectangle Stroke="{DynamicResource NormalBorderBrush}" Cursor="Hand" MouseUp="UIElement_OnMouseUp">
|
<Rectangle Stroke="{DynamicResource NormalBorderBrush}" Cursor="Hand" MouseUp="UIElement_OnMouseUp">
|
||||||
<Rectangle.Fill>
|
<Rectangle.Fill>
|
||||||
<LinearGradientBrush
|
<LinearGradientBrush
|
||||||
GradientStops="{Binding ColorGradient.Stops, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource ColorGradientToGradientStopsConverter}}"
|
GradientStops="{Binding ColorGradient, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource ColorGradientToGradientStopsConverter}}"
|
||||||
EndPoint="1,0"
|
EndPoint="1,0"
|
||||||
StartPoint="0,0" />
|
StartPoint="0,0" />
|
||||||
</Rectangle.Fill>
|
</Rectangle.Fill>
|
||||||
|
|||||||
@ -14,18 +14,18 @@ namespace Artemis.UI.Shared
|
|||||||
/// Converts <see cref="T:Artemis.Core.Models.Profile.ColorGradient" /> into a
|
/// Converts <see cref="T:Artemis.Core.Models.Profile.ColorGradient" /> into a
|
||||||
/// <see cref="T:System.Windows.Media.GradientStopCollection" />.
|
/// <see cref="T:System.Windows.Media.GradientStopCollection" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ValueConversion(typeof(List<ColorGradientStop>), typeof(GradientStopCollection))]
|
[ValueConversion(typeof(ColorGradient), typeof(GradientStopCollection))]
|
||||||
public class ColorGradientToGradientStopsConverter : IValueConverter
|
public class ColorGradientToGradientStopsConverter : IValueConverter
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
List<ColorGradientStop> colorGradients = (List<ColorGradientStop>) value;
|
ColorGradient? colorGradient = value as ColorGradient;
|
||||||
GradientStopCollection collection = new();
|
GradientStopCollection collection = new();
|
||||||
if (colorGradients == null)
|
if (colorGradient == null)
|
||||||
return collection;
|
return collection;
|
||||||
|
|
||||||
foreach (ColorGradientStop c in colorGradients.OrderBy(s => s.Position))
|
foreach (ColorGradientStop c in colorGradient.OrderBy(s => s.Position))
|
||||||
collection.Add(new GradientStop(Color.FromArgb(c.Color.Alpha, c.Color.Red, c.Color.Green, c.Color.Blue), c.Position));
|
collection.Add(new GradientStop(Color.FromArgb(c.Color.Alpha, c.Color.Red, c.Color.Green, c.Color.Blue), c.Position));
|
||||||
return collection;
|
return collection;
|
||||||
}
|
}
|
||||||
@ -33,8 +33,8 @@ namespace Artemis.UI.Shared
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
GradientStopCollection collection = (GradientStopCollection) value;
|
GradientStopCollection? collection = value as GradientStopCollection;
|
||||||
List<ColorGradientStop> colorGradients = new();
|
ColorGradient colorGradients = new();
|
||||||
if (collection == null)
|
if (collection == null)
|
||||||
return colorGradients;
|
return colorGradients;
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,6 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
|
|||||||
{
|
{
|
||||||
_gradientEditorViewModel = gradientEditorViewModel;
|
_gradientEditorViewModel = gradientEditorViewModel;
|
||||||
ColorStop = colorStop;
|
ColorStop = colorStop;
|
||||||
ColorStop.PropertyChanged += ColorStopOnPropertyChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ColorGradientStop ColorStop { get; }
|
public ColorGradientStop ColorStop { get; }
|
||||||
@ -58,12 +57,7 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
|
|||||||
get => _willRemoveColorStop;
|
get => _willRemoveColorStop;
|
||||||
set => SetAndNotify(ref _willRemoveColorStop, value);
|
set => SetAndNotify(ref _willRemoveColorStop, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ColorStopOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
_gradientEditorViewModel.ColorGradient.OnColorValuesUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Movement
|
#region Movement
|
||||||
|
|
||||||
public void StopMouseDown(object sender, MouseButtonEventArgs e)
|
public void StopMouseDown(object sender, MouseButtonEventArgs e)
|
||||||
@ -102,7 +96,7 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
|
|||||||
|
|
||||||
double minValue = 0.0;
|
double minValue = 0.0;
|
||||||
double maxValue = _gradientEditorViewModel.PreviewWidth;
|
double maxValue = _gradientEditorViewModel.PreviewWidth;
|
||||||
List<ColorGradientStop> stops = _gradientEditorViewModel.ColorGradient.Stops.OrderBy(s => s.Position).ToList();
|
List<ColorGradientStop> stops = _gradientEditorViewModel.ColorGradient.ToList();
|
||||||
ColorGradientStop? previous = stops.IndexOf(ColorStop) >= 1 ? stops[stops.IndexOf(ColorStop) - 1] : null;
|
ColorGradientStop? previous = stops.IndexOf(ColorStop) >= 1 ? stops[stops.IndexOf(ColorStop) - 1] : null;
|
||||||
ColorGradientStop? next = stops.IndexOf(ColorStop) + 1 < stops.Count ? stops[stops.IndexOf(ColorStop) + 1] : null;
|
ColorGradientStop? next = stops.IndexOf(ColorStop) + 1 < stops.Count ? stops[stops.IndexOf(ColorStop) + 1] : null;
|
||||||
if (previous != null)
|
if (previous != null)
|
||||||
@ -111,7 +105,6 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
|
|||||||
maxValue = next.Position * _gradientEditorViewModel.PreviewWidth;
|
maxValue = next.Position * _gradientEditorViewModel.PreviewWidth;
|
||||||
|
|
||||||
Offset = Math.Max(minValue, Math.Min(maxValue, position.X));
|
Offset = Math.Max(minValue, Math.Min(maxValue, position.X));
|
||||||
_gradientEditorViewModel.ColorGradient.OnColorValuesUpdated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -66,7 +66,7 @@
|
|||||||
<Rectangle x:Name="Preview" Height="40" shared:SizeObserver.Observe="True" shared:SizeObserver.ObservedWidth="{Binding PreviewWidth, Mode=OneWayToSource}">
|
<Rectangle x:Name="Preview" Height="40" shared:SizeObserver.Observe="True" shared:SizeObserver.ObservedWidth="{Binding PreviewWidth, Mode=OneWayToSource}">
|
||||||
<Rectangle.Fill>
|
<Rectangle.Fill>
|
||||||
<LinearGradientBrush
|
<LinearGradientBrush
|
||||||
GradientStops="{Binding ColorGradient.Stops, Converter={StaticResource ColorGradientToGradientStopsConverter}}"
|
GradientStops="{Binding ColorGradient, Converter={StaticResource ColorGradientToGradientStopsConverter}}"
|
||||||
EndPoint="1,0"
|
EndPoint="1,0"
|
||||||
StartPoint="0,0" />
|
StartPoint="0,0" />
|
||||||
</Rectangle.Fill>
|
</Rectangle.Fill>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
@ -6,6 +7,7 @@ using System.Windows.Controls;
|
|||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
|
using MaterialDesignThemes.Wpf;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
|
|
||||||
namespace Artemis.UI.Shared.Screens.GradientEditor
|
namespace Artemis.UI.Shared.Screens.GradientEditor
|
||||||
@ -21,12 +23,24 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
|
|||||||
ColorGradient = colorGradient;
|
ColorGradient = colorGradient;
|
||||||
ColorStopViewModels = new BindableCollection<ColorStopViewModel>();
|
ColorStopViewModels = new BindableCollection<ColorStopViewModel>();
|
||||||
|
|
||||||
_originalStops = ColorGradient.Stops.Select(s => new ColorGradientStop(s.Color, s.Position)).ToList();
|
_originalStops = ColorGradient.Select(s => new ColorGradientStop(s.Color, s.Position)).ToList();
|
||||||
|
|
||||||
PropertyChanged += UpdateColorStopViewModels;
|
PropertyChanged += UpdateColorStopViewModels;
|
||||||
|
ColorGradient.CollectionChanged += ColorGradientOnCollectionChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BindableCollection<ColorStopViewModel> ColorStopViewModels { get; set; }
|
#region Overrides of DialogViewModelBase
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnDialogClosed(object sender, DialogClosingEventArgs e)
|
||||||
|
{
|
||||||
|
ColorGradient.CollectionChanged -= ColorGradientOnCollectionChanged;
|
||||||
|
base.OnDialogClosed(sender, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public BindableCollection<ColorStopViewModel> ColorStopViewModels { get; }
|
||||||
|
|
||||||
public ColorStopViewModel? SelectedColorStopViewModel
|
public ColorStopViewModel? SelectedColorStopViewModel
|
||||||
{
|
{
|
||||||
@ -48,15 +62,19 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
|
|||||||
set => SetAndNotify(ref _previewWidth, value);
|
set => SetAndNotify(ref _previewWidth, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ColorGradient Stops
|
||||||
|
{
|
||||||
|
get => ColorGradient;
|
||||||
|
}
|
||||||
|
|
||||||
public void AddColorStop(object sender, MouseEventArgs e)
|
public void AddColorStop(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
Canvas? child = VisualTreeUtilities.FindChild<Canvas>((DependencyObject) sender, null);
|
Canvas? child = VisualTreeUtilities.FindChild<Canvas>((DependencyObject) sender, null);
|
||||||
float position = (float) (e.GetPosition(child).X / PreviewWidth);
|
float position = (float) (e.GetPosition(child).X / PreviewWidth);
|
||||||
ColorGradientStop stop = new(ColorGradient.GetColor(position), position);
|
ColorGradientStop stop = new(ColorGradient.GetColor(position), position);
|
||||||
ColorGradient.Stops.Add(stop);
|
ColorGradient.Add(stop);
|
||||||
ColorGradient.OnColorValuesUpdated();
|
|
||||||
|
|
||||||
int index = ColorGradient.Stops.OrderBy(s => s.Position).ToList().IndexOf(stop);
|
int index = ColorGradient.IndexOf(stop);
|
||||||
ColorStopViewModel viewModel = new(this, stop);
|
ColorStopViewModel viewModel = new(this, stop);
|
||||||
ColorStopViewModels.Insert(index, viewModel);
|
ColorStopViewModels.Insert(index, viewModel);
|
||||||
|
|
||||||
@ -69,8 +87,7 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
ColorStopViewModels.Remove(colorStopViewModel);
|
ColorStopViewModels.Remove(colorStopViewModel);
|
||||||
ColorGradient.Stops.Remove(colorStopViewModel.ColorStop);
|
ColorGradient.Remove(colorStopViewModel.ColorStop);
|
||||||
ColorGradient.OnColorValuesUpdated();
|
|
||||||
|
|
||||||
SelectColorStop(null);
|
SelectColorStop(null);
|
||||||
}
|
}
|
||||||
@ -97,9 +114,9 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
|
|||||||
public override void Cancel()
|
public override void Cancel()
|
||||||
{
|
{
|
||||||
// Restore the saved state
|
// Restore the saved state
|
||||||
ColorGradient.Stops.Clear();
|
ColorGradient.Clear();
|
||||||
ColorGradient.Stops.AddRange(_originalStops);
|
foreach (ColorGradientStop colorGradientStop in _originalStops)
|
||||||
ColorGradient.OnColorValuesUpdated();
|
ColorGradient.Add(colorGradientStop);
|
||||||
|
|
||||||
base.Cancel();
|
base.Cancel();
|
||||||
}
|
}
|
||||||
@ -107,8 +124,13 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
|
|||||||
private void UpdateColorStopViewModels(object? sender, PropertyChangedEventArgs e)
|
private void UpdateColorStopViewModels(object? sender, PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.PropertyName != nameof(PreviewWidth)) return;
|
if (e.PropertyName != nameof(PreviewWidth)) return;
|
||||||
foreach (ColorGradientStop colorStop in ColorGradient.Stops.OrderBy(s => s.Position))
|
foreach (ColorGradientStop colorStop in ColorGradient)
|
||||||
ColorStopViewModels.Add(new ColorStopViewModel(this, colorStop));
|
ColorStopViewModels.Add(new ColorStopViewModel(this, colorStop));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ColorGradientOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
NotifyOfPropertyChange(nameof(ColorGradient));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Controls.Primitives;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Modules;
|
using Artemis.Core.Modules;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
@ -86,7 +87,7 @@ namespace Artemis.UI.Shared.Services
|
|||||||
{
|
{
|
||||||
if (_currentTime.Equals(value)) return;
|
if (_currentTime.Equals(value)) return;
|
||||||
_currentTime = value;
|
_currentTime = value;
|
||||||
UpdateProfilePreview();
|
Tick();
|
||||||
OnCurrentTimeChanged();
|
OnCurrentTimeChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,16 +181,9 @@ namespace Artemis.UI.Shared.Services
|
|||||||
|
|
||||||
public void UpdateProfilePreview()
|
public void UpdateProfilePreview()
|
||||||
{
|
{
|
||||||
if (SelectedProfile == null)
|
if (Playing)
|
||||||
return;
|
return;
|
||||||
|
Tick();
|
||||||
// Stick to the main segment for any element that is not currently selected
|
|
||||||
foreach (Folder folder in SelectedProfile.GetAllFolders())
|
|
||||||
folder.Timeline.Override(CurrentTime, folder.Timeline.PlayMode == TimelinePlayMode.Repeat);
|
|
||||||
foreach (Layer layer in SelectedProfile.GetAllLayers())
|
|
||||||
layer.Timeline.Override(CurrentTime, (layer != SelectedProfileElement || layer.Timeline.Length < CurrentTime) && layer.Timeline.PlayMode == TimelinePlayMode.Repeat);
|
|
||||||
|
|
||||||
_coreService.FrameRendered += CoreServiceOnFrameRendered;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool UndoUpdateProfile()
|
public bool UndoUpdateProfile()
|
||||||
@ -357,6 +351,20 @@ namespace Artemis.UI.Shared.Services
|
|||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Tick()
|
||||||
|
{
|
||||||
|
if (SelectedProfile == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Stick to the main segment for any element that is not currently selected
|
||||||
|
foreach (Folder folder in SelectedProfile.GetAllFolders())
|
||||||
|
folder.Timeline.Override(CurrentTime, folder.Timeline.PlayMode == TimelinePlayMode.Repeat);
|
||||||
|
foreach (Layer layer in SelectedProfile.GetAllLayers())
|
||||||
|
layer.Timeline.Override(CurrentTime, (layer != SelectedProfileElement || layer.Timeline.Length < CurrentTime) && layer.Timeline.PlayMode == TimelinePlayMode.Repeat);
|
||||||
|
|
||||||
|
_coreService.FrameRendered += CoreServiceOnFrameRendered;
|
||||||
|
}
|
||||||
|
|
||||||
#region Copy/paste
|
#region Copy/paste
|
||||||
|
|
||||||
public ProfileElement? DuplicateProfileElement(ProfileElement profileElement)
|
public ProfileElement? DuplicateProfileElement(ProfileElement profileElement)
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.LayerBrushes;
|
using Artemis.Core.LayerBrushes;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
|
using Artemis.UI.Screens.ProfileEditor.Dialogs;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
@ -12,12 +14,15 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
|||||||
public class BrushPropertyInputViewModel : PropertyInputViewModel<LayerBrushReference>
|
public class BrushPropertyInputViewModel : PropertyInputViewModel<LayerBrushReference>
|
||||||
{
|
{
|
||||||
private readonly IPluginManagementService _pluginManagementService;
|
private readonly IPluginManagementService _pluginManagementService;
|
||||||
|
private readonly IDialogService _dialogService;
|
||||||
private BindableCollection<LayerBrushDescriptor> _descriptors;
|
private BindableCollection<LayerBrushDescriptor> _descriptors;
|
||||||
|
|
||||||
public BrushPropertyInputViewModel(LayerProperty<LayerBrushReference> layerProperty, IProfileEditorService profileEditorService, IPluginManagementService pluginManagementService)
|
public BrushPropertyInputViewModel(LayerProperty<LayerBrushReference> layerProperty, IProfileEditorService profileEditorService, IPluginManagementService pluginManagementService,
|
||||||
|
IDialogService dialogService)
|
||||||
: base(layerProperty, profileEditorService)
|
: base(layerProperty, profileEditorService)
|
||||||
{
|
{
|
||||||
_pluginManagementService = pluginManagementService;
|
_pluginManagementService = pluginManagementService;
|
||||||
|
_dialogService = dialogService;
|
||||||
UpdateEnumValues();
|
UpdateEnumValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +48,17 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
|||||||
protected override void OnInputValueApplied()
|
protected override void OnInputValueApplied()
|
||||||
{
|
{
|
||||||
if (LayerProperty.ProfileElement is Layer layer)
|
if (LayerProperty.ProfileElement is Layer layer)
|
||||||
|
{
|
||||||
layer.ChangeLayerBrush(SelectedDescriptor);
|
layer.ChangeLayerBrush(SelectedDescriptor);
|
||||||
|
if (layer.LayerBrush?.Presets != null && layer.LayerBrush.Presets.Any())
|
||||||
|
{
|
||||||
|
Execute.PostToUIThread(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(400);
|
||||||
|
_dialogService.ShowDialogAt<LayerBrushPresetViewModel>("LayerProperties", new Dictionary<string, object> {{"layerBrush", layer.LayerBrush}});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetBrushByDescriptor(LayerBrushDescriptor value)
|
private void SetBrushByDescriptor(LayerBrushDescriptor value)
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:s="https://github.com/canton7/Stylet"
|
xmlns:s="https://github.com/canton7/Stylet"
|
||||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||||
|
xmlns:propertyInput="clr-namespace:Artemis.UI.DefaultTypes.PropertyInput"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="25" d:DesignWidth="800"
|
d:DesignHeight="25" d:DesignWidth="800"
|
||||||
d:DataContext="{d:DesignInstance propertyInput:ColorGradientPropertyInputViewModel}">
|
d:DataContext="{d:DesignInstance propertyInput:ColorGradientPropertyInputViewModel}">
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.Dialogs.LayerBrushPresetView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:s="https://github.com/canton7/Stylet"
|
||||||
|
xmlns:layerBrushes="clr-namespace:Artemis.Core.LayerBrushes;assembly=Artemis.Core"
|
||||||
|
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="450" d:DesignWidth="800">
|
||||||
|
<StackPanel Margin="16">
|
||||||
|
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}">
|
||||||
|
Select a brush preset
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<ListBox ItemsSource="{Binding Presets}" SelectedItem="{Binding SelectedPreset}" HorizontalContentAlignment="Stretch" Margin="0 8 0 16">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate DataType="{x:Type layerBrushes:ILayerBrushPreset}">
|
||||||
|
<Border x:Name="Border" Padding="8" BorderThickness="0 0 0 1" BorderBrush="{DynamicResource MaterialDesignDivider}">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<shared:ArtemisIcon Icon="{Binding Icon}" VerticalAlignment="Center" Width="30" Height="30"/>
|
||||||
|
<StackPanel Margin="8 0 0 0" Grid.Column="1">
|
||||||
|
<TextBlock FontWeight="Bold" Text="{Binding Name}" />
|
||||||
|
<TextBlock Text="{Binding Description}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
|
<Button Style="{StaticResource MaterialDesignFlatButton}" IsDefault="True" Margin="0 8 8 0" Command="{s:Action Cancel}">
|
||||||
|
USE DEFAULT SETTINGS
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Artemis.Core.LayerBrushes;
|
||||||
|
using Artemis.UI.Shared.Services;
|
||||||
|
using Stylet;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.ProfileEditor.Dialogs
|
||||||
|
{
|
||||||
|
public class LayerBrushPresetViewModel : DialogViewModelBase
|
||||||
|
{
|
||||||
|
private ILayerBrushPreset _selectedPreset;
|
||||||
|
|
||||||
|
public LayerBrushPresetViewModel(BaseLayerBrush layerBrush)
|
||||||
|
{
|
||||||
|
Presets = new BindableCollection<ILayerBrushPreset>();
|
||||||
|
Presets.AddRange(layerBrush.Presets);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindableCollection<ILayerBrushPreset> Presets { get; }
|
||||||
|
|
||||||
|
public ILayerBrushPreset SelectedPreset
|
||||||
|
{
|
||||||
|
get => _selectedPreset;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetAndNotify(ref _selectedPreset, value);
|
||||||
|
SelectPreset(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SelectPreset(ILayerBrushPreset preset)
|
||||||
|
{
|
||||||
|
preset.Apply();
|
||||||
|
Execute.OnUIThreadAsync(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(250);
|
||||||
|
Session?.Close(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -66,210 +66,188 @@
|
|||||||
<KeyBinding Command="{s:Action Play}" Key="Space" />
|
<KeyBinding Command="{s:Action Play}" Key="Space" />
|
||||||
<KeyBinding Command="{s:Action PlayFromStart}" Modifiers="Shift" Key="Space" />
|
<KeyBinding Command="{s:Action PlayFromStart}" Modifiers="Shift" Key="Space" />
|
||||||
</UserControl.InputBindings>
|
</UserControl.InputBindings>
|
||||||
<Grid x:Name="ContainerGrid">
|
<materialDesign:DialogHost Identifier="LayerProperties" DialogTheme="Inherit">
|
||||||
<Grid.RowDefinitions>
|
<Grid x:Name="ContainerGrid">
|
||||||
<RowDefinition Height="*" />
|
|
||||||
<RowDefinition Height="28" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="1.5*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<!-- Left side -->
|
|
||||||
<Grid Grid.Column="0">
|
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="48" />
|
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="28" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="1.5*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<!-- Misc controls & time display -->
|
<!-- Left side -->
|
||||||
<Border BorderThickness="0,0,1,0" BorderBrush="{DynamicResource MaterialDesignDivider}">
|
<Grid Grid.Column="0">
|
||||||
<DockPanel VerticalAlignment="Center">
|
<Grid.RowDefinitions>
|
||||||
<StackPanel Orientation="Horizontal">
|
<RowDefinition Height="48" />
|
||||||
<Button Style="{StaticResource MaterialDesignIconForegroundButton}"
|
<RowDefinition Height="*" />
|
||||||
ToolTip="Play from start (Shift+Space)" Command="{s:Action PlayFromStart}"
|
</Grid.RowDefinitions>
|
||||||
Focusable="False">
|
|
||||||
<materialDesign:PackIcon Kind="StepForward" />
|
<!-- Misc controls & time display -->
|
||||||
</Button>
|
<Border BorderThickness="0,0,1,0" BorderBrush="{DynamicResource MaterialDesignDivider}">
|
||||||
<Button Style="{StaticResource MaterialDesignIconForegroundButton}"
|
<DockPanel VerticalAlignment="Center">
|
||||||
ToolTip="Toggle play/pause (Space)" Command="{s:Action Play}" Focusable="False">
|
<StackPanel Orientation="Horizontal">
|
||||||
<StackPanel>
|
<Button Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||||
<materialDesign:PackIcon Kind="Play"
|
ToolTip="Play from start (Shift+Space)" Command="{s:Action PlayFromStart}"
|
||||||
Visibility="{Binding Playing, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}" />
|
Focusable="False">
|
||||||
<materialDesign:PackIcon Kind="Pause"
|
<materialDesign:PackIcon Kind="StepForward" />
|
||||||
Visibility="{Binding Playing, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
</Button>
|
||||||
</StackPanel>
|
<Button Style="{StaticResource MaterialDesignIconForegroundButton}"
|
||||||
</Button>
|
ToolTip="Toggle play/pause (Space)" Command="{s:Action Play}" Focusable="False">
|
||||||
<Button Style="{StaticResource MaterialDesignIconForegroundButton}" ToolTip="Go to start" Command="{s:Action GoToStart}" Focusable="False">
|
|
||||||
<materialDesign:PackIcon Kind="SkipBackward" />
|
|
||||||
</Button>
|
|
||||||
<Button Style="{StaticResource MaterialDesignIconForegroundButton}" ToolTip="Go to end" Command="{s:Action GoToEnd}" Focusable="False">
|
|
||||||
<materialDesign:PackIcon Kind="SkipForward" />
|
|
||||||
</Button>
|
|
||||||
<Button Style="{StaticResource MaterialDesignIconForegroundButton}" ToolTip="Previous frame" Command="{s:Action GoToPreviousFrame}" Focusable="False">
|
|
||||||
<materialDesign:PackIcon Kind="SkipPrevious" />
|
|
||||||
</Button>
|
|
||||||
<Button Style="{StaticResource MaterialDesignIconForegroundButton}" ToolTip="Next frame" Command="{s:Action GoToNextFrame}" Focusable="False">
|
|
||||||
<materialDesign:PackIcon Kind="SkipNext" />
|
|
||||||
</Button>
|
|
||||||
<shared:LockableToggleButton Style="{StaticResource MaterialDesignFlatToggleButton}"
|
|
||||||
IsChecked="{Binding Repeating}"
|
|
||||||
IsLocked="True"
|
|
||||||
Focusable="False"
|
|
||||||
Click="{s:Action CycleRepeating}">
|
|
||||||
<shared:LockableToggleButton.ToolTip>
|
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<StackPanel Visibility="{Binding Repeating, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
|
<materialDesign:PackIcon Kind="Play"
|
||||||
<TextBlock Text="Repeat entire timeline"
|
Visibility="{Binding Playing, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}" />
|
||||||
Visibility="{Binding RepeatTimeline, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
<materialDesign:PackIcon Kind="Pause"
|
||||||
<TextBlock Text="Repeat segment"
|
Visibility="{Binding Playing, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
||||||
Visibility="{Binding RepeatSegment, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
|
||||||
</StackPanel>
|
|
||||||
<TextBlock Visibility="{Binding Repeating, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
|
|
||||||
Don't repeat the timeline
|
|
||||||
</TextBlock>
|
|
||||||
<TextBlock TextWrapping="Wrap">
|
|
||||||
This setting only applies to the editor and <Run Text="does not" FontWeight="Bold" /> affect the repeat mode during normal profile playback
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</shared:LockableToggleButton.ToolTip>
|
</Button>
|
||||||
|
<Button Style="{StaticResource MaterialDesignIconForegroundButton}" ToolTip="Go to start" Command="{s:Action GoToStart}" Focusable="False">
|
||||||
|
<materialDesign:PackIcon Kind="SkipBackward" />
|
||||||
|
</Button>
|
||||||
|
<Button Style="{StaticResource MaterialDesignIconForegroundButton}" ToolTip="Go to end" Command="{s:Action GoToEnd}" Focusable="False">
|
||||||
|
<materialDesign:PackIcon Kind="SkipForward" />
|
||||||
|
</Button>
|
||||||
|
<Button Style="{StaticResource MaterialDesignIconForegroundButton}" ToolTip="Previous frame" Command="{s:Action GoToPreviousFrame}" Focusable="False">
|
||||||
|
<materialDesign:PackIcon Kind="SkipPrevious" />
|
||||||
|
</Button>
|
||||||
|
<Button Style="{StaticResource MaterialDesignIconForegroundButton}" ToolTip="Next frame" Command="{s:Action GoToNextFrame}" Focusable="False">
|
||||||
|
<materialDesign:PackIcon Kind="SkipNext" />
|
||||||
|
</Button>
|
||||||
|
<shared:LockableToggleButton Style="{StaticResource MaterialDesignFlatToggleButton}"
|
||||||
|
IsChecked="{Binding Repeating}"
|
||||||
|
IsLocked="True"
|
||||||
|
Focusable="False"
|
||||||
|
Click="{s:Action CycleRepeating}">
|
||||||
|
<shared:LockableToggleButton.ToolTip>
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel Visibility="{Binding Repeating, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
|
||||||
|
<TextBlock Text="Repeat entire timeline"
|
||||||
|
Visibility="{Binding RepeatTimeline, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
||||||
|
<TextBlock Text="Repeat segment"
|
||||||
|
Visibility="{Binding RepeatSegment, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock Visibility="{Binding Repeating, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
|
||||||
|
Don't repeat the timeline
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock TextWrapping="Wrap">
|
||||||
|
This setting only applies to the editor and <Run Text="does not" FontWeight="Bold" /> affect the repeat mode during normal profile playback
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</shared:LockableToggleButton.ToolTip>
|
||||||
|
<Grid>
|
||||||
|
<materialDesign:PackIcon Kind="Repeat"
|
||||||
|
Height="24"
|
||||||
|
Width="24"
|
||||||
|
Visibility="{Binding RepeatTimeline, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
||||||
|
<materialDesign:PackIcon Kind="RepeatOne"
|
||||||
|
Height="24"
|
||||||
|
Width="24"
|
||||||
|
Visibility="{Binding RepeatSegment, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</shared:LockableToggleButton>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel VerticalAlignment="Center">
|
||||||
|
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}"
|
||||||
|
Text="{Binding FormattedCurrentTime}" HorizontalAlignment="Right" Margin="0 0 20 0" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Properties tree -->
|
||||||
|
<materialDesign:DialogHost Identifier="PropertyTreeDialogHost" DialogTheme="Inherit" CloseOnClickAway="True" Grid.Row="1">
|
||||||
|
<materialDesign:Transitioner x:Name="PropertyTreeTransitioner" SelectedIndex="{Binding PropertyTreeIndex}" DefaultTransitionOrigin="0.9, 0.9" AutoApplyTransitionOrigins="True">
|
||||||
|
<ScrollViewer x:Name="PropertyTreeScrollViewer"
|
||||||
|
HorizontalScrollBarVisibility="Hidden"
|
||||||
|
VerticalScrollBarVisibility="Hidden"
|
||||||
|
ScrollChanged="TimelineScrollChanged">
|
||||||
|
<Border BorderThickness="0,0,1,0" BorderBrush="{DynamicResource MaterialDesignDivider}">
|
||||||
|
<ContentControl s:View.Model="{Binding TreeViewModel}"
|
||||||
|
shared:SizeObserver.Observe="True"
|
||||||
|
shared:SizeObserver.ObservedHeight="{Binding TreeViewModelHeight, Mode=OneWayToSource}" />
|
||||||
|
</Border>
|
||||||
|
</ScrollViewer>
|
||||||
|
<materialDesign:TransitionerSlide>
|
||||||
|
<materialDesign:TransitionerSlide.BackwardWipe>
|
||||||
|
<materialDesign:CircleWipe />
|
||||||
|
</materialDesign:TransitionerSlide.BackwardWipe>
|
||||||
<Grid>
|
<Grid>
|
||||||
<materialDesign:PackIcon Kind="Repeat"
|
<ContentControl s:View.Model="{Binding EffectsViewModel}" />
|
||||||
Height="24"
|
<!-- Transitions only work when the command comes from inside the transitioner but we want the button outside,
|
||||||
Width="24"
|
|
||||||
Visibility="{Binding RepeatTimeline, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
|
||||||
<materialDesign:PackIcon Kind="RepeatOne"
|
|
||||||
Height="24"
|
|
||||||
Width="24"
|
|
||||||
Visibility="{Binding RepeatSegment, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
</shared:LockableToggleButton>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel VerticalAlignment="Center">
|
|
||||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}"
|
|
||||||
Text="{Binding FormattedCurrentTime}" HorizontalAlignment="Right" Margin="0 0 20 0" />
|
|
||||||
</StackPanel>
|
|
||||||
</DockPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Properties tree -->
|
|
||||||
<materialDesign:DialogHost Identifier="PropertyTreeDialogHost" DialogTheme="Inherit" CloseOnClickAway="True" Grid.Row="1">
|
|
||||||
<materialDesign:Transitioner x:Name="PropertyTreeTransitioner" SelectedIndex="{Binding PropertyTreeIndex}" DefaultTransitionOrigin="0.9, 0.9" AutoApplyTransitionOrigins="True">
|
|
||||||
<ScrollViewer x:Name="PropertyTreeScrollViewer"
|
|
||||||
HorizontalScrollBarVisibility="Hidden"
|
|
||||||
VerticalScrollBarVisibility="Hidden"
|
|
||||||
ScrollChanged="TimelineScrollChanged">
|
|
||||||
<Border BorderThickness="0,0,1,0" BorderBrush="{DynamicResource MaterialDesignDivider}">
|
|
||||||
<ContentControl s:View.Model="{Binding TreeViewModel}"
|
|
||||||
shared:SizeObserver.Observe="True"
|
|
||||||
shared:SizeObserver.ObservedHeight="{Binding TreeViewModelHeight, Mode=OneWayToSource}"/>
|
|
||||||
</Border>
|
|
||||||
</ScrollViewer>
|
|
||||||
<materialDesign:TransitionerSlide>
|
|
||||||
<materialDesign:TransitionerSlide.BackwardWipe>
|
|
||||||
<materialDesign:CircleWipe />
|
|
||||||
</materialDesign:TransitionerSlide.BackwardWipe>
|
|
||||||
<Grid>
|
|
||||||
<ContentControl s:View.Model="{Binding EffectsViewModel}" />
|
|
||||||
<!-- Transitions only work when the command comes from inside the transitioner but we want the button outside,
|
|
||||||
by setting the command target to this hidden button we circumvent that -->
|
by setting the command target to this hidden button we circumvent that -->
|
||||||
<Button x:Name="TransitionCommandAnchor"
|
<Button x:Name="TransitionCommandAnchor"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
VerticalAlignment="Bottom"
|
VerticalAlignment="Bottom"
|
||||||
Width="110"
|
Width="110"
|
||||||
Height="1"
|
Height="1"
|
||||||
Visibility="Hidden" />
|
Visibility="Hidden" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</materialDesign:TransitionerSlide>
|
</materialDesign:TransitionerSlide>
|
||||||
</materialDesign:Transitioner>
|
</materialDesign:Transitioner>
|
||||||
</materialDesign:DialogHost>
|
</materialDesign:DialogHost>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- Resize -->
|
<!-- Resize -->
|
||||||
<GridSplitter Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Width="15" HorizontalAlignment="Stretch" Cursor="SizeWE" Margin="-15 0" Background="Transparent" />
|
<GridSplitter Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Width="15" HorizontalAlignment="Stretch" Cursor="SizeWE" Margin="-15 0" Background="Transparent" />
|
||||||
|
|
||||||
<!-- Right side -->
|
<!-- Right side -->
|
||||||
<materialDesign:Transitioner Grid.Row="0"
|
<materialDesign:Transitioner Grid.Row="0"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
x:Name="RightSideTransitioner"
|
x:Name="RightSideTransitioner"
|
||||||
SelectedIndex="{Binding RightSideIndex}"
|
SelectedIndex="{Binding RightSideIndex}"
|
||||||
DefaultTransitionOrigin="0, 0.5">
|
DefaultTransitionOrigin="0, 0.5">
|
||||||
<materialDesign:TransitionerSlide>
|
<materialDesign:TransitionerSlide>
|
||||||
<materialDesign:TransitionerSlide.BackwardWipe>
|
<materialDesign:TransitionerSlide.BackwardWipe>
|
||||||
<materialDesign:CircleWipe />
|
<materialDesign:CircleWipe />
|
||||||
</materialDesign:TransitionerSlide.BackwardWipe>
|
</materialDesign:TransitionerSlide.BackwardWipe>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.InputBindings>
|
<Grid.InputBindings>
|
||||||
<KeyBinding Key="Delete" Command="{s:Action DeleteKeyframes}" s:View.ActionTarget="{Binding TimelineViewModel}"/>
|
<KeyBinding Key="Delete" Command="{s:Action DeleteKeyframes}" s:View.ActionTarget="{Binding TimelineViewModel}" />
|
||||||
<KeyBinding Key="D" Modifiers="Control" Command="{s:Action DuplicateKeyframes}" s:View.ActionTarget="{Binding TimelineViewModel}" />
|
<KeyBinding Key="D" Modifiers="Control" Command="{s:Action DuplicateKeyframes}" s:View.ActionTarget="{Binding TimelineViewModel}" />
|
||||||
<KeyBinding Key="C" Modifiers="Control" Command="{s:Action CopyKeyframes}" s:View.ActionTarget="{Binding TimelineViewModel}"/>
|
<KeyBinding Key="C" Modifiers="Control" Command="{s:Action CopyKeyframes}" s:View.ActionTarget="{Binding TimelineViewModel}" />
|
||||||
<KeyBinding Key="V" Modifiers="Control" Command="{s:Action PasteKeyframes}" s:View.ActionTarget="{Binding TimelineViewModel}"/>
|
<KeyBinding Key="V" Modifiers="Control" Command="{s:Action PasteKeyframes}" s:View.ActionTarget="{Binding TimelineViewModel}" />
|
||||||
</Grid.InputBindings>
|
</Grid.InputBindings>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="48" />
|
<RowDefinition Height="48" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<!-- Timeline headers -->
|
<!-- Timeline headers -->
|
||||||
<ScrollViewer Grid.Row="0"
|
<ScrollViewer Grid.Row="0"
|
||||||
x:Name="TimelineHeaderScrollViewer"
|
x:Name="TimelineHeaderScrollViewer"
|
||||||
HorizontalScrollBarVisibility="Hidden"
|
HorizontalScrollBarVisibility="Hidden"
|
||||||
VerticalScrollBarVisibility="Hidden"
|
VerticalScrollBarVisibility="Hidden"
|
||||||
ScrollChanged="TimelineScrollChanged">
|
ScrollChanged="TimelineScrollChanged">
|
||||||
<Canvas Background="{DynamicResource MaterialDesignCardBackground}" Width="{Binding ActualWidth, ElementName=PropertyTimeLine}">
|
<Canvas Background="{DynamicResource MaterialDesignCardBackground}" Width="{Binding ActualWidth, ElementName=PropertyTimeLine}">
|
||||||
<!-- Timeline segments -->
|
<!-- Timeline segments -->
|
||||||
<ContentControl Canvas.Left="{Binding EndTimelineSegmentViewModel.SegmentStartPosition}" s:View.Model="{Binding EndTimelineSegmentViewModel}" />
|
<ContentControl Canvas.Left="{Binding EndTimelineSegmentViewModel.SegmentStartPosition}" s:View.Model="{Binding EndTimelineSegmentViewModel}" />
|
||||||
<ContentControl Canvas.Left="{Binding MainTimelineSegmentViewModel.SegmentStartPosition}" s:View.Model="{Binding MainTimelineSegmentViewModel}" />
|
<ContentControl Canvas.Left="{Binding MainTimelineSegmentViewModel.SegmentStartPosition}" s:View.Model="{Binding MainTimelineSegmentViewModel}" />
|
||||||
<ContentControl Canvas.Left="{Binding StartTimelineSegmentViewModel.SegmentStartPosition}" s:View.Model="{Binding StartTimelineSegmentViewModel}" />
|
<ContentControl Canvas.Left="{Binding StartTimelineSegmentViewModel.SegmentStartPosition}" s:View.Model="{Binding StartTimelineSegmentViewModel}" />
|
||||||
|
|
||||||
<!-- Timeline header body -->
|
<!-- Timeline header body -->
|
||||||
<controls:PropertyTimelineHeader Margin="0 18 0 0"
|
<controls:PropertyTimelineHeader Margin="0 18 0 0"
|
||||||
Foreground="{DynamicResource MaterialDesignBody}"
|
Foreground="{DynamicResource MaterialDesignBody}"
|
||||||
Background="{DynamicResource MaterialDesignCardBackground}"
|
Background="{DynamicResource MaterialDesignCardBackground}"
|
||||||
PixelsPerSecond="{Binding ProfileEditorService.PixelsPerSecond}"
|
PixelsPerSecond="{Binding ProfileEditorService.PixelsPerSecond}"
|
||||||
HorizontalOffset="{Binding ContentHorizontalOffset, ElementName=TimelineHeaderScrollViewer}"
|
HorizontalOffset="{Binding ContentHorizontalOffset, ElementName=TimelineHeaderScrollViewer}"
|
||||||
VisibleWidth="{Binding ActualWidth, ElementName=TimelineHeaderScrollViewer}"
|
VisibleWidth="{Binding ActualWidth, ElementName=TimelineHeaderScrollViewer}"
|
||||||
OffsetFirstValue="True"
|
OffsetFirstValue="True"
|
||||||
MouseLeftButtonUp="{s:Action TimelineJump}"
|
MouseLeftButtonUp="{s:Action TimelineJump}"
|
||||||
Width="{Binding ActualWidth, ElementName=PropertyTimeLine}"
|
Width="{Binding ActualWidth, ElementName=PropertyTimeLine}"
|
||||||
Cursor="Hand"/>
|
Cursor="Hand" />
|
||||||
|
|
||||||
<!-- Timeline caret -->
|
<!-- Timeline caret -->
|
||||||
<Polygon Canvas.Left="{Binding TimeCaretPosition}"
|
<Polygon Canvas.Left="{Binding TimeCaretPosition}"
|
||||||
Cursor="SizeWE"
|
Cursor="SizeWE"
|
||||||
MouseDown="{s:Action TimelineMouseDown}"
|
MouseDown="{s:Action TimelineMouseDown}"
|
||||||
MouseUp="{s:Action TimelineMouseUp}"
|
MouseUp="{s:Action TimelineMouseUp}"
|
||||||
MouseMove="{s:Action TimelineMouseMove}"
|
MouseMove="{s:Action TimelineMouseMove}"
|
||||||
Points="-8,0 -8,8 0,20, 8,8 8,0"
|
Points="-8,0 -8,8 0,20, 8,8 8,0"
|
||||||
Fill="{StaticResource SecondaryAccentBrush}" />
|
Fill="{StaticResource SecondaryAccentBrush}" />
|
||||||
<Line Canvas.Left="{Binding TimeCaretPosition}"
|
|
||||||
Cursor="SizeWE"
|
|
||||||
MouseDown="{s:Action TimelineMouseDown}"
|
|
||||||
MouseUp="{s:Action TimelineMouseUp}"
|
|
||||||
MouseMove="{s:Action TimelineMouseMove}"
|
|
||||||
X1="0"
|
|
||||||
X2="0"
|
|
||||||
Y1="0"
|
|
||||||
Y2="{Binding ActualHeight, ElementName=ContainerGrid}"
|
|
||||||
StrokeThickness="2"
|
|
||||||
Stroke="{StaticResource SecondaryAccentBrush}" />
|
|
||||||
</Canvas>
|
|
||||||
</ScrollViewer>
|
|
||||||
|
|
||||||
<!-- Timeline rails -->
|
|
||||||
<ScrollViewer Grid.Row="1"
|
|
||||||
x:Name="TimelineRailsScrollViewer"
|
|
||||||
Style="{StaticResource SvStyle}"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
ScrollChanged="TimelineScrollChanged">
|
|
||||||
<Grid Background="{DynamicResource MaterialDesignToolBarBackground}">
|
|
||||||
<Canvas Grid.Column="0" Panel.ZIndex="1">
|
|
||||||
<Line Canvas.Left="{Binding TimeCaretPosition}"
|
<Line Canvas.Left="{Binding TimeCaretPosition}"
|
||||||
Cursor="SizeWE"
|
Cursor="SizeWE"
|
||||||
MouseDown="{s:Action TimelineMouseDown}"
|
MouseDown="{s:Action TimelineMouseDown}"
|
||||||
@ -278,169 +256,193 @@
|
|||||||
X1="0"
|
X1="0"
|
||||||
X2="0"
|
X2="0"
|
||||||
Y1="0"
|
Y1="0"
|
||||||
Y2="{Binding ActualHeight, ElementName=ContainerGrid}"
|
Y2="{Binding ActualHeight, ElementName=TimelineHeaderScrollViewer}"
|
||||||
StrokeThickness="2"
|
StrokeThickness="2"
|
||||||
Stroke="{StaticResource SecondaryAccentBrush}" />
|
Stroke="{StaticResource SecondaryAccentBrush}" />
|
||||||
</Canvas>
|
</Canvas>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
<ContentControl Grid.Column="0" s:View.Model="{Binding TimelineViewModel}" x:Name="PropertyTimeLine" />
|
<!-- Timeline rails -->
|
||||||
</Grid>
|
<ScrollViewer Grid.Row="1"
|
||||||
</ScrollViewer>
|
x:Name="TimelineRailsScrollViewer"
|
||||||
</Grid>
|
Style="{StaticResource SvStyle}"
|
||||||
</materialDesign:TransitionerSlide>
|
HorizontalScrollBarVisibility="Auto"
|
||||||
|
VerticalScrollBarVisibility="Auto"
|
||||||
|
ScrollChanged="TimelineScrollChanged">
|
||||||
|
<Grid Background="{DynamicResource MaterialDesignToolBarBackground}" x:Name="RailsGrid">
|
||||||
|
<Canvas Grid.Column="0" Panel.ZIndex="1">
|
||||||
|
<Line Canvas.Left="{Binding TimeCaretPosition}"
|
||||||
|
Cursor="SizeWE"
|
||||||
|
MouseDown="{s:Action TimelineMouseDown}"
|
||||||
|
MouseUp="{s:Action TimelineMouseUp}"
|
||||||
|
MouseMove="{s:Action TimelineMouseMove}"
|
||||||
|
X1="0"
|
||||||
|
X2="0"
|
||||||
|
Y1="0"
|
||||||
|
Y2="{Binding ActualHeight, ElementName=RailsGrid}"
|
||||||
|
StrokeThickness="2"
|
||||||
|
Stroke="{StaticResource SecondaryAccentBrush}" />
|
||||||
|
</Canvas>
|
||||||
|
|
||||||
<materialDesign:TransitionerSlide>
|
<ContentControl Grid.Column="0" s:View.Model="{Binding TimelineViewModel}" x:Name="PropertyTimeLine" />
|
||||||
<materialDesign:TransitionerSlide.BackwardWipe>
|
</Grid>
|
||||||
<materialDesign:CircleWipe />
|
</ScrollViewer>
|
||||||
</materialDesign:TransitionerSlide.BackwardWipe>
|
</Grid>
|
||||||
<Grid Background="{StaticResource MaterialDesignCardBackground}">
|
</materialDesign:TransitionerSlide>
|
||||||
<ContentControl s:View.Model="{Binding DataBindingsViewModel}" />
|
|
||||||
</Grid>
|
|
||||||
</materialDesign:TransitionerSlide>
|
|
||||||
</materialDesign:Transitioner>
|
|
||||||
|
|
||||||
<!-- Bottom row, a bit hacky but has a ZIndex of 2 to cut off the time caret that overlaps the entire timeline -->
|
<materialDesign:TransitionerSlide>
|
||||||
<Grid Grid.Row="1"
|
<materialDesign:TransitionerSlide.BackwardWipe>
|
||||||
Grid.Column="0"
|
<materialDesign:CircleWipe />
|
||||||
Panel.ZIndex="2"
|
</materialDesign:TransitionerSlide.BackwardWipe>
|
||||||
HorizontalAlignment="Stretch"
|
<Grid Background="{StaticResource MaterialDesignCardBackground}">
|
||||||
Background="{DynamicResource MaterialDesignCardBackground}">
|
<ContentControl s:View.Model="{Binding DataBindingsViewModel}" />
|
||||||
<!-- Selected layer controls -->
|
</Grid>
|
||||||
<Grid.ColumnDefinitions>
|
</materialDesign:TransitionerSlide>
|
||||||
<ColumnDefinition />
|
</materialDesign:Transitioner>
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<StackPanel Grid.Column="0"
|
<!-- Bottom row, a bit hacky but has a ZIndex of 2 to cut off the time caret that overlaps the entire timeline -->
|
||||||
|
<Grid Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Panel.ZIndex="2"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Background="{DynamicResource MaterialDesignCardBackground}">
|
||||||
|
<!-- Selected layer controls -->
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Grid.Column="0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Margin="6"
|
||||||
|
Visibility="{Binding SelectedLayer, Converter={StaticResource NullToVisibilityConverter}}">
|
||||||
|
<materialDesign:PackIcon Kind="Layers" Width="16" />
|
||||||
|
<materialDesign:PackIcon Kind="{Binding SelectedLayer.LayerBrush.Descriptor.Icon}"
|
||||||
|
Width="16"
|
||||||
|
Margin="5 0 0 0"
|
||||||
|
ToolTip="{Binding SelectedLayer.LayerBrush.Descriptor.DisplayName, Mode=OneWay}"
|
||||||
|
Background="Transparent"
|
||||||
|
Visibility="{Binding SelectedLayer.LayerBrush, Converter={StaticResource NullToVisibilityConverter}}" />
|
||||||
|
<TextBlock Text="{Binding SelectedLayer.Name}" Margin="5 0 0 0" />
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Margin="6"
|
||||||
|
Visibility="{Binding SelectedFolder, Converter={StaticResource NullToVisibilityConverter}}">
|
||||||
|
<materialDesign:PackIcon Kind="Folder" Width="16" />
|
||||||
|
<TextBlock Text="{Binding SelectedFolder.Name}" Margin="5 0 0 0" />
|
||||||
|
</StackPanel>
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
Style="{StaticResource MaterialDesignFlatMidBgButton}"
|
||||||
|
Margin="0 -2 5 -2"
|
||||||
|
Padding="10 0"
|
||||||
|
Height="20"
|
||||||
|
Width="115"
|
||||||
|
ToolTip="Select an effect to add"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Visibility="{Binding PropertyTreeVisible, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
||||||
|
Command="{x:Static materialDesign:Transitioner.MoveLastCommand}"
|
||||||
|
CommandTarget="{Binding ElementName=TransitionCommandAnchor}"
|
||||||
|
Click="{s:Action ToggleEffectsViewModel}"
|
||||||
|
IsEnabled="{Binding CanToggleEffectsViewModel}">
|
||||||
|
<TextBlock FontSize="11">
|
||||||
|
ADD EFFECT
|
||||||
|
</TextBlock>
|
||||||
|
</Button>
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
Style="{StaticResource MaterialDesignFlatMidBgButton}"
|
||||||
|
Margin="0 -2 5 -2"
|
||||||
|
Padding="10 0"
|
||||||
|
Height="20"
|
||||||
|
Width="115"
|
||||||
|
ToolTip="Show the layer/folder properties"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Visibility="{Binding PropertyTreeVisible, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}}"
|
||||||
|
Command="{x:Static materialDesign:Transitioner.MoveFirstCommand}"
|
||||||
|
CommandTarget="{Binding ElementName=TransitionCommandAnchor}"
|
||||||
|
Click="{s:Action ToggleEffectsViewModel}"
|
||||||
|
IsEnabled="{Binding CanToggleEffectsViewModel}">
|
||||||
|
<TextBlock FontSize="10">
|
||||||
|
SHOW PROPERTIES
|
||||||
|
</TextBlock>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1"
|
||||||
|
Grid.Column="2"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Panel.ZIndex="2"
|
||||||
|
Background="{DynamicResource MaterialDesignCardBackground}">
|
||||||
|
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Button Grid.Column="0"
|
||||||
|
Margin="5 -2 0 -2"
|
||||||
|
Padding="10 0"
|
||||||
|
Height="20"
|
||||||
|
Width="110"
|
||||||
|
ToolTip="Add a new segment to the timeline"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
IsEnabled="{Binding SelectedProfileElement, Converter={StaticResource NullToBooleanConverter}}"
|
||||||
|
Visibility="{Binding TimelineVisible, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">
|
||||||
|
<Button.Style>
|
||||||
|
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignFlatMidBgButton}">
|
||||||
|
<Style.Triggers>
|
||||||
|
<EventTrigger RoutedEvent="Click">
|
||||||
|
<EventTrigger.Actions>
|
||||||
|
<BeginStoryboard>
|
||||||
|
<Storyboard>
|
||||||
|
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
|
||||||
|
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True" />
|
||||||
|
</BooleanAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
</BeginStoryboard>
|
||||||
|
</EventTrigger.Actions>
|
||||||
|
</EventTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Button.Style>
|
||||||
|
<Button.ContextMenu>
|
||||||
|
<ContextMenu>
|
||||||
|
<ContextMenu.Resources>
|
||||||
|
<Converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
|
||||||
|
</ContextMenu.Resources>
|
||||||
|
<MenuItem Header="Start"
|
||||||
|
Command="{s:Action EnableSegment}"
|
||||||
|
CommandParameter="Start"
|
||||||
|
IsEnabled="{Binding Data.StartTimelineSegmentViewModel.SegmentEnabled, Converter={StaticResource InverseBooleanConverter}, Source={StaticResource DataContextProxy}}" />
|
||||||
|
<MenuItem Header="Main"
|
||||||
|
Command="{s:Action EnableSegment}"
|
||||||
|
CommandParameter="Main"
|
||||||
|
IsEnabled="{Binding Data.MainTimelineSegmentViewModel.SegmentEnabled, Converter={StaticResource InverseBooleanConverter}, Source={StaticResource DataContextProxy}}" />
|
||||||
|
<MenuItem Header="End"
|
||||||
|
Command="{s:Action EnableSegment}"
|
||||||
|
CommandParameter="End"
|
||||||
|
IsEnabled="{Binding Data.EndTimelineSegmentViewModel.SegmentEnabled, Converter={StaticResource InverseBooleanConverter}, Source={StaticResource DataContextProxy}}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</Button.ContextMenu>
|
||||||
|
<TextBlock FontSize="11">
|
||||||
|
ADD SEGMENT
|
||||||
|
</TextBlock>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<!-- Zoom control -->
|
||||||
|
<Slider Grid.Column="1"
|
||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
Margin="6"
|
HorizontalAlignment="Right"
|
||||||
Visibility="{Binding SelectedLayer, Converter={StaticResource NullToVisibilityConverter}}">
|
Margin="10 5"
|
||||||
<materialDesign:PackIcon Kind="Layers" Width="16" />
|
Minimum="31"
|
||||||
<materialDesign:PackIcon Kind="{Binding SelectedLayer.LayerBrush.Descriptor.Icon}"
|
Maximum="350"
|
||||||
Width="16"
|
TickFrequency="1"
|
||||||
Margin="5 0 0 0"
|
IsSnapToTickEnabled="True"
|
||||||
ToolTip="{Binding SelectedLayer.LayerBrush.Descriptor.DisplayName, Mode=OneWay}"
|
AutoToolTipPlacement="TopLeft"
|
||||||
Background="Transparent"
|
Value="{Binding ProfileEditorService.PixelsPerSecond}"
|
||||||
Visibility="{Binding SelectedLayer.LayerBrush, Converter={StaticResource NullToVisibilityConverter}}" />
|
Width="319" />
|
||||||
<TextBlock Text="{Binding SelectedLayer.Name}" Margin="5 0 0 0" />
|
|
||||||
</StackPanel>
|
</Grid>
|
||||||
<StackPanel Grid.Column="0"
|
|
||||||
Orientation="Horizontal"
|
|
||||||
Margin="6"
|
|
||||||
Visibility="{Binding SelectedFolder, Converter={StaticResource NullToVisibilityConverter}}">
|
|
||||||
<materialDesign:PackIcon Kind="Folder" Width="16" />
|
|
||||||
<TextBlock Text="{Binding SelectedFolder.Name}" Margin="5 0 0 0" />
|
|
||||||
</StackPanel>
|
|
||||||
<Button Grid.Column="1"
|
|
||||||
Style="{StaticResource MaterialDesignFlatMidBgButton}"
|
|
||||||
Margin="0 -2 5 -2"
|
|
||||||
Padding="10 0"
|
|
||||||
Height="20"
|
|
||||||
Width="115"
|
|
||||||
ToolTip="Select an effect to add"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Visibility="{Binding PropertyTreeVisible, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
|
||||||
Command="{x:Static materialDesign:Transitioner.MoveLastCommand}"
|
|
||||||
CommandTarget="{Binding ElementName=TransitionCommandAnchor}"
|
|
||||||
Click="{s:Action ToggleEffectsViewModel}"
|
|
||||||
IsEnabled="{Binding CanToggleEffectsViewModel}">
|
|
||||||
<TextBlock FontSize="11">
|
|
||||||
ADD EFFECT
|
|
||||||
</TextBlock>
|
|
||||||
</Button>
|
|
||||||
<Button Grid.Column="1"
|
|
||||||
Style="{StaticResource MaterialDesignFlatMidBgButton}"
|
|
||||||
Margin="0 -2 5 -2"
|
|
||||||
Padding="10 0"
|
|
||||||
Height="20"
|
|
||||||
Width="115"
|
|
||||||
ToolTip="Show the layer/folder properties"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Visibility="{Binding PropertyTreeVisible, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}}"
|
|
||||||
Command="{x:Static materialDesign:Transitioner.MoveFirstCommand}"
|
|
||||||
CommandTarget="{Binding ElementName=TransitionCommandAnchor}"
|
|
||||||
Click="{s:Action ToggleEffectsViewModel}"
|
|
||||||
IsEnabled="{Binding CanToggleEffectsViewModel}">
|
|
||||||
<TextBlock FontSize="10">
|
|
||||||
SHOW PROPERTIES
|
|
||||||
</TextBlock>
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</materialDesign:DialogHost>
|
||||||
<Grid Grid.Row="1"
|
|
||||||
Grid.Column="2"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
Panel.ZIndex="2"
|
|
||||||
Background="{DynamicResource MaterialDesignCardBackground}">
|
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<Button Grid.Column="0"
|
|
||||||
Margin="5 -2 0 -2"
|
|
||||||
Padding="10 0"
|
|
||||||
Height="20"
|
|
||||||
Width="110"
|
|
||||||
ToolTip="Add a new segment to the timeline"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
IsEnabled="{Binding SelectedProfileElement, Converter={StaticResource NullToBooleanConverter}}"
|
|
||||||
Visibility="{Binding TimelineVisible, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">
|
|
||||||
<Button.Style>
|
|
||||||
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignFlatMidBgButton}">
|
|
||||||
<Style.Triggers>
|
|
||||||
<EventTrigger RoutedEvent="Click">
|
|
||||||
<EventTrigger.Actions>
|
|
||||||
<BeginStoryboard>
|
|
||||||
<Storyboard>
|
|
||||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
|
|
||||||
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True" />
|
|
||||||
</BooleanAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</BeginStoryboard>
|
|
||||||
</EventTrigger.Actions>
|
|
||||||
</EventTrigger>
|
|
||||||
</Style.Triggers>
|
|
||||||
</Style>
|
|
||||||
</Button.Style>
|
|
||||||
<Button.ContextMenu>
|
|
||||||
<ContextMenu>
|
|
||||||
<ContextMenu.Resources>
|
|
||||||
<Converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
|
|
||||||
</ContextMenu.Resources>
|
|
||||||
<MenuItem Header="Start"
|
|
||||||
Command="{s:Action EnableSegment}"
|
|
||||||
CommandParameter="Start"
|
|
||||||
IsEnabled="{Binding Data.StartTimelineSegmentViewModel.SegmentEnabled, Converter={StaticResource InverseBooleanConverter}, Source={StaticResource DataContextProxy}}" />
|
|
||||||
<MenuItem Header="Main"
|
|
||||||
Command="{s:Action EnableSegment}"
|
|
||||||
CommandParameter="Main"
|
|
||||||
IsEnabled="{Binding Data.MainTimelineSegmentViewModel.SegmentEnabled, Converter={StaticResource InverseBooleanConverter}, Source={StaticResource DataContextProxy}}" />
|
|
||||||
<MenuItem Header="End"
|
|
||||||
Command="{s:Action EnableSegment}"
|
|
||||||
CommandParameter="End"
|
|
||||||
IsEnabled="{Binding Data.EndTimelineSegmentViewModel.SegmentEnabled, Converter={StaticResource InverseBooleanConverter}, Source={StaticResource DataContextProxy}}" />
|
|
||||||
</ContextMenu>
|
|
||||||
</Button.ContextMenu>
|
|
||||||
<TextBlock FontSize="11">
|
|
||||||
ADD SEGMENT
|
|
||||||
</TextBlock>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<!-- Zoom control -->
|
|
||||||
<Slider Grid.Column="1"
|
|
||||||
Orientation="Horizontal"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Margin="10 5"
|
|
||||||
Minimum="31"
|
|
||||||
Maximum="350"
|
|
||||||
TickFrequency="1"
|
|
||||||
IsSnapToTickEnabled="True"
|
|
||||||
AutoToolTipPlacement="TopLeft"
|
|
||||||
Value="{Binding ProfileEditorService.PixelsPerSecond}"
|
|
||||||
Width="319" />
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
Loading…
x
Reference in New Issue
Block a user