Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 53 additions & 13 deletions src/ColorPicker.AvaloniaUI/DualPickerControlBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Avalonia.Media;
using Avalonia.Reactive;
using ColorPicker.Models;
using System.ComponentModel;

namespace ColorPicker;

Expand Down Expand Up @@ -47,6 +48,8 @@ static DualPickerControlBase()
new AnonymousObserver<AvaloniaPropertyChangedEventArgs<ColorState>>(OnHintColorStatePropertyChange));
HintColorProperty.Changed.Subscribe(
new AnonymousObserver<AvaloniaPropertyChangedEventArgs<Color>>(OnHintColorPropertyChanged));

IsEffectivelyEnabledProperty.Changed.Subscribe(new AnonymousObserver<AvaloniaPropertyChangedEventArgs<bool>>(OnIsEffectivelyEnabledChanged));
}

protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
Expand All @@ -64,34 +67,62 @@ public DualPickerControlBase()
hintColorDecorator = new HintColorDecorator(this);

SecondColor = new NotifyableColor(secondColorDecorator);
SecondColor.PropertyChanged += (sender, args) =>
SecondColor.PropertyChanged += SecondColor_PropertyChanged;

HintNotifyableColor = new NotifyableColor(hintColorDecorator);
HintNotifyableColor.PropertyChanged += HintNotifyableColor_PropertyChanged;
}

private void SecondColor_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!ignoreSecondaryColorChange)
{
if (!ignoreSecondaryColorChange)
{
ignoreSecondaryColorPropertyChange = true;
ignoreSecondaryColorPropertyChange = true;
if (IsEffectivelyEnabled)
SecondaryColor = Avalonia.Media.Color.FromArgb(
(byte)Math.Round(SecondColor.A),
(byte)Math.Round(SecondColor.RGB_R),
(byte)Math.Round(SecondColor.RGB_G),
(byte)Math.Round(SecondColor.RGB_B));
ignoreSecondaryColorPropertyChange = false;
else
{
var grayColor = SecondColor.RGB_R * 0.21
+ SecondColor.RGB_G * 0.72
+ SecondColor.RGB_B * 0.07;
SecondaryColor = Avalonia.Media.Color.FromArgb(
(byte)Math.Round(SecondColor.A),
(byte)grayColor,
(byte)grayColor,
(byte)grayColor);
}
};
ignoreSecondaryColorPropertyChange = false;
}
}

HintNotifyableColor = new NotifyableColor(hintColorDecorator);
HintNotifyableColor.PropertyChanged += (sender, args) =>
private void HintNotifyableColor_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!ignoreHintNotifyableColorChange)
{
if (!ignoreHintNotifyableColorChange)
{
ignoreHintColorPropertyChange = true;
ignoreHintColorPropertyChange = true;
if (IsEffectivelyEnabled)
HintColor = Avalonia.Media.Color.FromArgb(
(byte)Math.Round(HintNotifyableColor.A),
(byte)Math.Round(HintNotifyableColor.RGB_R),
(byte)Math.Round(HintNotifyableColor.RGB_G),
(byte)Math.Round(HintNotifyableColor.RGB_B));
ignoreHintColorPropertyChange = false;
else
{
var grayColor = HintNotifyableColor.RGB_R * 0.21
+ HintNotifyableColor.RGB_G * 0.72
+ HintNotifyableColor.RGB_B * 0.07;
HintColor = Avalonia.Media.Color.FromArgb(
(byte)Math.Round(HintNotifyableColor.A),
(byte)grayColor,
(byte)grayColor,
(byte)grayColor);
}
};
ignoreHintColorPropertyChange = false;
}
}

public bool UseHintColor
Expand Down Expand Up @@ -177,4 +208,13 @@ private static void OnSecondaryColorPropertyChange(AvaloniaPropertyChangedEventA
sender.SecondColor.RGB_B = newValue.B;
sender.ignoreSecondaryColorChange = false;
}

