Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f63ec5f
Separate color picker control styles
robloo Apr 26, 2022
21dd839
Add initial ColorPreviewer primitive
robloo Apr 26, 2022
ad52499
Add initial ColorSlider primitive
robloo Apr 26, 2022
a5b3e85
Simplify default color property values
robloo Apr 26, 2022
e9cb628
Improve formatting
robloo Apr 26, 2022
c068adb
Update ColorPickerPage in ControlCatalog with ColorSlider and ColorPr…
robloo Apr 26, 2022
b6b8e96
Move spectrum enums into ColorSpectrum directory
robloo Apr 26, 2022
1f5e3c0
Move helpers into separate directory
robloo Apr 26, 2022
65e5e58
Make ColorSlider fully functional
robloo Apr 27, 2022
aae4b67
Remove Color property from ColorPreviewer
robloo Apr 27, 2022
357eddf
Implement ColorSlider PseudoClasses
robloo Apr 27, 2022
2d764ec
Merge branch 'master' into color-slider-previewer
robloo Apr 27, 2022
25a34ef
Implement color display name in ColorSpectrum
robloo Apr 28, 2022
7cb09a2
Follow Avalonia convention
robloo Apr 28, 2022
d9ef01a
Render the ColorSpectrum to physical device pixel resolution
robloo Apr 28, 2022
aef0d01
Reorder properties following Avalonia convention
robloo Apr 28, 2022
21190ff
Add :dark-selector PseudoClass to ColorSpectrum
robloo Apr 28, 2022
c3cdf85
Add default themes for ColorSlider and ColorPreviewer
robloo Apr 28, 2022
a86e0cc
Move ColorPicker theme definitions into theme folders
robloo Apr 28, 2022
e0bc2e3
Add RgbComponent enum and support direct casting with all component e…
robloo Apr 28, 2022
c3ce137
Move AccentColorConverter in Converters directory
robloo Apr 28, 2022
f550b8f
Move AccentColorConverter in Avalonia.Controls.Primitives.Converters …
robloo Apr 28, 2022
42b6a2f
Add ColorToDisplayNameConverter and ColorToHexConverter
robloo Apr 28, 2022
3d1818c
Fix .net standard 2.0 builds
robloo Apr 28, 2022
36ff1b7
Improve ColorChanged event implementations
robloo Apr 28, 2022
46e35ca
Use CornerRadiusFilterConverter in ColorPreviewer templates
robloo Apr 28, 2022
b447dd5
Merge branch 'master' into color-slider-previewer
robloo Apr 28, 2022
85a062d
Add ToBrushConverter and ToColorConverter
robloo Apr 28, 2022
b649d9f
Comments and formatting
robloo Apr 28, 2022
8e128c9
Add ValueConverterGroup
robloo Apr 28, 2022
e7f3bb7
TwoWay bind color properties by default
robloo Apr 29, 2022
f3d429f
Add ThirdComponentConverter and use it
robloo Apr 29, 2022
eb9bb2d
Standardize ImageBrush usage
robloo Apr 29, 2022
2eff303
Remove unused IncrementAlphaComponent helper method
robloo Apr 29, 2022
b8fc69a
Remove blue as default slider thumb color
robloo Apr 29, 2022
1e13e99
Improve ColorPreviewer control template
robloo Apr 29, 2022
c55a472
Reorganize color and color picker helpers
robloo Apr 29, 2022
53bc556
Rename 'channel' to 'component'
robloo Apr 29, 2022
282b9b0
Merge branch 'master' into color-slider-previewer
maxkatz6 May 3, 2022
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
4 changes: 2 additions & 2 deletions samples/ControlCatalog/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ public App()

public static readonly StyleInclude ColorPickerFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
{
Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Fluent.xaml")
Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml")
};

public static readonly StyleInclude ColorPickerDefault = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
{
Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Default.xaml")
Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml")
};

public static readonly StyleInclude DataGridFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
Expand Down
86 changes: 68 additions & 18 deletions samples/ControlCatalog/Pages/ColorPickerPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,77 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:primitives="clr-namespace:Avalonia.Controls.Primitives;assembly=Avalonia.Controls"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:pc="clr-namespace:Avalonia.Controls.Primitives.Converters;assembly=Avalonia.Controls.ColorPicker"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
x:Class="ControlCatalog.Pages.ColorPickerPage">

<Grid ColumnDefinitions="Auto,Auto"
RowDefinitions="Auto,Auto">
<ColorSpectrum Grid.Column="0"
<UserControl.Resources>
<pc:ThirdComponentConverter x:Key="ThirdComponent" />
</UserControl.Resources>

