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

Merge pull request #586 from AlpacaFur/development

Add Gradient Tools
This commit is contained in:
Robert Beekman 2021-05-03 11:46:07 +02:00 committed by GitHub
commit de6edaf565
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 237 additions and 32 deletions

View File

@ -73,7 +73,9 @@
MinWidth="95" MinWidth="95"
MaxLength="9" MaxLength="9"
Margin="0" Margin="0"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch"
FontFamily="Consolas"
CharacterCasing="Upper">
<materialDesign:TextFieldAssist.CharacterCounterStyle> <materialDesign:TextFieldAssist.CharacterCounterStyle>
<Style TargetType="TextBlock" /> <Style TargetType="TextBlock" />
</materialDesign:TextFieldAssist.CharacterCounterStyle> </materialDesign:TextFieldAssist.CharacterCounterStyle>
@ -82,7 +84,7 @@
<Border Width="15" <Border Width="15"
Height="15" Height="15"
CornerRadius="15" CornerRadius="15"
Margin="0 0 8 0" Margin="0 0 2 0"
VerticalAlignment="Center" VerticalAlignment="Center"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Background="{StaticResource Checkerboard}"> Background="{StaticResource Checkerboard}">

View File

@ -32,6 +32,7 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
ColorStop.Position = (float) Math.Round(value / _gradientEditorViewModel.PreviewWidth, 3, MidpointRounding.AwayFromZero); ColorStop.Position = (float) Math.Round(value / _gradientEditorViewModel.PreviewWidth, 3, MidpointRounding.AwayFromZero);
NotifyOfPropertyChange(nameof(Offset)); NotifyOfPropertyChange(nameof(Offset));
NotifyOfPropertyChange(nameof(OffsetPercent)); NotifyOfPropertyChange(nameof(OffsetPercent));
NotifyOfPropertyChange(nameof(OffsetFloat));
} }
} }
@ -43,6 +44,20 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
ColorStop.Position = Math.Min(100, Math.Max(0, value)) / 100f; ColorStop.Position = Math.Min(100, Math.Max(0, value)) / 100f;
NotifyOfPropertyChange(nameof(Offset)); NotifyOfPropertyChange(nameof(Offset));
NotifyOfPropertyChange(nameof(OffsetPercent)); NotifyOfPropertyChange(nameof(OffsetPercent));
NotifyOfPropertyChange(nameof(OffsetFloat));
}
}
// Functionally similar to Offset Percent, but doesn't round on get in order to prevent inconsistencies (and is 0 to 1)
public float OffsetFloat
{
get => ColorStop.Position;
set
{
ColorStop.Position = Math.Min(1, Math.Max(0, value));
NotifyOfPropertyChange(nameof(Offset));
NotifyOfPropertyChange(nameof(OffsetPercent));
NotifyOfPropertyChange(nameof(OffsetFloat));
} }
} }

View File

