mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Color brush - Added linear gradient rotation
Profile editor - Updated conditions UI
This commit is contained in:
parent
7b1cefca8b
commit
10c839f8c9
@ -21,6 +21,7 @@ namespace Artemis.Core.Models.Profile
|
|||||||
Parent = parent;
|
Parent = parent;
|
||||||
Name = name;
|
Name = name;
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
|
DisplayContinuously = true;
|
||||||
|
|
||||||
_layerEffects = new List<BaseLayerEffect>();
|
_layerEffects = new List<BaseLayerEffect>();
|
||||||
_expandedPropertyGroups = new List<string>();
|
_expandedPropertyGroups = new List<string>();
|
||||||
@ -95,7 +96,7 @@ namespace Artemis.Core.Models.Profile
|
|||||||
|
|
||||||
if (stickToMainSegment)
|
if (stickToMainSegment)
|
||||||
{
|
{
|
||||||
if (!RepeatMainSegment)
|
if (!DisplayContinuously)
|
||||||
{
|
{
|
||||||
var position = timeOverride + StartSegmentLength;
|
var position = timeOverride + StartSegmentLength;
|
||||||
if (position > StartSegmentLength + EndSegmentLength)
|
if (position > StartSegmentLength + EndSegmentLength)
|
||||||
@ -146,6 +147,16 @@ namespace Artemis.Core.Models.Profile
|
|||||||
|
|
||||||
folderPath.Transform(SKMatrix.MakeTranslation(folderPath.Bounds.Left * -1, folderPath.Bounds.Top * -1));
|
folderPath.Transform(SKMatrix.MakeTranslation(folderPath.Bounds.Left * -1, folderPath.Bounds.Top * -1));
|
||||||
|
|
||||||
|
var targetLocation = Path.Bounds.Location;
|
||||||
|
if (Parent is Folder parentFolder)
|
||||||
|
targetLocation -= parentFolder.Path.Bounds.Location;
|
||||||
|
|
||||||
|
canvas.Save();
|
||||||
|
|
||||||
|
using var clipPath = new SKPath(folderPath);
|
||||||
|
clipPath.Transform(SKMatrix.MakeTranslation(targetLocation.X, targetLocation.Y));
|
||||||
|
canvas.ClipPath(clipPath);
|
||||||
|
|
||||||
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||||
baseLayerEffect.PreProcess(folderCanvas, _folderBitmap.Info, folderPath, folderPaint);
|
baseLayerEffect.PreProcess(folderCanvas, _folderBitmap.Info, folderPath, folderPaint);
|
||||||
|
|
||||||
@ -162,16 +173,6 @@ namespace Artemis.Core.Models.Profile
|
|||||||
folderCanvas.Restore();
|
folderCanvas.Restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetLocation = Path.Bounds.Location;
|
|
||||||
if (Parent is Folder parentFolder)
|
|
||||||
targetLocation -= parentFolder.Path.Bounds.Location;
|
|
||||||
|
|
||||||
canvas.Save();
|
|
||||||
|
|
||||||
using var clipPath = new SKPath(folderPath);
|
|
||||||
clipPath.Transform(SKMatrix.MakeTranslation(targetLocation.X, targetLocation.Y));
|
|
||||||
canvas.ClipPath(clipPath);
|
|
||||||
|
|
||||||
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||||
baseLayerEffect.PostProcess(canvas, canvasInfo, folderPath, folderPaint);
|
baseLayerEffect.PostProcess(canvas, canvasInfo, folderPath, folderPaint);
|
||||||
canvas.DrawBitmap(_folderBitmap, targetLocation, folderPaint);
|
canvas.DrawBitmap(_folderBitmap, targetLocation, folderPaint);
|
||||||
|
|||||||
@ -39,6 +39,7 @@ namespace Artemis.Core.Models.Profile
|
|||||||
Parent = parent;
|
Parent = parent;
|
||||||
Name = name;
|
Name = name;
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
|
DisplayContinuously = true;
|
||||||
General = new LayerGeneralProperties {IsCorePropertyGroup = true};
|
General = new LayerGeneralProperties {IsCorePropertyGroup = true};
|
||||||
Transform = new LayerTransformProperties {IsCorePropertyGroup = true};
|
Transform = new LayerTransformProperties {IsCorePropertyGroup = true};
|
||||||
|
|
||||||
@ -131,8 +132,11 @@ namespace Artemis.Core.Models.Profile
|
|||||||
keyframes.AddRange(baseLayerProperty.BaseKeyframes);
|
keyframes.AddRange(baseLayerProperty.BaseKeyframes);
|
||||||
foreach (var baseLayerProperty in Transform.GetAllLayerProperties())
|
foreach (var baseLayerProperty in Transform.GetAllLayerProperties())
|
||||||
keyframes.AddRange(baseLayerProperty.BaseKeyframes);
|
keyframes.AddRange(baseLayerProperty.BaseKeyframes);
|
||||||
foreach (var baseLayerProperty in LayerBrush.BaseProperties.GetAllLayerProperties())
|
if (LayerBrush?.BaseProperties != null)
|
||||||
keyframes.AddRange(baseLayerProperty.BaseKeyframes);
|
{
|
||||||
|
foreach (var baseLayerProperty in LayerBrush.BaseProperties.GetAllLayerProperties())
|
||||||
|
keyframes.AddRange(baseLayerProperty.BaseKeyframes);
|
||||||
|
}
|
||||||
|
|
||||||
return keyframes;
|
return keyframes;
|
||||||
}
|
}
|
||||||
@ -246,7 +250,7 @@ namespace Artemis.Core.Models.Profile
|
|||||||
|
|
||||||
if (stickToMainSegment)
|
if (stickToMainSegment)
|
||||||
{
|
{
|
||||||
if (!RepeatMainSegment)
|
if (!DisplayContinuously)
|
||||||
{
|
{
|
||||||
var position = timeOverride + StartSegmentLength;
|
var position = timeOverride + StartSegmentLength;
|
||||||
if (position > StartSegmentLength + EndSegmentLength)
|
if (position > StartSegmentLength + EndSegmentLength)
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Core.Events;
|
||||||
using Artemis.Core.Models.Profile.LayerProperties.Attributes;
|
using Artemis.Core.Models.Profile.LayerProperties.Attributes;
|
||||||
using Artemis.Storage.Entities.Profile;
|
using Artemis.Storage.Entities.Profile;
|
||||||
|
|
||||||
@ -102,61 +103,61 @@ namespace Artemis.Core.Models.Profile.LayerProperties
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs once every frame when the layer property is updated
|
/// Occurs once every frame when the layer property is updated
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler Updated;
|
public event EventHandler<LayerPropertyEventArgs> Updated;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when the base value of the layer property was updated
|
/// Occurs when the base value of the layer property was updated
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler BaseValueChanged;
|
public event EventHandler<LayerPropertyEventArgs> BaseValueChanged;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when the <see cref="IsHidden" /> value of the layer property was updated
|
/// Occurs when the <see cref="IsHidden" /> value of the layer property was updated
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler VisibilityChanged;
|
public event EventHandler<LayerPropertyEventArgs> VisibilityChanged;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when keyframes are enabled/disabled
|
/// Occurs when keyframes are enabled/disabled
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler KeyframesToggled;
|
public event EventHandler<LayerPropertyEventArgs> KeyframesToggled;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when a new keyframe was added to the layer property
|
/// Occurs when a new keyframe was added to the layer property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler KeyframeAdded;
|
public event EventHandler<LayerPropertyEventArgs> KeyframeAdded;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when a keyframe was removed from the layer property
|
/// Occurs when a keyframe was removed from the layer property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler KeyframeRemoved;
|
public event EventHandler<LayerPropertyEventArgs> KeyframeRemoved;
|
||||||
|
|
||||||
protected virtual void OnUpdated()
|
protected virtual void OnUpdated()
|
||||||
{
|
{
|
||||||
Updated?.Invoke(this, EventArgs.Empty);
|
Updated?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnBaseValueChanged()
|
protected virtual void OnBaseValueChanged()
|
||||||
{
|
{
|
||||||
BaseValueChanged?.Invoke(this, EventArgs.Empty);
|
BaseValueChanged?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnVisibilityChanged()
|
protected virtual void OnVisibilityChanged()
|
||||||
{
|
{
|
||||||
VisibilityChanged?.Invoke(this, EventArgs.Empty);
|
VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnKeyframesToggled()
|
protected virtual void OnKeyframesToggled()
|
||||||
{
|
{
|
||||||
KeyframesToggled?.Invoke(this, EventArgs.Empty);
|
KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnKeyframeAdded()
|
protected virtual void OnKeyframeAdded()
|
||||||
{
|
{
|
||||||
KeyframeAdded?.Invoke(this, EventArgs.Empty);
|
KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnKeyframeRemoved()
|
protected virtual void OnKeyframeRemoved()
|
||||||
{
|
{
|
||||||
KeyframeRemoved?.Invoke(this, EventArgs.Empty);
|
KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -253,13 +253,29 @@ namespace Artemis.Core.Models.Profile
|
|||||||
}
|
}
|
||||||
|
|
||||||
instance.ApplyToLayerProperty(entity, this, fromStorage);
|
instance.ApplyToLayerProperty(entity, this, fromStorage);
|
||||||
|
instance.BaseValueChanged += InstanceOnBaseValueChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InstanceOnBaseValueChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
OnLayerPropertyBaseValueChanged(new LayerPropertyEventArgs((BaseLayerProperty) sender));
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
|
|
||||||
internal event EventHandler PropertyGroupUpdating;
|
internal event EventHandler PropertyGroupUpdating;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the property group has initialized all its children
|
||||||
|
/// </summary>
|
||||||
public event EventHandler PropertyGroupInitialized;
|
public event EventHandler PropertyGroupInitialized;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when one of the base value of one of the layer properties in this group changes
|
||||||
|
/// <para>Note: Will not trigger on properties in child groups</para>
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<LayerPropertyEventArgs> LayerPropertyBaseValueChanged;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when the <see cref="IsHidden" /> value of the layer property was updated
|
/// Occurs when the <see cref="IsHidden" /> value of the layer property was updated
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -275,6 +291,11 @@ namespace Artemis.Core.Models.Profile
|
|||||||
VisibilityChanged?.Invoke(this, EventArgs.Empty);
|
VisibilityChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void OnLayerPropertyBaseValueChanged(LayerPropertyEventArgs e)
|
||||||
|
{
|
||||||
|
LayerPropertyBaseValueChanged?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@ namespace Artemis.Core.Models.Profile
|
|||||||
StartSegmentLength = RenderElementEntity.StartSegmentLength;
|
StartSegmentLength = RenderElementEntity.StartSegmentLength;
|
||||||
MainSegmentLength = RenderElementEntity.MainSegmentLength;
|
MainSegmentLength = RenderElementEntity.MainSegmentLength;
|
||||||
EndSegmentLength = RenderElementEntity.EndSegmentLength;
|
EndSegmentLength = RenderElementEntity.EndSegmentLength;
|
||||||
RepeatMainSegment = RenderElementEntity.RepeatMainSegment;
|
DisplayContinuously = RenderElementEntity.DisplayContinuously;
|
||||||
AlwaysFinishTimeline = RenderElementEntity.AlwaysFinishTimeline;
|
AlwaysFinishTimeline = RenderElementEntity.AlwaysFinishTimeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ namespace Artemis.Core.Models.Profile
|
|||||||
RenderElementEntity.StartSegmentLength = StartSegmentLength;
|
RenderElementEntity.StartSegmentLength = StartSegmentLength;
|
||||||
RenderElementEntity.MainSegmentLength = MainSegmentLength;
|
RenderElementEntity.MainSegmentLength = MainSegmentLength;
|
||||||
RenderElementEntity.EndSegmentLength = EndSegmentLength;
|
RenderElementEntity.EndSegmentLength = EndSegmentLength;
|
||||||
RenderElementEntity.RepeatMainSegment = RepeatMainSegment;
|
RenderElementEntity.DisplayContinuously = DisplayContinuously;
|
||||||
RenderElementEntity.AlwaysFinishTimeline = AlwaysFinishTimeline;
|
RenderElementEntity.AlwaysFinishTimeline = AlwaysFinishTimeline;
|
||||||
|
|
||||||
RenderElementEntity.LayerEffects.Clear();
|
RenderElementEntity.LayerEffects.Clear();
|
||||||
@ -127,7 +127,7 @@ namespace Artemis.Core.Models.Profile
|
|||||||
private TimeSpan _startSegmentLength;
|
private TimeSpan _startSegmentLength;
|
||||||
private TimeSpan _mainSegmentLength;
|
private TimeSpan _mainSegmentLength;
|
||||||
private TimeSpan _endSegmentLength;
|
private TimeSpan _endSegmentLength;
|
||||||
private bool _repeatMainSegment;
|
private bool _displayContinuously;
|
||||||
private bool _alwaysFinishTimeline;
|
private bool _alwaysFinishTimeline;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -174,10 +174,10 @@ namespace Artemis.Core.Models.Profile
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets whether main timeline should repeat itself as long as display conditions are met
|
/// Gets or sets whether main timeline should repeat itself as long as display conditions are met
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RepeatMainSegment
|
public bool DisplayContinuously
|
||||||
{
|
{
|
||||||
get => _repeatMainSegment;
|
get => _displayContinuously;
|
||||||
set => SetAndNotify(ref _repeatMainSegment, value);
|
set => SetAndNotify(ref _displayContinuously, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -201,7 +201,7 @@ namespace Artemis.Core.Models.Profile
|
|||||||
if (DisplayConditionMet)
|
if (DisplayConditionMet)
|
||||||
{
|
{
|
||||||
// If we are at the end of the main timeline, wrap around back to the beginning
|
// If we are at the end of the main timeline, wrap around back to the beginning
|
||||||
if (RepeatMainSegment && TimelinePosition >= mainSegmentEnd)
|
if (DisplayContinuously && TimelinePosition >= mainSegmentEnd)
|
||||||
TimelinePosition = StartSegmentLength + (mainSegmentEnd - TimelinePosition);
|
TimelinePosition = StartSegmentLength + (mainSegmentEnd - TimelinePosition);
|
||||||
else if (TimelinePosition >= TimelineLength)
|
else if (TimelinePosition >= TimelineLength)
|
||||||
TimelinePosition = TimelineLength;
|
TimelinePosition = TimelineLength;
|
||||||
|
|||||||
@ -8,7 +8,7 @@ namespace Artemis.Storage.Entities.Profile.Abstract
|
|||||||
public TimeSpan StartSegmentLength { get; set; }
|
public TimeSpan StartSegmentLength { get; set; }
|
||||||
public TimeSpan MainSegmentLength { get; set; }
|
public TimeSpan MainSegmentLength { get; set; }
|
||||||
public TimeSpan EndSegmentLength { get; set; }
|
public TimeSpan EndSegmentLength { get; set; }
|
||||||
public bool RepeatMainSegment { get; set; }
|
public bool DisplayContinuously { get; set; }
|
||||||
public bool AlwaysFinishTimeline { get; set; }
|
public bool AlwaysFinishTimeline { get; set; }
|
||||||
|
|
||||||
public List<LayerEffectEntity> LayerEffects { get; set; }
|
public List<LayerEffectEntity> LayerEffects { get; set; }
|
||||||
|
|||||||
@ -22,6 +22,8 @@ namespace Artemis.Storage.Migrations
|
|||||||
folder.MainSegmentLength = folder.PropertyEntities.Where(p => p.KeyframeEntities.Any()).Max(p => p.KeyframeEntities.Max(k => k.Position));
|
folder.MainSegmentLength = folder.PropertyEntities.Where(p => p.KeyframeEntities.Any()).Max(p => p.KeyframeEntities.Max(k => k.Position));
|
||||||
if (folder.MainSegmentLength == TimeSpan.Zero)
|
if (folder.MainSegmentLength == TimeSpan.Zero)
|
||||||
folder.MainSegmentLength = TimeSpan.FromSeconds(5);
|
folder.MainSegmentLength = TimeSpan.FromSeconds(5);
|
||||||
|
|
||||||
|
folder.DisplayContinuously = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var layer in profileEntity.Layers.Where(l => l.MainSegmentLength == TimeSpan.Zero))
|
foreach (var layer in profileEntity.Layers.Where(l => l.MainSegmentLength == TimeSpan.Zero))
|
||||||
@ -30,6 +32,8 @@ namespace Artemis.Storage.Migrations
|
|||||||
layer.MainSegmentLength = layer.PropertyEntities.Where(p => p.KeyframeEntities.Any()).Max(p => p.KeyframeEntities.Max(k => k.Position));
|
layer.MainSegmentLength = layer.PropertyEntities.Where(p => p.KeyframeEntities.Any()).Max(p => p.KeyframeEntities.Max(k => k.Position));
|
||||||
if (layer.MainSegmentLength == TimeSpan.Zero)
|
if (layer.MainSegmentLength == TimeSpan.Zero)
|
||||||
layer.MainSegmentLength = TimeSpan.FromSeconds(5);
|
layer.MainSegmentLength = TimeSpan.FromSeconds(5);
|
||||||
|
|
||||||
|
layer.DisplayContinuously = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
repository.Update(profileEntity);
|
repository.Update(profileEntity);
|
||||||
|
|||||||
40
src/Artemis.UI/Converters/NullToBooleanConverter.cs
Normal file
40
src/Artemis.UI/Converters/NullToBooleanConverter.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Windows.Data;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Converters
|
||||||
|
{
|
||||||
|
public class NullToBooleanConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
Parameters direction;
|
||||||
|
if (parameter == null)
|
||||||
|
direction = Parameters.Normal;
|
||||||
|
else
|
||||||
|
direction = (Parameters)Enum.Parse(typeof(Parameters), (string)parameter);
|
||||||
|
|
||||||
|
if (direction == Parameters.Normal)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == null)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum Parameters
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Inverted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,7 +8,7 @@
|
|||||||
xmlns:s="https://github.com/canton7/Stylet"
|
xmlns:s="https://github.com/canton7/Stylet"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="450" d:DesignWidth="800">
|
d:DesignHeight="450" d:DesignWidth="800">
|
||||||
<TextBox VerticalAlignment="Center" Text="{Binding InputValue}" PreviewTextInput="{s:Action NumberValidationTextBox}" LostFocus="{s:Action Submit}">
|
<TextBox VerticalAlignment="Center" Text="{Binding InputValue}" PreviewTextInput="{s:Action NumberValidationTextBox}" LostFocus="{s:Action Submit}" Width="140">
|
||||||
<b:Interaction.Behaviors>
|
<b:Interaction.Behaviors>
|
||||||
<behaviors:PutCursorAtEndTextBoxBehavior/>
|
<behaviors:PutCursorAtEndTextBoxBehavior/>
|
||||||
</b:Interaction.Behaviors>
|
</b:Interaction.Behaviors>
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
xmlns:s="https://github.com/canton7/Stylet"
|
xmlns:s="https://github.com/canton7/Stylet"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="450" d:DesignWidth="800">
|
d:DesignHeight="450" d:DesignWidth="800">
|
||||||
<TextBox VerticalAlignment="Center" Text="{Binding InputValue}" PreviewTextInput="{s:Action NumberValidationTextBox}" LostFocus="{s:Action Submit}">
|
<TextBox VerticalAlignment="Center" Text="{Binding InputValue}" PreviewTextInput="{s:Action NumberValidationTextBox}" LostFocus="{s:Action Submit}" Width="140">
|
||||||
<b:Interaction.Behaviors>
|
<b:Interaction.Behaviors>
|
||||||
<behaviors:PutCursorAtEndTextBoxBehavior/>
|
<behaviors:PutCursorAtEndTextBoxBehavior/>
|
||||||
</b:Interaction.Behaviors>
|
</b:Interaction.Behaviors>
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
xmlns:s="https://github.com/canton7/Stylet"
|
xmlns:s="https://github.com/canton7/Stylet"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="450" d:DesignWidth="800">
|
d:DesignHeight="450" d:DesignWidth="800">
|
||||||
<TextBox VerticalAlignment="Center" Text="{Binding InputValue}" LostFocus="{s:Action Submit}">
|
<TextBox VerticalAlignment="Center" Text="{Binding InputValue}" LostFocus="{s:Action Submit}" Width="140">
|
||||||
<b:Interaction.Behaviors>
|
<b:Interaction.Behaviors>
|
||||||
<behaviors:PutCursorAtEndTextBoxBehavior/>
|
<behaviors:PutCursorAtEndTextBoxBehavior/>
|
||||||
</b:Interaction.Behaviors>
|
</b:Interaction.Behaviors>
|
||||||
|
|||||||
@ -38,7 +38,8 @@
|
|||||||
Command="{s:Action Delete}">
|
Command="{s:Action Delete}">
|
||||||
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
|
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button Grid.Row="0"
|
<Button x:Name="BooleanOperatorButton"
|
||||||
|
Grid.Row="0"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Style="{StaticResource DisplayConditionButtonLeftClickMenu}"
|
Style="{StaticResource DisplayConditionButtonLeftClickMenu}"
|
||||||
Background="#E74C4C"
|
Background="#E74C4C"
|
||||||
|
|||||||
@ -16,6 +16,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
|||||||
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
|
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
|
||||||
private bool _isRootGroup;
|
private bool _isRootGroup;
|
||||||
private bool _isInitialized;
|
private bool _isInitialized;
|
||||||
|
private bool _isSelectedBooleanOperatorOpen;
|
||||||
|
|
||||||
public DisplayConditionGroupViewModel(DisplayConditionGroup displayConditionGroup, DisplayConditionViewModel parent,
|
public DisplayConditionGroupViewModel(DisplayConditionGroup displayConditionGroup, DisplayConditionViewModel parent,
|
||||||
IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory) : base(displayConditionGroup, parent)
|
IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory) : base(displayConditionGroup, parent)
|
||||||
@ -43,6 +44,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
|||||||
set => SetAndNotify(ref _isInitialized, value);
|
set => SetAndNotify(ref _isInitialized, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsSelectedBooleanOperatorOpen
|
||||||
|
{
|
||||||
|
get => _isSelectedBooleanOperatorOpen;
|
||||||
|
set => SetAndNotify(ref _isSelectedBooleanOperatorOpen, value);
|
||||||
|
}
|
||||||
|
|
||||||
public string SelectedBooleanOperator => DisplayConditionGroup.BooleanOperator.Humanize();
|
public string SelectedBooleanOperator => DisplayConditionGroup.BooleanOperator.Humanize();
|
||||||
|
|
||||||
public void SelectBooleanOperator(string type)
|
public void SelectBooleanOperator(string type)
|
||||||
|
|||||||
@ -150,7 +150,7 @@
|
|||||||
Command="{s:Action ActivateRightSideInputViewModel}"
|
Command="{s:Action ActivateRightSideInputViewModel}"
|
||||||
HorizontalAlignment="Left">
|
HorizontalAlignment="Left">
|
||||||
<Grid>
|
<Grid>
|
||||||
<StackPanel Visibility="{Binding RightStaticValue, Converter={StaticResource NullToVisibilityConverter}}" Orientation="Horizontal">
|
<StackPanel x:Name="StaticValueDisplay" Visibility="{Binding RightStaticValue, Converter={StaticResource NullToVisibilityConverter}}" Orientation="Horizontal" >
|
||||||
<TextBlock FontWeight="Light"
|
<TextBlock FontWeight="Light"
|
||||||
Text="{Binding SelectedLeftSideProperty.PropertyDescription.Prefix}"
|
Text="{Binding SelectedLeftSideProperty.PropertyDescription.Prefix}"
|
||||||
Visibility="{Binding SelectedLeftSideProperty.PropertyDescription.Prefix, Converter={StaticResource NullToVisibilityConverter}}" />
|
Visibility="{Binding SelectedLeftSideProperty.PropertyDescription.Prefix, Converter={StaticResource NullToVisibilityConverter}}" />
|
||||||
@ -169,7 +169,6 @@
|
|||||||
Background="{DynamicResource MaterialDesignPaper}"
|
Background="{DynamicResource MaterialDesignPaper}"
|
||||||
CornerRadius="3"
|
CornerRadius="3"
|
||||||
Padding="3"
|
Padding="3"
|
||||||
Width="140"
|
|
||||||
HorizontalAlignment="Left">
|
HorizontalAlignment="Left">
|
||||||
<ContentControl s:View.Model="{Binding RightSideInputViewModel}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
|
<ContentControl s:View.Model="{Binding RightSideInputViewModel}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@ -14,67 +14,166 @@
|
|||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
|
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<Grid>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Margin="10 5 0 -4">
|
|
||||||
Display conditions
|
|
||||||
</TextBlock>
|
|
||||||
<Separator Grid.Row="1" Grid.Column="0" Style="{StaticResource MaterialDesignDarkSeparator}" Margin="8 0" />
|
|
||||||
|
|
||||||
<Grid Grid.Row="2" Grid.Column="0">
|
<materialDesign:Transitioner AutoApplyTransitionOrigins="True" SelectedIndex="{Binding TransitionerIndex}">
|
||||||
<ScrollViewer Margin="8 0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
|
<!-- Conditions intro -->
|
||||||
<ContentControl s:View.Model="{Binding RootGroup}" />
|
<materialDesign:ColorZone Mode="PrimaryDark" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" VerticalContentAlignment="Stretch">
|
||||||
</ScrollViewer>
|
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Margin="32">
|
||||||
|
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}" TextWrapping="Wrap" TextAlignment="Center" Margin="0 15">Unleash Artemis's true potential</TextBlock>
|
||||||
|
<TextBlock TextWrapping="Wrap" TextAlignment="Center">Start using conditions to dynamically show and hide groups and layers</TextBlock>
|
||||||
|
|
||||||
|
<Button Style="{DynamicResource MaterialDesignFloatingActionAccentButton}" Command="{x:Static materialDesign:Transitioner.MoveNextCommand}" Margin="16">
|
||||||
|
<materialDesign:PackIcon Kind="ArrowRight" />
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</materialDesign:ColorZone>
|
||||||
|
|
||||||
|
<!-- Conditions content -->
|
||||||
|
<Grid Margin="10">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Margin="0 0 0 -4">
|
||||||
|
Display conditions
|
||||||
|
</TextBlock>
|
||||||
|
<Separator Grid.Row="1" Grid.Column="0" Style="{StaticResource MaterialDesignDarkSeparator}" Margin="-2 0" />
|
||||||
|
|
||||||
|
<Grid Grid.Row="2" Grid.Column="0">
|
||||||
|
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Background="{StaticResource MaterialDesignCardBackground}">
|
||||||
|
<ContentControl s:View.Model="{Binding RootGroup}" />
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid Grid.Row="3">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="20" />
|
||||||
|
<RowDefinition />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" MinWidth="140" />
|
||||||
|
<ColumnDefinition Width="*" MinWidth="170" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<!-- Play mode -->
|
||||||
|
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||||
|
<materialDesign:PackIcon Kind="PlayOutline" VerticalAlignment="Center" />
|
||||||
|
<TextBlock Text="Play mode" VerticalAlignment="Center">
|
||||||
|
<TextBlock.ToolTip>
|
||||||
|
<ToolTip Placement="Center" VerticalOffset="-30">
|
||||||
|
<TextBlock>
|
||||||
|
Configure how the layer should act while the conditions above are met
|
||||||
|
</TextBlock>
|
||||||
|
</ToolTip>
|
||||||
|
</TextBlock.ToolTip>
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<materialDesign:ColorZone Grid.Row="1" Grid.Column="0" Mode="Standard" CornerRadius="3" Margin="0 0 2 0">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<RadioButton Grid.Column="0"
|
||||||
|
Style="{StaticResource MaterialDesignTabRadioButton}"
|
||||||
|
IsChecked="{Binding RenderProfileElement.DisplayContinuously}"
|
||||||
|
MinWidth="0"
|
||||||
|
Padding="5 0">
|
||||||
|
<RadioButton.ToolTip>
|
||||||
|
<ToolTip Placement="Center" VerticalOffset="-40">
|
||||||
|
<TextBlock>
|
||||||
|
Continue repeating the main segment of the timeline while the condition is met
|
||||||
|
</TextBlock>
|
||||||
|
</ToolTip>
|
||||||
|
</RadioButton.ToolTip>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<materialDesign:PackIcon Kind="Repeat" VerticalAlignment="Center" />
|
||||||
|
<TextBlock FontSize="12" VerticalAlignment="Center">REPEAT</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</RadioButton>
|
||||||
|
<RadioButton Grid.Column="1"
|
||||||
|
Style="{StaticResource MaterialDesignTabRadioButton}"
|
||||||
|
IsChecked="{Binding RenderProfileElement.DisplayContinuously, Converter={StaticResource InverseBooleanConverter}}"
|
||||||
|
MinWidth="0"
|
||||||
|
Padding="5 0">
|
||||||
|
<RadioButton.ToolTip>
|
||||||
|
<ToolTip Placement="Center" VerticalOffset="-40">
|
||||||
|
<TextBlock>
|
||||||
|
Only play the timeline once when the condition is met
|
||||||
|
</TextBlock>
|
||||||
|
</ToolTip>
|
||||||
|
</RadioButton.ToolTip>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<materialDesign:PackIcon Kind="StopwatchOutline" VerticalAlignment="Center" />
|
||||||
|
<TextBlock FontSize="12" VerticalAlignment="Center">ONCE</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</RadioButton>
|
||||||
|
</Grid>
|
||||||
|
</materialDesign:ColorZone>
|
||||||
|
|
||||||
|
<!-- Stop mode -->
|
||||||
|
<StackPanel Grid.Column="1" Orientation="Horizontal">
|
||||||
|
<materialDesign:PackIcon Kind="Stop" VerticalAlignment="Center" />
|
||||||
|
<TextBlock Text="Stop mode" VerticalAlignment="Center">
|
||||||
|
<TextBlock.ToolTip>
|
||||||
|
<ToolTip Placement="Center" VerticalOffset="-30">
|
||||||
|
<TextBlock>
|
||||||
|
Configure how the layer should act when the conditions above are no longer met
|
||||||
|
</TextBlock>
|
||||||
|
</ToolTip>
|
||||||
|
</TextBlock.ToolTip>
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
<materialDesign:ColorZone Grid.Row="1" Grid.Column="1" Mode="Standard" CornerRadius="3" Margin="2 0 0 0" HorizontalAlignment="Stretch">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition MinWidth="100" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<RadioButton Grid.Column="0"
|
||||||
|
Style="{StaticResource MaterialDesignTabRadioButton}"
|
||||||
|
IsChecked="{Binding RenderProfileElement.AlwaysFinishTimeline}"
|
||||||
|
MinWidth="0"
|
||||||
|
Padding="5 0">
|
||||||
|
<RadioButton.ToolTip>
|
||||||
|
<ToolTip Placement="Center" VerticalOffset="-40">
|
||||||
|
<TextBlock>
|
||||||
|
When conditions are no longer met, finish the the current run of the main timeline
|
||||||
|
</TextBlock>
|
||||||
|
</ToolTip>
|
||||||
|
</RadioButton.ToolTip>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<materialDesign:PackIcon Kind="PlayOutline" VerticalAlignment="Center" />
|
||||||
|
<TextBlock FontSize="12" VerticalAlignment="Center">FINISH</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</RadioButton>
|
||||||
|
<RadioButton Grid.Column="1"
|
||||||
|
Style="{StaticResource MaterialDesignTabRadioButton}"
|
||||||
|
IsChecked="{Binding RenderProfileElement.AlwaysFinishTimeline, Converter={StaticResource InverseBooleanConverter}}"
|
||||||
|
MinWidth="0"
|
||||||
|
Padding="5 0">
|
||||||
|
<RadioButton.ToolTip>
|
||||||
|
<ToolTip Placement="Center" VerticalOffset="-40">
|
||||||
|
<TextBlock>
|
||||||
|
When conditions are no longer met, skip to the end segment of the timeline
|
||||||
|
</TextBlock>
|
||||||
|
</ToolTip>
|
||||||
|
</RadioButton.ToolTip>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<materialDesign:PackIcon Kind="SkipNextOutline" VerticalAlignment="Center" />
|
||||||
|
<TextBlock FontSize="12" VerticalAlignment="Center">SKIP TO END</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</RadioButton>
|
||||||
|
</Grid>
|
||||||
|
</materialDesign:ColorZone>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<StackPanel Grid.Row="3" Margin="10" HorizontalAlignment="Right" Orientation="Horizontal">
|
|
||||||
<TextBlock VerticalAlignment="Center" Margin="0 0 5 0">On end: </TextBlock>
|
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal">
|
</materialDesign:Transitioner>
|
||||||
<RadioButton Style="{StaticResource MaterialDesignTabRadioButton}"
|
|
||||||
IsChecked="{Binding RenderProfileElement.AlwaysFinishTimeline}"
|
|
||||||
Padding="5 0">
|
|
||||||
<RadioButton.ToolTip>
|
|
||||||
<ToolTip Placement="Top" VerticalOffset="-5">
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock>
|
|
||||||
When conditions are no longer met, finish the timelines and then stop displaying.
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</ToolTip>
|
|
||||||
</RadioButton.ToolTip>
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<materialDesign:PackIcon Kind="PlayArrow" Width="20" Height="20" VerticalAlignment="Center" />
|
|
||||||
<TextBlock Margin="5 0 0 0" FontSize="12" VerticalAlignment="Center">
|
|
||||||
WAIT FOR FINISH
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</RadioButton>
|
|
||||||
<RadioButton Style="{StaticResource MaterialDesignTabRadioButton}"
|
|
||||||
IsChecked="{Binding RenderProfileElement.AlwaysFinishTimeline, Converter={StaticResource InverseBooleanConverter}}"
|
|
||||||
Padding="0">
|
|
||||||
<RadioButton.ToolTip>
|
|
||||||
<ToolTip Placement="Top" VerticalOffset="-5">
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock>
|
|
||||||
When conditions are no longer met, stop displaying immediately.
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</ToolTip>
|
|
||||||
</RadioButton.ToolTip>
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<materialDesign:PackIcon Kind="SkipNext" Width="20" Height="20" VerticalAlignment="Center" Margin="-5 0 0 0"/>
|
|
||||||
<TextBlock Margin="5 0 0 0" FontSize="12" VerticalAlignment="Center">
|
|
||||||
SKIP
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</RadioButton>
|
|
||||||
</StackPanel>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core.Models.Profile;
|
using Artemis.Core.Models.Profile;
|
||||||
using Artemis.Core.Models.Profile.Conditions;
|
using Artemis.Core.Models.Profile.Conditions;
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
@ -15,12 +16,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
|||||||
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
|
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
|
||||||
private DisplayConditionGroupViewModel _rootGroup;
|
private DisplayConditionGroupViewModel _rootGroup;
|
||||||
private RenderProfileElement _renderProfileElement;
|
private RenderProfileElement _renderProfileElement;
|
||||||
|
private int _transitionerIndex;
|
||||||
|
|
||||||
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory)
|
public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory)
|
||||||
{
|
{
|
||||||
_profileEditorService = profileEditorService;
|
_profileEditorService = profileEditorService;
|
||||||
_displayConditionsVmFactory = displayConditionsVmFactory;
|
_displayConditionsVmFactory = displayConditionsVmFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int TransitionerIndex
|
||||||
|
{
|
||||||
|
get => _transitionerIndex;
|
||||||
|
set => SetAndNotify(ref _transitionerIndex, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisplayConditionGroupViewModel RootGroup
|
public DisplayConditionGroupViewModel RootGroup
|
||||||
@ -69,6 +76,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions
|
|||||||
RootGroup = _displayConditionsVmFactory.DisplayConditionGroupViewModel(e.RenderProfileElement.DisplayConditionGroup, null);
|
RootGroup = _displayConditionsVmFactory.DisplayConditionGroupViewModel(e.RenderProfileElement.DisplayConditionGroup, null);
|
||||||
RootGroup.IsRootGroup = true;
|
RootGroup.IsRootGroup = true;
|
||||||
RootGroup.Update();
|
RootGroup.Update();
|
||||||
|
|
||||||
|
// Only show the intro to conditions once, and only if the layer has no conditions
|
||||||
|
if (TransitionerIndex != 1)
|
||||||
|
TransitionerIndex = RootGroup.Children.Any() ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnActivate()
|
protected override void OnActivate()
|
||||||
|
|||||||
@ -50,6 +50,15 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.LayerEffects
|
|||||||
LayerEffectDescriptors.AddRange(descriptors.Except(LayerEffectDescriptors));
|
LayerEffectDescriptors.AddRange(descriptors.Except(LayerEffectDescriptors));
|
||||||
LayerEffectDescriptors.RemoveRange(LayerEffectDescriptors.Except(descriptors));
|
LayerEffectDescriptors.RemoveRange(LayerEffectDescriptors.Except(descriptors));
|
||||||
|
|
||||||
|
// Sort by display name
|
||||||
|
var index = 0;
|
||||||
|
foreach (var layerEffectDescriptor in LayerEffectDescriptors.OrderBy(d => d.DisplayName).ToList())
|
||||||
|
{
|
||||||
|
if (LayerEffectDescriptors.IndexOf(layerEffectDescriptor) != index)
|
||||||
|
LayerEffectDescriptors.Move(LayerEffectDescriptors.IndexOf(layerEffectDescriptor), index);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
SelectedLayerEffectDescriptor = null;
|
SelectedLayerEffectDescriptor = null;
|
||||||
NotifyOfPropertyChange(nameof(HasLayerEffectDescriptors));
|
NotifyOfPropertyChange(nameof(HasLayerEffectDescriptors));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
behaviors:InputBindingBehavior.PropagateInputBindingsToWindow="True">
|
behaviors:InputBindingBehavior.PropagateInputBindingsToWindow="True">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<Converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
<Converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||||
|
<Converters:NullToBooleanConverter x:Key="NullToBooleanConverter" />
|
||||||
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
|
<utilities:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
|
||||||
<Style x:Key="SvStyle" TargetType="{x:Type ScrollViewer}">
|
<Style x:Key="SvStyle" TargetType="{x:Type ScrollViewer}">
|
||||||
<Setter Property="OverridesDefaultStyle" Value="True" />
|
<Setter Property="OverridesDefaultStyle" Value="True" />
|
||||||
@ -273,6 +274,7 @@
|
|||||||
ToolTip="Select an effect to add"
|
ToolTip="Select an effect to add"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Visibility="{Binding PropertyTreeVisible, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
Visibility="{Binding PropertyTreeVisible, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
||||||
|
IsEnabled="{Binding SelectedProfileElement, Converter={StaticResource NullToBooleanConverter}}"
|
||||||
Command="{x:Static materialDesign:Transitioner.MoveLastCommand}"
|
Command="{x:Static materialDesign:Transitioner.MoveLastCommand}"
|
||||||
CommandTarget="{Binding ElementName=TransitionCommandAnchor}">
|
CommandTarget="{Binding ElementName=TransitionCommandAnchor}">
|
||||||
<TextBlock FontSize="11">
|
<TextBlock FontSize="11">
|
||||||
@ -315,7 +317,7 @@
|
|||||||
ToolTip="Select an effect to add"
|
ToolTip="Select an effect to add"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Visibility="{Binding PropertyTreeVisible, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
Visibility="{Binding PropertyTreeVisible, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
||||||
IsEnabled="{Binding CanAddSegment}">
|
IsEnabled="{Binding SelectedProfileElement, Converter={StaticResource NullToBooleanConverter}}">
|
||||||
<Button.Style>
|
<Button.Style>
|
||||||
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignFlatMidBgButton}">
|
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignFlatMidBgButton}">
|
||||||
<Style.Triggers>
|
<Style.Triggers>
|
||||||
@ -362,7 +364,7 @@
|
|||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Margin="10 5"
|
Margin="10 5"
|
||||||
Minimum="100"
|
Minimum="31"
|
||||||
Maximum="350"
|
Maximum="350"
|
||||||
TickFrequency="1"
|
TickFrequency="1"
|
||||||
IsSnapToTickEnabled="True"
|
IsSnapToTickEnabled="True"
|
||||||
|
|||||||
@ -78,14 +78,14 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
|
|||||||
if (Segment != SegmentViewModelType.Main)
|
if (Segment != SegmentViewModelType.Main)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return SelectedProfileElement?.RepeatMainSegment ?? false;
|
return SelectedProfileElement?.DisplayContinuously ?? false;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (Segment != SegmentViewModelType.Main)
|
if (Segment != SegmentViewModelType.Main)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SelectedProfileElement.RepeatMainSegment = value;
|
SelectedProfileElement.DisplayContinuously = value;
|
||||||
ProfileEditorService.UpdateSelectedProfileElement();
|
ProfileEditorService.UpdateSelectedProfileElement();
|
||||||
NotifyOfPropertyChange(nameof(RepeatSegment));
|
NotifyOfPropertyChange(nameof(RepeatSegment));
|
||||||
}
|
}
|
||||||
@ -269,6 +269,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
|
|||||||
NotifyOfPropertyChange(nameof(SegmentStartPosition));
|
NotifyOfPropertyChange(nameof(SegmentStartPosition));
|
||||||
NotifyOfPropertyChange(nameof(SegmentWidth));
|
NotifyOfPropertyChange(nameof(SegmentWidth));
|
||||||
}
|
}
|
||||||
|
else if (e.PropertyName == nameof(RenderProfileElement.DisplayContinuously))
|
||||||
|
NotifyOfPropertyChange(nameof(RepeatSegment));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -12,14 +12,12 @@ namespace Artemis.Plugins.LayerBrushes.Color
|
|||||||
private SKPaint _paint;
|
private SKPaint _paint;
|
||||||
private SKShader _shader;
|
private SKShader _shader;
|
||||||
private SKRect _shaderBounds;
|
private SKRect _shaderBounds;
|
||||||
|
private float _linearGradientRotation;
|
||||||
|
|
||||||
public override void EnableLayerBrush()
|
public override void EnableLayerBrush()
|
||||||
{
|
{
|
||||||
Layer.RenderPropertiesUpdated += HandleShaderChange;
|
Layer.RenderPropertiesUpdated += HandleShaderChange;
|
||||||
Properties.GradientType.BaseValueChanged += HandleShaderChange;
|
Properties.LayerPropertyBaseValueChanged += HandleShaderChange;
|
||||||
Properties.Color.BaseValueChanged += HandleShaderChange;
|
|
||||||
Properties.GradientTileMode.BaseValueChanged += HandleShaderChange;
|
|
||||||
Properties.GradientRepeat.BaseValueChanged += HandleShaderChange;
|
|
||||||
Properties.Gradient.BaseValue.PropertyChanged += BaseValueOnPropertyChanged;
|
Properties.Gradient.BaseValue.PropertyChanged += BaseValueOnPropertyChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,13 +38,12 @@ namespace Artemis.Plugins.LayerBrushes.Color
|
|||||||
|
|
||||||
public override void Update(double deltaTime)
|
public override void Update(double deltaTime)
|
||||||
{
|
{
|
||||||
// Only check if a solid is being drawn, because that can be changed by keyframes
|
// While rendering a solid, if the color was changed since the last frame, recreate the shader
|
||||||
if (Properties.GradientType.BaseValue == GradientType.Solid && _color != Properties.Color.CurrentValue)
|
if (Properties.GradientType.BaseValue == GradientType.Solid && _color != Properties.Color.CurrentValue)
|
||||||
{
|
CreateSolid();
|
||||||
// If the color was changed since the last frame, recreate the shader
|
// While rendering a linear gradient, if the rotation was changed since the last frame, recreate the shader
|
||||||
_color = Properties.Color.CurrentValue;
|
else if (Properties.GradientType.BaseValue == GradientType.LinearGradient && Math.Abs(_linearGradientRotation - Properties.LinearGradientRotation.CurrentValue) > 0.01)
|
||||||
CreateShader();
|
CreateLinearGradient();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Render(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
|
public override void Render(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
|
||||||
@ -78,46 +75,95 @@ namespace Artemis.Plugins.LayerBrushes.Color
|
|||||||
CreateShader();
|
CreateShader();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleShaderChange(object? sender, EventArgs e)
|
private void HandleShaderChange(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
CreateShader();
|
CreateShader();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateShader()
|
private void CreateShader()
|
||||||
{
|
{
|
||||||
var center = new SKPoint(_shaderBounds.MidX, _shaderBounds.MidY);
|
switch (Properties.GradientType.CurrentValue)
|
||||||
var repeat = Properties.GradientRepeat.CurrentValue;
|
|
||||||
var shader = Properties.GradientType.CurrentValue switch
|
|
||||||
{
|
{
|
||||||
GradientType.Solid => SKShader.CreateColor(_color),
|
case GradientType.Solid:
|
||||||
GradientType.LinearGradient => SKShader.CreateLinearGradient(
|
CreateSolid();
|
||||||
new SKPoint(_shaderBounds.Left, _shaderBounds.Top),
|
break;
|
||||||
new SKPoint(_shaderBounds.Right, _shaderBounds.Top),
|
case GradientType.LinearGradient:
|
||||||
Properties.Gradient.BaseValue.GetColorsArray(repeat),
|
CreateLinearGradient();
|
||||||
Properties.Gradient.BaseValue.GetPositionsArray(repeat),
|
break;
|
||||||
Properties.GradientTileMode.CurrentValue),
|
case GradientType.RadialGradient:
|
||||||
GradientType.RadialGradient => SKShader.CreateRadialGradient(
|
CreateRadialGradient();
|
||||||
center,
|
break;
|
||||||
Math.Max(_shaderBounds.Width, _shaderBounds.Height) / 2f,
|
case GradientType.SweepGradient:
|
||||||
Properties.Gradient.BaseValue.GetColorsArray(repeat),
|
CreateSweepGradient();
|
||||||
Properties.Gradient.BaseValue.GetPositionsArray(repeat),
|
break;
|
||||||
Properties.GradientTileMode.CurrentValue),
|
default:
|
||||||
GradientType.SweepGradient => SKShader.CreateSweepGradient(
|
throw new ArgumentOutOfRangeException();
|
||||||
center,
|
}
|
||||||
Properties.Gradient.BaseValue.GetColorsArray(repeat),
|
}
|
||||||
Properties.Gradient.BaseValue.GetPositionsArray(repeat),
|
|
||||||
Properties.GradientTileMode.CurrentValue,
|
|
||||||
0,
|
|
||||||
360),
|
|
||||||
_ => throw new ArgumentOutOfRangeException()
|
|
||||||
};
|
|
||||||
|
|
||||||
var oldShader = _shader;
|
private void UpdatePaint()
|
||||||
var oldPaint = _paint;
|
{
|
||||||
_shader = shader;
|
if (_paint == null)
|
||||||
_paint = new SKPaint {Shader = _shader, FilterQuality = SKFilterQuality.Low};
|
_paint = new SKPaint {Shader = _shader, FilterQuality = SKFilterQuality.Low};
|
||||||
oldShader?.Dispose();
|
else
|
||||||
oldPaint?.Dispose();
|
_paint.Shader = _shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateSolid()
|
||||||
|
{
|
||||||
|
_color = Properties.Color.CurrentValue;
|
||||||
|
|
||||||
|
_shader?.Dispose();
|
||||||
|
_shader = SKShader.CreateColor(_color);
|
||||||
|
UpdatePaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateLinearGradient()
|
||||||
|
{
|
||||||
|
var repeat = Properties.GradientRepeat.CurrentValue;
|
||||||
|
_linearGradientRotation = Properties.LinearGradientRotation.CurrentValue;
|
||||||
|
|
||||||
|
_shader?.Dispose();
|
||||||
|
_shader = SKShader.CreateLinearGradient(
|
||||||
|
new SKPoint(_shaderBounds.Left, _shaderBounds.Top),
|
||||||
|
new SKPoint(_shaderBounds.Right, _shaderBounds.Top),
|
||||||
|
Properties.Gradient.BaseValue.GetColorsArray(repeat),
|
||||||
|
Properties.Gradient.BaseValue.GetPositionsArray(repeat),
|
||||||
|
Properties.GradientTileMode.CurrentValue,
|
||||||
|
SKMatrix.MakeRotationDegrees(_linearGradientRotation, _shaderBounds.Left, _shaderBounds.MidY)
|
||||||
|
);
|
||||||
|
UpdatePaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateRadialGradient()
|
||||||
|
{
|
||||||
|
var repeat = Properties.GradientRepeat.CurrentValue;
|
||||||
|
|
||||||
|
_shader?.Dispose();
|
||||||
|
_shader = SKShader.CreateRadialGradient(
|
||||||
|
new SKPoint(_shaderBounds.MidX, _shaderBounds.MidY),
|
||||||
|
Math.Max(_shaderBounds.Width, _shaderBounds.Height) / 2f,
|
||||||
|
Properties.Gradient.BaseValue.GetColorsArray(repeat),
|
||||||
|
Properties.Gradient.BaseValue.GetPositionsArray(repeat),
|
||||||
|
Properties.GradientTileMode.CurrentValue
|
||||||
|
);
|
||||||
|
UpdatePaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateSweepGradient()
|
||||||
|
{
|
||||||
|
var repeat = Properties.GradientRepeat.CurrentValue;
|
||||||
|
|
||||||
|
_shader?.Dispose();
|
||||||
|
_shader = SKShader.CreateSweepGradient(
|
||||||
|
new SKPoint(_shaderBounds.MidX, _shaderBounds.MidY),
|
||||||
|
Properties.Gradient.BaseValue.GetColorsArray(repeat),
|
||||||
|
Properties.Gradient.BaseValue.GetPositionsArray(repeat),
|
||||||
|
Properties.GradientTileMode.CurrentValue,
|
||||||
|
0,
|
||||||
|
360
|
||||||
|
);
|
||||||
|
UpdatePaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -24,6 +24,9 @@ namespace Artemis.Plugins.LayerBrushes.Color
|
|||||||
[PropertyDescription(DisableKeyframes = true, Description = "How many times to repeat the colors in the selected gradient", MinInputValue = 0, MaxInputValue = 10)]
|
[PropertyDescription(DisableKeyframes = true, Description = "How many times to repeat the colors in the selected gradient", MinInputValue = 0, MaxInputValue = 10)]
|
||||||
public IntLayerProperty GradientRepeat { get; set; }
|
public IntLayerProperty GradientRepeat { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Name = "Rotation", Description = "Change the rotation of the linear gradient without affecting the rotation of the shape", InputAffix = "°")]
|
||||||
|
public FloatLayerProperty LinearGradientRotation { get; set; }
|
||||||
|
|
||||||
protected override void PopulateDefaults()
|
protected override void PopulateDefaults()
|
||||||
{
|
{
|
||||||
GradientType.DefaultValue = LayerBrushes.Color.GradientType.Solid;
|
GradientType.DefaultValue = LayerBrushes.Color.GradientType.Solid;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user