<Grid ColumnDefinitions="Auto,10,Auto">
<Grid Grid.Column="0"
Grid.Row="0"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
<ColorSpectrum x:Name="ColorSpectrum1"
Grid.Row="0"
Color="Red"
CornerRadius="10"
Height="256"
Width="256" />
<ColorSlider Grid.Row="1"
Margin="0,10,0,0"
ColorComponent="Component1"
ColorModel="Hsva"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" />
<ColorSlider Grid.Row="2"
ColorComponent="Component2"
ColorModel="Hsva"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" />
<ColorSlider Grid.Row="3"
ColorComponent="Component3"
ColorModel="Hsva"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" />
<ColorSlider Grid.Row="4"
ColorComponent="Alpha"
ColorModel="Hsva"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" />
<ColorPreviewer Grid.Row="5"
ShowAccentColors="True"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" />
</Grid>
<Grid Grid.Column="2"
Grid.Row="0"
ColumnDefinitions="Auto,Auto,Auto"
RowDefinitions="Auto,Auto">
<ColorSlider Grid.Column="0"
Grid.Row="0"
Color="Red"
Height="256"
Width="256" />
<ColorSpectrum Grid.Column="1"
IsAlphaMaxForced="True"
IsSaturationValueMaxForced="False"
ColorComponent="{Binding Components, ElementName=ColorSpectrum2, Converter={StaticResource ThirdComponent}}"
ColorModel="Hsva"
Orientation="Vertical"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum2}" />
<ColorSpectrum x:Name="ColorSpectrum2"
Grid.Column="1"
Grid.Row="0"
Color="Green"
Shape="Ring"
Height="256"
Width="256" />
<ColorSlider Grid.Column="2"
Grid.Row="0"
Color="Green"
Shape="Ring"
Height="256"
Width="256" />
<ColorSpectrum Grid.Column="0"
Grid.Row="1"
CornerRadius="10"
Color="Blue"
Height="256"
Width="256" />
ColorComponent="Alpha"
ColorModel="Hsva"
Orientation="Vertical"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum2}" />
<ColorPreviewer Grid.Column="0"
Grid.ColumnSpan="3"
Grid.Row="1"
ShowAccentColors="True"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum2}" />
</Grid>
</Grid>
</UserControl>
28 changes: 28 additions & 0 deletions src/Avalonia.Controls.ColorPicker/ColorComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace Avalonia.Controls
{
/// <summary>
/// Defines a specific component within a color model.
/// </summary>
public enum ColorComponent
{
/// <summary>
/// Represents the alpha component.
/// </summary>
Alpha = 0,

/// <summary>
/// Represents the first color component which is Red when RGB or Hue when HSV.
/// </summary>
Component1 = 1,

/// <summary>
/// Represents the second color component which is Green when RGB or Saturation when HSV.
/// </summary>
Component2 = 2,

/// <summary>
/// Represents the third color component which is Blue when RGB or Value when HSV.
/// </summary>
Component3 = 3
}
}
18 changes: 18 additions & 0 deletions src/Avalonia.Controls.ColorPicker/ColorModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Avalonia.Controls
{
/// <summary>
/// Defines the model used to represent colors.
/// </summary>
public enum ColorModel
{
/// <summary>
/// Color is represented by hue, saturation, value and alpha components.
/// </summary>
Hsva,

/// <summary>
/// Color is represented by red, green, blue and alpha components.
/// </summary>
Rgba
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using Avalonia.Data;
using Avalonia.Media;

namespace Avalonia.Controls.Primitives
{
/// <inheritdoc/>
public partial class ColorPreviewer
{
/// <summary>
/// Defines the <see cref="HsvColor"/> property.
/// </summary>
public static readonly StyledProperty<HsvColor> HsvColorProperty =
AvaloniaProperty.Register<ColorPreviewer, HsvColor>(
nameof(HsvColor),
Colors.Transparent.ToHsv(),
defaultBindingMode: BindingMode.TwoWay);

/// <summary>
/// Defines the <see cref="ShowAccentColors"/> property.
/// </summary>
public static readonly StyledProperty<bool> ShowAccentColorsProperty =
AvaloniaProperty.Register<ColorPreviewer, bool>(
nameof(ShowAccentColors),
true);

/// <summary>
/// Gets or sets the currently previewed color in the HSV color model.
/// </summary>
/// <remarks>
/// Only an HSV color is supported in this control to ensure there is never any
/// loss of precision or color information. Accent colors, like the color spectrum,
/// only operate with the HSV color model.
/// </remarks>
public HsvColor HsvColor
{
get => GetValue(HsvColorProperty);
set => SetValue(HsvColorProperty, value);
}

/// <summary>
/// Gets or sets a value indicating whether accent colors are shown along
/// with the preview color.
/// </summary>
public bool ShowAccentColors
{
get => GetValue(ShowAccentColorsProperty);
set => SetValue(ShowAccentColorsProperty, value);
}
}
}
130 changes: 130 additions & 0 deletions src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using System;
using System.Globalization;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives.Converters;
using Avalonia.Input;
using Avalonia.Media;

namespace Avalonia.Controls.Primitives
{
/// <summary>
/// Presents a preview color with optional accent colors.
/// </summary>
[TemplatePart(Name = nameof(AccentDec1Border), Type = typeof(Border))]
[TemplatePart(Name = nameof(AccentDec2Border), Type = typeof(Border))]
[TemplatePart(Name = nameof(AccentInc1Border), Type = typeof(Border))]
[TemplatePart(Name = nameof(AccentInc2Border), Type = typeof(Border))]
public partial class ColorPreviewer : TemplatedControl
{
/// <summary>
/// Event for when the selected color changes within the previewer.
/// This occurs when an accent color is pressed.
/// </summary>
public event EventHandler<ColorChangedEventArgs>? ColorChanged;

private bool eventsConnected = false;

private Border? AccentDec1Border;
private Border? AccentDec2Border;
private Border? AccentInc1Border;
private Border? AccentInc2Border;

/// <summary>
/// Initializes a new instance of the <see cref="ColorPreviewer"/> class.
/// </summary>
public ColorPreviewer() : base()
{
}

/// <summary>
/// Connects or disconnects all control event handlers.
/// </summary>
/// <param name="connected">True to connect event handlers, otherwise false.</param>
private void ConnectEvents(bool connected)
{
if (connected == true && eventsConnected == false)
{
// Add all events
if (AccentDec1Border != null) { AccentDec1Border.PointerPressed += AccentBorder_PointerPressed; }
if (AccentDec2Border != null) { AccentDec2Border.PointerPressed += AccentBorder_PointerPressed; }
if (AccentInc1Border != null) { AccentInc1Border.PointerPressed += AccentBorder_PointerPressed; }
if (AccentInc2Border != null) { AccentInc2Border.PointerPressed += AccentBorder_PointerPressed; }

eventsConnected = true;
}
else if (connected == false && eventsConnected == true)
{
// Remove all events
if (AccentDec1Border != null) { AccentDec1Border.PointerPressed -= AccentBorder_PointerPressed; }
if (AccentDec2Border != null) { AccentDec2Border.PointerPressed -= AccentBorder_PointerPressed; }
if (AccentInc1Border != null) { AccentInc1Border.PointerPressed -= AccentBorder_PointerPressed; }
if (AccentInc2Border != null) { AccentInc2Border.PointerPressed -= AccentBorder_PointerPressed; }

eventsConnected = false;
}
}

/// <inheritdoc/>
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
// Remove any existing events present if the control was previously loaded then unloaded
ConnectEvents(false);

AccentDec1Border = e.NameScope.Find<Border>(nameof(AccentDec1Border));
AccentDec2Border = e.NameScope.Find<Border>(nameof(AccentDec2Border));
AccentInc1Border = e.NameScope.Find<Border>(nameof(AccentInc1Border));
AccentInc2Border = e.NameScope.Find<Border>(nameof(AccentInc2Border));

// Must connect after controls are found
ConnectEvents(true);

base.OnApplyTemplate(e);
}

/// <inheritdoc/>
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
if (change.Property == HsvColorProperty)
{
OnColorChanged(new ColorChangedEventArgs(
change.GetOldValue<HsvColor>().ToRgb(),
change.GetNewValue<HsvColor>().ToRgb()));
}

base.OnPropertyChanged(change);
}

/// <summary>
/// Called before the <see cref="ColorChanged"/> event occurs.
/// </summary>
/// <param name="e">The <see cref="ColorChangedEventArgs"/> defining old/new colors.</param>
protected virtual void OnColorChanged(ColorChangedEventArgs e)
{
ColorChanged?.Invoke(this, e);
}

/// <summary>
/// Event handler for when an accent color border is pressed.
/// This will update the color to the background of the pressed panel.
/// </summary>
private void AccentBorder_PointerPressed(object? sender, PointerPressedEventArgs e)
{
Border? border = sender as Border;
int accentStep = 0;
HsvColor hsvColor = HsvColor;

// Get the value component delta
try
{
accentStep = int.Parse(border?.Tag?.ToString() ?? "", CultureInfo.InvariantCulture);
}
catch { }

HsvColor newHsvColor = AccentColorConverter.GetAccent(hsvColor, accentStep);
HsvColor oldHsvColor = HsvColor;

HsvColor = newHsvColor;
OnColorChanged(new ColorChangedEventArgs(oldHsvColor.ToRgb(), newHsvColor.ToRgb()));
}
}
}
Loading