@ -12,9 +12,9 @@
Background="{DynamicResource MaterialDesignPaper}" Background="{DynamicResource MaterialDesignPaper}"
FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto" FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto"
Width="400" Width="400"
Height="400" Height="450"
d:DesignHeight="450" d:DesignHeight="450"
d:DesignWidth="800" d:DesignWidth="400"
d:DataContext="{d:DesignInstance local:GradientEditorViewModel}"> d:DataContext="{d:DesignInstance local:GradientEditorViewModel}">
<UserControl.Resources> <UserControl.Resources>
<shared:ColorGradientToGradientStopsConverter x:Key="ColorGradientToGradientStopsConverter" /> <shared:ColorGradientToGradientStopsConverter x:Key="ColorGradientToGradientStopsConverter" />
@ -60,8 +60,83 @@
</StackPanel> </StackPanel>
<Separator Grid.Row="1" Margin="0 5" /> <Separator Grid.Row="1" Margin="0 5" />
<StackPanel Grid.Row="2" Margin="0 5" ClipToBounds="False"> <StackPanel Grid.Row="2" Margin="0 5" ClipToBounds="False">
<TextBlock Margin="0 5">Gradient</TextBlock> <Grid Margin="0 0 0 5">
<TextBlock Margin="0 5" VerticalAlignment="Center">Gradient</TextBlock>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
<Button Style="{StaticResource MaterialDesignFlatButton}"
Margin="0 0 5 0"
Padding="0"
Command="{s:Action SpreadColorStops}"
ToolTip="Spread Stops"
IsEnabled="{Binding HasMoreThanOneStop}"
HorizontalAlignment="Left"
Height="25" Width="25">
<materialDesign:PackIcon Kind="ArrowLeftRight" />
</Button>
<Button Style="{StaticResource MaterialDesignFlatButton}"
Margin="0 0 5 0"
Padding="0"
Command="{s:Action ToggleSeam}"
ToolTip="Toggle Seamless"
IsEnabled="{Binding HasMoreThanOneStop}"
HorizontalAlignment="Left"
Height="25" Width="25">
<materialDesign:PackIcon Kind="SineWave" />
</Button>
<Button Style="{StaticResource MaterialDesignFlatButton}"
Margin="0 0 5 0"
Padding="0"
Command="{s:Action FlipColorStops}"
ToolTip="Flip Stops"
IsEnabled="{Binding HasMoreThanOneStop}"
HorizontalAlignment="Left"
Height="25" Width="25">
<materialDesign:PackIcon Kind="FlipHorizontal" />
</Button>
<Button Style="{StaticResource MaterialDesignFlatButton}"
Margin="0 0 5 0"
Padding="0"
Command="{s:Action RotateColorStops}"
ToolTip="Rotate Stops (shift to invert)"
IsEnabled="{Binding HasMoreThanOneStop}"
HorizontalAlignment="Left"
Height="25" Width="25">
<materialDesign:PackIcon Kind="AxisZRotateCounterclockwise" />
</Button>
<Button Style="{StaticResource MaterialDesignFlatButton}"
Padding="0"
Command="{s:Action ShowClearGradientPopup}"
ToolTip="Clear All Color Stops"
IsEnabled="{Binding HasMoreThanOneStop}"
HorizontalAlignment="Left"
Height="25" Width="25"
x:Name="ClearGradientButton">
<StackPanel>
<materialDesign:PackIcon Kind="DeleteSweep" />
</StackPanel>
</Button>
<Popup
AllowsTransparency="True"
Placement="Right"
VerticalOffset="-25"
HorizontalOffset="-5"
CustomPopupPlacementCallback="{x:Static materialDesign:CustomPopupPlacementCallbackHelper.LargePopupCallback}"
PlacementTarget="{Binding ElementName=ClearGradientButton}"
StaysOpen="False"
PopupAnimation="Fade"
IsOpen="{Binding ClearGradientPopupOpen, Mode=OneWay}">
<materialDesign:Card Margin="30" Padding="12" materialDesign:ShadowAssist.ShadowDepth="Depth4">
<StackPanel HorizontalAlignment="Center">
<TextBlock Style="{StaticResource MaterialDesignSubtitle2TextBlock}" TextAlignment="Center" Margin="0 0 0 10">Clear Gradient?</TextBlock>
<StackPanel Orientation="Horizontal">
<Button Command="{s:Action HideClearGradientPopup}" Style="{StaticResource MaterialDesignOutlinedButton}" Margin="0 0 10 0">No</Button>
<Button Command="{s:Action ClearGradientAndHide}">Yes</Button>
</StackPanel>
</StackPanel>
</materialDesign:Card>
</Popup>
</StackPanel>
</Grid>
<Border Background="{StaticResource Checkerboard}"> <Border Background="{StaticResource Checkerboard}">
<Rectangle x:Name="Preview" Height="40" shared:SizeObserver.Observe="True" shared:SizeObserver.ObservedWidth="{Binding PreviewWidth, Mode=OneWayToSource}"> <Rectangle x:Name="Preview" Height="40" shared:SizeObserver.Observe="True" shared:SizeObserver.ObservedWidth="{Binding PreviewWidth, Mode=OneWayToSource}">
<Rectangle.Fill> <Rectangle.Fill>
@ -90,8 +165,7 @@
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
<TextBlock Margin="0 6 0 0" FontWeight="Bold">Selected stop:</TextBlock>
<TextBlock Margin="0 5">Selected stop</TextBlock>
<Grid Margin="0 5"> <Grid Margin="0 5">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@ -100,7 +174,7 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Orientation="Horizontal"> <StackPanel Grid.Column="0" Orientation="Horizontal">
<Label HorizontalAlignment="Left" Margin="-5,0,0,0">Color:</Label> <Label HorizontalAlignment="Left" Margin="-5,1,0,0">Color:</Label>
<shared:ColorPicker <shared:ColorPicker
x:Name="CurrentColor" x:Name="CurrentColor"
Width="85" Width="85"
@ -109,8 +183,8 @@
IsEnabled="{Binding HasSelectedColorStopViewModel}" /> IsEnabled="{Binding HasSelectedColorStopViewModel}" />
<Label HorizontalAlignment="Center">Location:</Label> <Label HorizontalAlignment="Center">Location:</Label>
<TextBox Width="40" <TextBox Width="30"
Text="{Binding SelectedColorStopViewModel.OffsetPercent}" Text="{Binding SelectedColorStopViewModel.OffsetPercent, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding HasSelectedColorStopViewModel}" IsEnabled="{Binding HasSelectedColorStopViewModel}"
materialDesign:HintAssist.Hint="0" materialDesign:HintAssist.Hint="0"
Margin="5 0 0 0" /> Margin="5 0 0 0" />
@ -122,8 +196,15 @@
Margin="8,0,0,0" Margin="8,0,0,0"
IsEnabled="{Binding HasSelectedColorStopViewModel}" IsEnabled="{Binding HasSelectedColorStopViewModel}"
Command="{s:Action RemoveColorStop}" Command="{s:Action RemoveColorStop}"
CommandParameter="{Binding SelectedColorStopViewModel}"> CommandParameter="{Binding SelectedColorStopViewModel}"
DELETE ToolTip="Delete Selected Stop">
<Button.Resources>
<SolidColorBrush x:Key="PrimaryHueMidBrush" Color="#c94848" />
</Button.Resources>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Delete" />
<TextBlock Margin="4 0 0 0">Delete</TextBlock>
</StackPanel>
</Button> </Button>
</Grid> </Grid>
</StackPanel> </StackPanel>

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
@ -27,19 +28,9 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
PropertyChanged += UpdateColorStopViewModels; PropertyChanged += UpdateColorStopViewModels;
ColorGradient.CollectionChanged += ColorGradientOnCollectionChanged; ColorGradient.CollectionChanged += ColorGradientOnCollectionChanged;
ColorStopViewModels.CollectionChanged += ColorStopViewModelsOnCollectionChanged;
} }
#region Overrides of DialogViewModelBase
/// <inheritdoc />
public override void OnDialogClosed(object sender, DialogClosingEventArgs e)
{
ColorGradient.CollectionChanged -= ColorGradientOnCollectionChanged;
base.OnDialogClosed(sender, e);
}
#endregion
public BindableCollection<ColorStopViewModel> ColorStopViewModels { get; } public BindableCollection<ColorStopViewModel> ColorStopViewModels { get; }
public ColorStopViewModel? SelectedColorStopViewModel public ColorStopViewModel? SelectedColorStopViewModel
@ -53,6 +44,9 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
} }
public bool HasSelectedColorStopViewModel => SelectedColorStopViewModel != null; public bool HasSelectedColorStopViewModel => SelectedColorStopViewModel != null;
public bool HasMoreThanOneStop => ColorStopViewModels.Count > 1;
private bool popupOpen = false;
public bool ClearGradientPopupOpen => popupOpen;
public ColorGradient ColorGradient { get; } public ColorGradient ColorGradient { get; }
@ -62,11 +56,20 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
set => SetAndNotify(ref _previewWidth, value); set => SetAndNotify(ref _previewWidth, value);
} }
public ColorGradient Stops public ColorGradient Stops => ColorGradient;
#region Overrides of DialogViewModelBase
/// <inheritdoc />
public override void OnDialogClosed(object sender, DialogClosingEventArgs e)
{ {
get => ColorGradient; ColorGradient.CollectionChanged -= ColorGradientOnCollectionChanged;
ColorStopViewModels.CollectionChanged -= ColorStopViewModelsOnCollectionChanged;
base.OnDialogClosed(sender, e);
} }
#endregion
public void AddColorStop(object sender, MouseEventArgs e) public void AddColorStop(object sender, MouseEventArgs e)
{ {
Canvas? child = VisualTreeUtilities.FindChild<Canvas>((DependencyObject) sender, null); Canvas? child = VisualTreeUtilities.FindChild<Canvas>((DependencyObject) sender, null);
@ -79,19 +82,118 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
ColorStopViewModels.Insert(index, viewModel); ColorStopViewModels.Insert(index, viewModel);
SelectColorStop(viewModel); SelectColorStop(viewModel);
NotifyOfPropertyChange(nameof(HasMoreThanOneStop));
} }
public void RemoveColorStop(ColorStopViewModel colorStopViewModel) public void RemoveColorStop(ColorStopViewModel colorStopViewModel)
{ {
if (colorStopViewModel == null)
return;
ColorStopViewModels.Remove(colorStopViewModel); ColorStopViewModels.Remove(colorStopViewModel);
ColorGradient.Remove(colorStopViewModel.ColorStop); ColorGradient.Remove(colorStopViewModel.ColorStop);
SelectColorStop(null); SelectColorStop(null);
NotifyOfPropertyChange(nameof(HasMoreThanOneStop));
} }
#region Gradient Tools
public void SpreadColorStops()
{
List<ColorStopViewModel> stops = ColorStopViewModels.OrderBy(x => x.OffsetFloat).ToList();
int index = 0;
foreach (ColorStopViewModel stop in stops)
{
stop.OffsetFloat = index / ((float) stops.Count - 1);
index++;
}
}
public void RotateColorStops()
{
List<ColorStopViewModel> stops;
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
stops = ColorStopViewModels.OrderBy(x => x.OffsetFloat).ToList();
else
stops = ColorStopViewModels.OrderByDescending(x => x.OffsetFloat).ToList();
float lastStopPosition = stops.Last().OffsetFloat;
foreach (ColorStopViewModel stop in stops)
{
float tempStop = stop.OffsetFloat;
stop.OffsetFloat = lastStopPosition;
lastStopPosition = tempStop;
}
}
public void FlipColorStops()
{
foreach (ColorStopViewModel stop in ColorStopViewModels) stop.OffsetFloat = 1 - stop.OffsetFloat;
}
public void ToggleSeam()
{
if (ColorGradient.IsSeamless())
{
// Remove the last stop
ColorStopViewModel? stopToRemove = ColorStopViewModels.OrderBy(x => x.OffsetFloat).Last();
if (stopToRemove == SelectedColorStopViewModel) SelectColorStop(null);
ColorStopViewModels.Remove(stopToRemove);
ColorGradient.Remove(stopToRemove.ColorStop);
// Uncompress the stops if there is still more than one
List<ColorStopViewModel> stops = ColorStopViewModels.OrderBy(x => x.OffsetFloat).ToList();
if (stops.Count >= 2)
{
float multiplier = stops.Count/(stops.Count - 1f);
foreach (ColorStopViewModel stop in stops)
stop.OffsetFloat = Math.Min(stop.OffsetFloat * multiplier, 100f);
}
}
else
{
// Compress existing stops to the left
List<ColorStopViewModel> stops = ColorStopViewModels.OrderBy(x => x.OffsetFloat).ToList();
float multiplier = (stops.Count - 1f)/stops.Count;
foreach (ColorStopViewModel stop in stops)
stop.OffsetFloat *= multiplier;
// Add a stop to the end that is the same color as the first stop
ColorGradientStop newStop = new(ColorGradient.First().Color, 1f);
ColorGradient.Add(newStop);
int index = ColorGradient.IndexOf(newStop);
ColorStopViewModel viewModel = new(this, newStop);
ColorStopViewModels.Insert(index, viewModel);
}
}
public void ShowClearGradientPopup()
{
popupOpen = true;
NotifyOfPropertyChange(nameof(ClearGradientPopupOpen));
}
public void HideClearGradientPopup()
{
popupOpen = false;
NotifyOfPropertyChange(nameof(ClearGradientPopupOpen));
}
public void ClearGradientAndHide()
{
ClearGradient();
HideClearGradientPopup();
}
public void ClearGradient()
{
ColorGradient.Clear();
ColorStopViewModels.Clear();
}
#endregion
public Point GetPositionInPreview(object sender, MouseEventArgs e) public Point GetPositionInPreview(object sender, MouseEventArgs e)
{ {
Canvas? parent = VisualTreeUtilities.FindParent<Canvas>((DependencyObject) sender, null); Canvas? parent = VisualTreeUtilities.FindParent<Canvas>((DependencyObject) sender, null);
@ -132,5 +234,10 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
{ {
NotifyOfPropertyChange(nameof(ColorGradient)); NotifyOfPropertyChange(nameof(ColorGradient));
} }
private void ColorStopViewModelsOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
NotifyOfPropertyChange(nameof(HasMoreThanOneStop));
}
} }
} }