private static void OnIsEffectivelyEnabledChanged(AvaloniaPropertyChangedEventArgs<bool> args)
{
if (args.Sender is DualPickerControlBase control)
{
control.SecondColor_PropertyChanged(control, new PropertyChangedEventArgs(nameof(SecondColor)));
control.HintNotifyableColor_PropertyChanged(control, new PropertyChangedEventArgs(nameof(HintNotifyableColor)));
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 39 additions & 7 deletions src/ColorPicker.AvaloniaUI/PickerControlBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ static PickerControlBase()
new AnonymousObserver<AvaloniaPropertyChangedEventArgs<ColorState>>(OnColorStatePropertyChange));
SelectedColorProperty.Changed.Subscribe(
new AnonymousObserver<AvaloniaPropertyChangedEventArgs<Color>>(OnSelectedColorPropertyChange));

IsEffectivelyEnabledProperty.Changed.Subscribe(new AnonymousObserver<AvaloniaPropertyChangedEventArgs<bool>>(e =>
{
if (e.Sender is PickerControlBase sender)
{
var color = Avalonia.Media.Color.FromArgb(
(byte)Math.Round(sender.Color.A),
(byte)Math.Round(sender.Color.RGB_R),
(byte)Math.Round(sender.Color.RGB_G),
(byte)Math.Round(sender.Color.RGB_B));
sender.updateColorAction(sender, new ColorRoutedEventArgs(ColorChangedEvent, color));
}
}));
}

public PickerControlBase()
Expand All @@ -54,16 +67,35 @@ public PickerControlBase()
previousColor = newColor;
}
};
ColorChanged += (sender, newColor) =>



ColorChanged += (sender, args) => updateColorAction(sender, args);

}

private Action<object, RoutedEventArgs> updateColorAction => new Action<object, RoutedEventArgs>
((sender, newColor) =>
{
if (!ignoreColorChange)
{
if (!ignoreColorChange)
{
ignoreColorPropertyChange = true;
ignoreColorPropertyChange = true;
if (IsEffectivelyEnabled)
SelectedColor = ((ColorRoutedEventArgs)newColor).Color;
ignoreColorPropertyChange = false;
else
{
var grayColor = ((ColorRoutedEventArgs)newColor).Color.R * 0.21
+ ((ColorRoutedEventArgs)newColor).Color.G * 0.72
+ ((ColorRoutedEventArgs)newColor).Color.B * 0.07;
SelectedColor = Avalonia.Media.Color.FromArgb(
((ColorRoutedEventArgs)newColor).Color.A,
(byte)grayColor,
(byte)grayColor,
(byte)grayColor);
}
};
}
ignoreColorPropertyChange = false;
}
});

public NotifyableColor Color
{
Expand Down
42 changes: 31 additions & 11 deletions src/ColorPicker.AvaloniaUI/SquareSlider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ static SquareSlider()
HueProperty.Changed.Subscribe(new AnonymousObserver<AvaloniaPropertyChangedEventArgs<double>>(OnHueChanged));
PickerTypeProperty.Changed.Subscribe(
new AnonymousObserver<AvaloniaPropertyChangedEventArgs<PickerType>>(OnColorSpaceChanged));
IsEffectivelyEnabledProperty.Changed.Subscribe(new AnonymousObserver<AvaloniaPropertyChangedEventArgs<bool>>(OnIsEffectivelyEnabledChanged));
}

public double RangeY
Expand Down Expand Up @@ -127,7 +128,7 @@ protected override void OnPointerPressed(PointerPressedEventArgs e)
base.OnPointerPressed(e);
e.Pointer.Capture(this);
UpdatePos(e.GetPosition(this));

e.Handled = true;
}

Expand All @@ -143,7 +144,7 @@ protected override void OnPointerMoved(PointerEventArgs e)
if (Equals(e.Pointer.Captured, this))
{
UpdatePos(e.GetPosition(this));

e.Handled = true;
}
}
Expand All @@ -155,15 +156,15 @@ private void RecalculateGradient()
var hue = Hue;
var pixels = new byte[w * h * 3];
for (var j = 0; j < h; j++)
for (var i = 0; i < w; i++)
{
var rgbtuple = colorSpaceConversionMethod(hue, i / (double)(w - 1), (h - 1 - j) / (double)(h - 1));
double r = rgbtuple.Item1, g = rgbtuple.Item2, b = rgbtuple.Item3;
var pos = (j * h + i) * 3;
pixels[pos] = (byte)(r * 255);
pixels[pos + 1] = (byte)(g * 255);
pixels[pos + 2] = (byte)(b * 255);
}
for (var i = 0; i < w; i++)
{
var rgbtuple = colorSpaceConversionMethod(hue, i / (double)(w - 1), (h - 1 - j) / (double)(h - 1));
double r = rgbtuple.Item1, g = rgbtuple.Item2, b = rgbtuple.Item3;
var pos = (j * h + i) * 3;
pixels[pos] = (byte)(r * 255);
pixels[pos + 1] = (byte)(g * 255);
pixels[pos + 2] = (byte)(b * 255);
}

