1
0
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:
SpoinkyNL 2020-08-03 22:41:13 +02:00
parent 7b1cefca8b
commit 10c839f8c9
21 changed files with 400 additions and 150 deletions

View File

@ -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);

View File

@ -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);
if (LayerBrush?.BaseProperties != null)
{
foreach (var baseLayerProperty in LayerBrush.BaseProperties.GetAllLayerProperties()) foreach (var baseLayerProperty in LayerBrush.BaseProperties.GetAllLayerProperties())
keyframes.AddRange(baseLayerProperty.BaseKeyframes); 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)

View File

@ -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

View File

@ -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
} }
} }

View File

@ -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;

View File

@ -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; }

View File

@ -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);

View 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
}
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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"

View File

@ -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)

View File

@ -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>

View File

@ -14,67 +14,166 @@
<UserControl.Resources> <UserControl.Resources>
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter" /> <converters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
</UserControl.Resources> </UserControl.Resources>
<Grid>
<materialDesign:Transitioner AutoApplyTransitionOrigins="True" SelectedIndex="{Binding TransitionerIndex}">
<!-- Conditions intro -->
<materialDesign:ColorZone Mode="PrimaryDark" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" VerticalContentAlignment="Stretch">
<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> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Margin="10 5 0 -4"> <TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}" Margin="0 0 0 -4">
Display conditions Display conditions
</TextBlock> </TextBlock>
<Separator Grid.Row="1" Grid.Column="0" Style="{StaticResource MaterialDesignDarkSeparator}" Margin="8 0" /> <Separator Grid.Row="1" Grid.Column="0" Style="{StaticResource MaterialDesignDarkSeparator}" Margin="-2 0" />
<Grid Grid.Row="2" Grid.Column="0"> <Grid Grid.Row="2" Grid.Column="0">
<ScrollViewer Margin="8 0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Background="{StaticResource MaterialDesignCardBackground}">
<ContentControl s:View.Model="{Binding RootGroup}" /> <ContentControl s:View.Model="{Binding RootGroup}" />
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>
<StackPanel Grid.Row="3" Margin="10" HorizontalAlignment="Right" Orientation="Horizontal"> <Grid Grid.Row="3">
<TextBlock VerticalAlignment="Center" Margin="0 0 5 0">On end: </TextBlock> <Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="140" />
<ColumnDefinition Width="*" MinWidth="170" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal"> <!-- Play mode -->
<RadioButton Style="{StaticResource MaterialDesignTabRadioButton}" <StackPanel Grid.Column="0" Orientation="Horizontal">
IsChecked="{Binding RenderProfileElement.AlwaysFinishTimeline}" <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"> Padding="5 0">
<RadioButton.ToolTip> <RadioButton.ToolTip>
<ToolTip Placement="Top" VerticalOffset="-5"> <ToolTip Placement="Center" VerticalOffset="-40">
<StackPanel>
<TextBlock> <TextBlock>
When conditions are no longer met, finish the timelines and then stop displaying. Continue repeating the main segment of the timeline while the condition is met
</TextBlock> </TextBlock>
</StackPanel>
</ToolTip> </ToolTip>
</RadioButton.ToolTip> </RadioButton.ToolTip>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="PlayArrow" Width="20" Height="20" VerticalAlignment="Center" /> <materialDesign:PackIcon Kind="Repeat" VerticalAlignment="Center" />
<TextBlock Margin="5 0 0 0" FontSize="12" VerticalAlignment="Center"> <TextBlock FontSize="12" VerticalAlignment="Center">REPEAT</TextBlock>
WAIT FOR FINISH
</TextBlock>
</StackPanel> </StackPanel>
</RadioButton> </RadioButton>
<RadioButton Style="{StaticResource MaterialDesignTabRadioButton}" <RadioButton Grid.Column="1"
IsChecked="{Binding RenderProfileElement.AlwaysFinishTimeline, Converter={StaticResource InverseBooleanConverter}}" Style="{StaticResource MaterialDesignTabRadioButton}"
Padding="0"> IsChecked="{Binding RenderProfileElement.DisplayContinuously, Converter={StaticResource InverseBooleanConverter}}"
MinWidth="0"
Padding="5 0">
<RadioButton.ToolTip> <RadioButton.ToolTip>
<ToolTip Placement="Top" VerticalOffset="-5"> <ToolTip Placement="Center" VerticalOffset="-40">
<StackPanel>
<TextBlock> <TextBlock>
When conditions are no longer met, stop displaying immediately. Only play the timeline once when the condition is met
</TextBlock> </TextBlock>
</StackPanel>
</ToolTip> </ToolTip>
</RadioButton.ToolTip> </RadioButton.ToolTip>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="SkipNext" Width="20" Height="20" VerticalAlignment="Center" Margin="-5 0 0 0"/> <materialDesign:PackIcon Kind="StopwatchOutline" VerticalAlignment="Center" />
<TextBlock Margin="5 0 0 0" FontSize="12" VerticalAlignment="Center"> <TextBlock FontSize="12" VerticalAlignment="Center">ONCE</TextBlock>
SKIP
</TextBlock>
</StackPanel> </StackPanel>
</RadioButton> </RadioButton>
</StackPanel>
</StackPanel>
</Grid> </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>
</materialDesign:Transitioner>
</UserControl> </UserControl>

