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"
MaxLength="9"
Margin="0"
HorizontalAlignment="Stretch">
HorizontalAlignment="Stretch"
FontFamily="Consolas"
CharacterCasing="Upper">
<materialDesign:TextFieldAssist.CharacterCounterStyle>
<Style TargetType="TextBlock" />
</materialDesign:TextFieldAssist.CharacterCounterStyle>
@ -82,7 +84,7 @@
<Border Width="15"
Height="15"
CornerRadius="15"
Margin="0 0 8 0"
Margin="0 0 2 0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
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);
NotifyOfPropertyChange(nameof(Offset));
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;
NotifyOfPropertyChange(nameof(Offset));
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}"
FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto"
Width="400"
Height="400"
Height="450"
d:DesignHeight="450"
d:DesignWidth="800"
d:DesignWidth="400"
d:DataContext="{d:DesignInstance local:GradientEditorViewModel}">
<UserControl.Resources>
<shared:ColorGradientToGradientStopsConverter x:Key="ColorGradientToGradientStopsConverter" />
@ -40,7 +40,7 @@
</VisualBrush>
</UserControl.Resources>
<UserControl.InputBindings>
<KeyBinding Key="Delete" Command="{s:Action RemoveColorStop}" CommandParameter="{Binding SelectedColorStopViewModel}"/>
<KeyBinding Key="Delete" Command="{s:Action RemoveColorStop}" CommandParameter="{Binding SelectedColorStopViewModel}" />
</UserControl.InputBindings>
<Grid Margin="16">
<Grid.RowDefinitions>
@ -60,8 +60,83 @@
</StackPanel>
<Separator Grid.Row="1" Margin="0 5" />
<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}">
<Rectangle x:Name="Preview" Height="40" shared:SizeObserver.Observe="True" shared:SizeObserver.ObservedWidth="{Binding PreviewWidth, Mode=OneWayToSource}">
<Rectangle.Fill>
@ -90,8 +165,7 @@
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Margin="0 5">Selected stop</TextBlock>
<TextBlock Margin="0 6 0 0" FontWeight="Bold">Selected stop:</TextBlock>
<Grid Margin="0 5">
<Grid.ColumnDefinitions>
@ -100,7 +174,7 @@
</Grid.ColumnDefinitions>
<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
x:Name="CurrentColor"
Width="85"
@ -109,8 +183,8 @@
IsEnabled="{Binding HasSelectedColorStopViewModel}" />
<Label HorizontalAlignment="Center">Location:</Label>
<TextBox Width="40"
Text="{Binding SelectedColorStopViewModel.OffsetPercent}"
<TextBox Width="30"
Text="{Binding SelectedColorStopViewModel.OffsetPercent, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding HasSelectedColorStopViewModel}"
materialDesign:HintAssist.Hint="0"
Margin="5 0 0 0" />
@ -122,8 +196,15 @@
Margin="8,0,0,0"
IsEnabled="{Binding HasSelectedColorStopViewModel}"
Command="{s:Action RemoveColorStop}"
CommandParameter="{Binding SelectedColorStopViewModel}">
DELETE
CommandParameter="{Binding SelectedColorStopViewModel}"
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>
</Grid>
</StackPanel>

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
@ -27,19 +28,9 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
PropertyChanged += UpdateColorStopViewModels;
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 ColorStopViewModel? SelectedColorStopViewModel
@ -53,6 +44,9 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
}
public bool HasSelectedColorStopViewModel => SelectedColorStopViewModel != null;
public bool HasMoreThanOneStop => ColorStopViewModels.Count > 1;
private bool popupOpen = false;
public bool ClearGradientPopupOpen => popupOpen;
public ColorGradient ColorGradient { get; }
@ -62,11 +56,20 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
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)
{
Canvas? child = VisualTreeUtilities.FindChild<Canvas>((DependencyObject) sender, null);
@ -79,19 +82,118 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
ColorStopViewModels.Insert(index, viewModel);
SelectColorStop(viewModel);
NotifyOfPropertyChange(nameof(HasMoreThanOneStop));
}
public void RemoveColorStop(ColorStopViewModel colorStopViewModel)
{
if (colorStopViewModel == null)
return;
ColorStopViewModels.Remove(colorStopViewModel);
ColorGradient.Remove(colorStopViewModel.ColorStop);
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)
{
Canvas? parent = VisualTreeUtilities.FindParent<Canvas>((DependencyObject) sender, null);
@ -127,10 +229,15 @@ namespace Artemis.UI.Shared.Screens.GradientEditor
foreach (ColorGradientStop colorStop in ColorGradient)
ColorStopViewModels.Add(new ColorStopViewModel(this, colorStop));
}
private void ColorGradientOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
NotifyOfPropertyChange(nameof(ColorGradient));
}
private void ColorStopViewModelsOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
NotifyOfPropertyChange(nameof(HasMoreThanOneStop));
}
}
}