using (var framebuffer = GradientBitmap.Lock())
{
Expand Down Expand Up @@ -217,6 +218,25 @@ private static void OnHueChanged(AvaloniaPropertyChangedEventArgs<double> args)
((SquareSlider)args.Sender).RecalculateGradient();
}

private static void OnIsEffectivelyEnabledChanged(AvaloniaPropertyChangedEventArgs<bool> args)
{
if (args.Sender is not SquareSlider sqs)
return;

if (args.NewValue.Value)
if (sqs.PickerType == PickerType.HSV)
sqs.colorSpaceConversionMethod = ColorSpaceHelper.HsvToRgb;
else
sqs.colorSpaceConversionMethod = ColorSpaceHelper.HslToRgb;
else
if (sqs.PickerType == PickerType.HSV)
sqs.colorSpaceConversionMethod = ColorSpaceHelper.HsvToGray;
else
sqs.colorSpaceConversionMethod = ColorSpaceHelper.HslToGray;

sqs.RecalculateGradient();
}

private void UpdatePos(Point pos)
{
HeadX = MathHelper.Clamp(pos.X / Bounds.Width, 0, 1) * RangeX;
Expand Down
14 changes: 12 additions & 2 deletions src/ColorPicker.AvaloniaUI/Templates/HueSlider.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:colorPicker="clr-namespace:ColorPicker">
<ControlTheme TargetType="colorPicker:HueSlider" x:Key="{x:Type colorPicker:HueSlider}">
<ControlTheme.Resources>
<Bitmap x:Key="masterImage">avares://ColorPicker.AvaloniaUI/Images/CircularHueGradient.png</Bitmap>
<Bitmap x:Key="grayscaleImage">avares://ColorPicker.AvaloniaUI/Images/CircularHueGradientGray.png</Bitmap>
</ControlTheme.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="colorPicker:HueSlider">
<Viewbox>
<Grid>
<Image Source="/Images/CircularHueGradient.png" Stretch="Fill"
<Image Stretch="Fill"
Name="HueImage"
Source="{StaticResource masterImage}"
IsHitTestVisible="False">
<Image.OpacityMask>
<RadialGradientBrush GradientOrigin="50%,50%" Center="50%, 50%" Radius="0.5">
Expand Down Expand Up @@ -59,5 +65,9 @@
</ControlTemplate>
</Setter.Value>
</Setter>
</ControlTheme>

<Style Selector="^:disabled /template/ Image#HueImage">
<Setter Property="Source" Value="{StaticResource grayscaleImage}" />
</Style>
</ControlTheme>
</ResourceDictionary>
23 changes: 16 additions & 7 deletions src/ColorPicker.AvaloniaUI/UIExtensions/HslColorSlider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,27 +73,36 @@ private Color GetColorForSelectedArgb(int value)
{
case "H":
{
var rgbtuple = ColorSpaceHelper.HslToRgb(value, 1.0, 0.5);
var rgbtuple = IsEffectivelyEnabled ? ColorSpaceHelper.HslToRgb(value, 1.0, 0.5)
: ColorSpaceHelper.HslToGray(value, 1.0, 0.5);
double r = rgbtuple.Item1, g = rgbtuple.Item2, b = rgbtuple.Item3;
return Color.FromArgb(255, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255));
}
case "S":
{
var rgbtuple =
ColorSpaceHelper.HslToRgb(CurrentColorState.HSL_H, value / 255.0, CurrentColorState.HSL_L);
var rgbtuple = IsEffectivelyEnabled ? ColorSpaceHelper.HslToRgb(CurrentColorState.HSL_H, value / 255.0,
CurrentColorState.HSL_L)
: ColorSpaceHelper.HslToGray(CurrentColorState.HSL_H, value / 255.0,
CurrentColorState.HSL_L);
double r = rgbtuple.Item1, g = rgbtuple.Item2, b = rgbtuple.Item3;
return Color.FromArgb(255, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255));
}
case "L":
{
var rgbtuple =
ColorSpaceHelper.HslToRgb(CurrentColorState.HSL_H, CurrentColorState.HSL_S, value / 255.0);
var rgbtuple = IsEffectivelyEnabled ? ColorSpaceHelper.HslToRgb(CurrentColorState.HSL_H, CurrentColorState.HSL_S,
value / 255.0)
: ColorSpaceHelper.HslToGray(CurrentColorState.HSL_H, CurrentColorState.HSL_S,
value / 255.0);
double r = rgbtuple.Item1, g = rgbtuple.Item2, b = rgbtuple.Item3;
return Color.FromArgb(255, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255));
}
default:
return Color.FromArgb(255, (byte)(CurrentColorState.RGB_R * 255), (byte)(CurrentColorState.RGB_G * 255),
(byte)(CurrentColorState.RGB_B * 255));
var rgbtupleDef = IsEffectivelyEnabled ? new System.Tuple<double, double, double>(CurrentColorState.RGB_R,
CurrentColorState.RGB_G, CurrentColorState.RGB_B)
: ColorSpaceHelper.HslToGray(CurrentColorState.HSL_H,
CurrentColorState.HSL_S, CurrentColorState.HSL_L);
double rDef = rgbtupleDef.Item1, gDef = rgbtupleDef.Item2, bDef = rgbtupleDef.Item3;
return Color.FromArgb(255, (byte)(rDef * 255), (byte)(gDef * 255), (byte)(bDef * 255));
}
}
}
24 changes: 17 additions & 7 deletions src/ColorPicker.AvaloniaUI/UIExtensions/HsvColorSlider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,27 +58,37 @@ private Color GetColorForSelectedArgb(int value)
{
case "H":
{
var rgbtuple = ColorSpaceHelper.HsvToRgb(value, 1.0, 1.0);
var rgbtuple = IsEffectivelyEnabled ? ColorSpaceHelper.HsvToRgb(value, 1.0, 1.0)
: ColorSpaceHelper.HsvToGray(value, 1.0, 1.0);
double r = rgbtuple.Item1, g = rgbtuple.Item2, b = rgbtuple.Item3;
return Color.FromArgb(255, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255));
}
case "S":
{
var rgbtuple =
ColorSpaceHelper.HsvToRgb(CurrentColorState.HSV_H, value / 255.0, CurrentColorState.HSV_V);
var rgbtuple = IsEffectivelyEnabled ? ColorSpaceHelper.HsvToRgb(CurrentColorState.HSV_H, value / 255.0,
CurrentColorState.HSV_V)
: ColorSpaceHelper.HsvToGray(CurrentColorState.HSV_H, value / 255.0,
CurrentColorState.HSV_V);
double r = rgbtuple.Item1, g = rgbtuple.Item2, b = rgbtuple.Item3;
return Color.FromArgb(255, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255));
}
case "V":
{
var rgbtuple =
ColorSpaceHelper.HsvToRgb(CurrentColorState.HSV_H, CurrentColorState.HSV_S, value / 255.0);
var rgbtuple = IsEffectivelyEnabled ? ColorSpaceHelper.HsvToRgb(CurrentColorState.HSV_H, CurrentColorState.HSV_S,
value / 255.0)
: ColorSpaceHelper.HsvToGray(CurrentColorState.HSV_H, CurrentColorState.HSV_S,
value / 255.0);
double r = rgbtuple.Item1, g = rgbtuple.Item2, b = rgbtuple.Item3;
return Color.FromArgb(255, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255));
}
default:
return Color.FromArgb((byte)(CurrentColorState.A * 255), (byte)(CurrentColorState.RGB_R * 255),
(byte)(CurrentColorState.RGB_G * 255), (byte)(CurrentColorState.RGB_B * 255));
var rgbtupleDef = IsEffectivelyEnabled ? new Tuple<double, double, double>(CurrentColorState.RGB_R,
CurrentColorState.RGB_G, CurrentColorState.RGB_B)
: ColorSpaceHelper.RgbToGrayTuple(CurrentColorState.RGB_R,
CurrentColorState.RGB_G, CurrentColorState.RGB_B);
double rDef = rgbtupleDef.Item1, gDef = rgbtupleDef.Item2, bDef = rgbtupleDef.Item3;
return Color.FromArgb((byte)(CurrentColorState.A * 255), (byte)(rDef * 255),
(byte)(gDef * 255), (byte)(bDef * 255));
}
}
}
7 changes: 7 additions & 0 deletions src/ColorPicker.AvaloniaUI/UIExtensions/PreviewColorSlider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ static PreviewColorSlider()
new AnonymousObserver<AvaloniaPropertyChangedEventArgs<ColorState>>(ColorStateChangedCallback));
SmallChangeBindableProperty.Changed.Subscribe(
new AnonymousObserver<AvaloniaPropertyChangedEventArgs<double>>(SmallChangeBindableChangedCallback));

IsEffectivelyEnabledProperty.Changed.Subscribe(
new AnonymousObserver<AvaloniaPropertyChangedEventArgs<bool>>(e =>
{
var slider = e.Sender as PreviewColorSlider;
slider?.GenerateBackground();
}));
}

public PreviewColorSlider()
Expand Down
Loading