View File

@ -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()

View File

@ -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));
} }

View File

@ -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"

View File

@ -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));
} }

View File

@ -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();
break;
case GradientType.LinearGradient:
CreateLinearGradient();
break;
case GradientType.RadialGradient:
CreateRadialGradient();
break;
case GradientType.SweepGradient:
CreateSweepGradient();
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private void UpdatePaint()
{
if (_paint == null)
_paint = new SKPaint {Shader = _shader, FilterQuality = SKFilterQuality.Low};
else
_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.Left, _shaderBounds.Top),
new SKPoint(_shaderBounds.Right, _shaderBounds.Top), new SKPoint(_shaderBounds.Right, _shaderBounds.Top),
Properties.Gradient.BaseValue.GetColorsArray(repeat), Properties.Gradient.BaseValue.GetColorsArray(repeat),
Properties.Gradient.BaseValue.GetPositionsArray(repeat), Properties.Gradient.BaseValue.GetPositionsArray(repeat),
Properties.GradientTileMode.CurrentValue), Properties.GradientTileMode.CurrentValue,
GradientType.RadialGradient => SKShader.CreateRadialGradient( SKMatrix.MakeRotationDegrees(_linearGradientRotation, _shaderBounds.Left, _shaderBounds.MidY)
center, );
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, Math.Max(_shaderBounds.Width, _shaderBounds.Height) / 2f,
Properties.Gradient.BaseValue.GetColorsArray(repeat), Properties.Gradient.BaseValue.GetColorsArray(repeat),
Properties.Gradient.BaseValue.GetPositionsArray(repeat), Properties.Gradient.BaseValue.GetPositionsArray(repeat),
Properties.GradientTileMode.CurrentValue), Properties.GradientTileMode.CurrentValue
GradientType.SweepGradient => SKShader.CreateSweepGradient( );
center, 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.GetColorsArray(repeat),
Properties.Gradient.BaseValue.GetPositionsArray(repeat), Properties.Gradient.BaseValue.GetPositionsArray(repeat),
Properties.GradientTileMode.CurrentValue, Properties.GradientTileMode.CurrentValue,
0, 0,
360), 360
_ => throw new ArgumentOutOfRangeException() );
}; UpdatePaint();
var oldShader = _shader;
var oldPaint = _paint;
_shader = shader;
_paint = new SKPaint {Shader = _shader, FilterQuality = SKFilterQuality.Low};
oldShader?.Dispose();
oldPaint?.Dispose();
} }
} }
} }

View File

@ -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;