Browse Source

Merge branch 'master' into fixes/scoped-keyword-fixes

pull/9169/head
Steven Kirk 3 years ago
committed by GitHub
parent
commit
ca3d761fb4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .editorconfig
  2. 8
      samples/ControlCatalog/Pages/ColorPickerPage.xaml
  3. 16
      samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs
  4. 2
      src/Avalonia.Base/AvaloniaProperty.cs
  5. 2
      src/Avalonia.Base/AvaloniaPropertyRegistry.cs
  6. 12
      src/Avalonia.Base/Compatibility/StringCompatibilityExtensions.cs
  7. 2
      src/Avalonia.Base/Media/UnicodeRange.cs
  8. 781
      src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs
  9. 150
      src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs
  10. 662
      src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs
  11. 150
      src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs
  12. 229
      src/Avalonia.Controls.ColorPicker/ColorPalettes/SixteenColorPalette.cs
  13. 33
      src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs
  14. 53
      src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs
  15. 24
      src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs
  16. 117
      src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs
  17. 53
      src/Avalonia.Controls.ColorPicker/Converters/ThirdComponentConverter.cs
  18. 71
      src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs
  19. 34
      src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs
  20. 33
      src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
  21. 33
      src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml
  22. 463
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
  23. 754
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
  24. 131
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs
  25. 39
      src/Avalonia.Controls/AutoCompleteBox/PopulatedEventArgs.cs
  26. 39
      src/Avalonia.Controls/AutoCompleteBox/PopulatingEventArgs.cs
  27. 2
      src/Avalonia.Controls/Documents/IInlineHost.cs
  28. 42
      src/Avalonia.Controls/Documents/InlineRun.cs
  29. 48
      src/Avalonia.Controls/Documents/InlineUIContainer.cs
  30. 2
      src/Avalonia.Controls/Flyouts/FlyoutBase.cs
  31. 4
      src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
  32. 2
      src/Avalonia.Controls/Primitives/RangeBase.cs
  33. 88
      src/Avalonia.Controls/RichTextBlock.cs
  34. 3
      src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml
  35. 5
      src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
  36. 14
      src/Avalonia.Remote.Protocol/MetsysBson.cs
  37. 6
      src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml
  38. 2
      src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml
  39. 6
      src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml
  40. 4
      src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs
  41. 2
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
  42. 36
      tests/Avalonia.Controls.UnitTests/RichTextBlockTests.cs

2
.editorconfig

@ -143,6 +143,8 @@ dotnet_diagnostic.CA1802.severity = warning
dotnet_diagnostic.CA1825.severity = warning
# CA1821: Remove empty finalizers
dotnet_diagnostic.CA1821.severity = warning
#CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters
dotnet_diagnostic.CA1847.severity = warning
# Wrapping preferences
csharp_wrap_before_ternary_opsigns = false

8
samples/ControlCatalog/Pages/ColorPickerPage.xaml

@ -11,16 +11,17 @@
x:Class="ControlCatalog.Pages.ColorPickerPage">
<UserControl.Resources>
<pc:ThirdComponentConverter x:Key="ThirdComponent" />
</UserControl.Resources>
<Grid ColumnDefinitions="Auto,10,Auto,10,Auto"
<Grid x:Name="LayoutRoot"
ColumnDefinitions="Auto,10,Auto"
RowDefinitions="Auto,Auto">
<ColorView Grid.Column="0"
Grid.Row="0"
ColorSpectrumShape="Ring" />
<ColorPicker Grid.Column="0"
Grid.Row="1"
HsvColor="hsv(120, 1, 1)"
Margin="0,50,0,0">
<ColorPicker.Palette>
<controls:FlatColorPalette />
@ -56,8 +57,5 @@
IsAccentColorsVisible="False"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" />
</Grid>
<Grid Grid.Column="4"
Grid.Row="0">
</Grid>
</Grid>
</UserControl>

16
samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs

@ -1,6 +1,8 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
namespace ControlCatalog.Pages
{
@ -9,6 +11,20 @@ namespace ControlCatalog.Pages
public ColorPickerPage()
{
InitializeComponent();
var layoutRoot = this.GetControl<Grid>("LayoutRoot");
// ColorPicker added from code-behind
var colorPicker = new ColorPicker()
{
Color = Colors.Blue,
Margin = new Thickness(0, 50, 0, 0),
HorizontalAlignment = HorizontalAlignment.Center,
};
Grid.SetColumn(colorPicker, 2);
Grid.SetRow(colorPicker, 1);
layoutRoot.Children.Add(colorPicker);
}
private void InitializeComponent()

2
src/Avalonia.Base/AvaloniaProperty.cs

@ -41,7 +41,7 @@ namespace Avalonia
{
_ = name ?? throw new ArgumentNullException(nameof(name));
if (name.Contains("."))
if (name.Contains('.'))
{
throw new ArgumentException("'name' may not contain periods.");
}

2
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@ -228,7 +228,7 @@ namespace Avalonia
_ = type ?? throw new ArgumentNullException(nameof(type));
_ = name ?? throw new ArgumentNullException(nameof(name));
if (name.Contains("."))
if (name.Contains('.'))
{
throw new InvalidOperationException("Attached properties not supported.");
}

12
src/Avalonia.Base/Compatibility/StringCompatibilityExtensions.cs

@ -0,0 +1,12 @@
using System.Runtime.CompilerServices;
namespace System;
#if !NET6_0_OR_GREATER
public static class StringCompatibilityExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Contains(this string str, char search) =>
str.Contains(search.ToString());
}
#endif

2
src/Avalonia.Base/Media/UnicodeRange.cs

@ -163,7 +163,7 @@ namespace Avalonia.Media
throw new FormatException("Could not parse specified Unicode range segment.");
}
if (!single.Value.Contains("?"))
if (!single.Value.Contains('?'))
{
start = int.Parse(single.Groups[1].Value, System.Globalization.NumberStyles.HexNumber);
end = start;

781
src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs

@ -17,268 +17,603 @@ namespace Avalonia.Controls
/// </remarks>
public class FlatColorPalette : IColorPalette
{
// The full Flat UI color chart has 10 rows and 20 columns
// See: https://htmlcolorcodes.com/assets/downloads/flat-design-colors/flat-design-color-chart.png
// This is a reduced palette for usability
private static Color[,] colorChart = new Color[,]
/// <summary>
/// Defines all colors in the <see cref="FlatColorPalette"/>.
/// </summary>
/// <remarks>
/// This is done in an enum to ensure it is compiled into the assembly improving
/// startup performance.
/// </remarks>
public enum FlatColor : uint
{
// Pomegranate
{
Color.FromArgb(0xFF, 0xF9, 0xEB, 0xEA),
Color.FromArgb(0xFF, 0xE6, 0xB0, 0xAA),
Color.FromArgb(0xFF, 0xCD, 0x61, 0x55),
Color.FromArgb(0xFF, 0xA9, 0x32, 0x26),
Color.FromArgb(0xFF, 0x7B, 0x24, 0x1C),
},
Pomegranate1 = 0xFFF9EBEA,
Pomegranate2 = 0xFFF2D7D5,
Pomegranate3 = 0xFFE6B0AA,
Pomegranate4 = 0xFFD98880,
Pomegranate5 = 0xFFCD6155,
Pomegranate6 = 0xFFC0392B,
Pomegranate7 = 0xFFA93226,
Pomegranate8 = 0xFF922B21,
Pomegranate9 = 0xFF7B241C,
Pomegranate10 = 0xFF641E16,
// Alizarin
Alizarin1 = 0xFFFDEDEC,
Alizarin2 = 0xFFFADBD8,
Alizarin3 = 0xFFF5B7B1,
Alizarin4 = 0xFFF1948A,
Alizarin5 = 0xFFEC7063,
Alizarin6 = 0xFFE74C3C,
Alizarin7 = 0xFFCB4335,
Alizarin8 = 0xFFB03A2E,
Alizarin9 = 0xFF943126,
Alizarin10 = 0xFF78281F,
// Amethyst
{
Color.FromArgb(0xFF, 0xF5, 0xEE, 0xF8),
Color.FromArgb(0xFF, 0xD7, 0xBD, 0xE2),
Color.FromArgb(0xFF, 0xAF, 0x7A, 0xC5),
Color.FromArgb(0xFF, 0x88, 0x4E, 0xA0),
Color.FromArgb(0xFF, 0x63, 0x39, 0x74),
},
Amethyst1 = 0xFFF5EEF8,
Amethyst2 = 0xFFEBDEF0,
Amethyst3 = 0xFFD7BDE2,
Amethyst4 = 0xFFC39BD3,
Amethyst5 = 0xFFAF7AC5,
Amethyst6 = 0xFF9B59B6,
Amethyst7 = 0xFF884EA0,
Amethyst8 = 0xFF76448A,
Amethyst9 = 0xFF633974,
Amethyst10 = 0xFF512E5F,
// Wisteria
Wisteria1 = 0xFFF4ECF7,
Wisteria2 = 0xFFE8DAEF,
Wisteria3 = 0xFFD2B4DE,
Wisteria4 = 0xFFBB8FCE,
Wisteria5 = 0xFFA569BD,
Wisteria6 = 0xFF8E44AD,
Wisteria7 = 0xFF7D3C98,
Wisteria8 = 0xFF6C3483,
Wisteria9 = 0xFF5B2C6F,
Wisteria10 = 0xFF4A235A,
// Belize Hole
{
Color.FromArgb(0xFF, 0xEA, 0xF2, 0xF8),
Color.FromArgb(0xFF, 0xA9, 0xCC, 0xE3),
Color.FromArgb(0xFF, 0x54, 0x99, 0xC7),
Color.FromArgb(0xFF, 0x24, 0x71, 0xA3),
Color.FromArgb(0xFF, 0x1A, 0x52, 0x76),
},
BelizeHole1 = 0xFFEAF2F8,
BelizeHole2 = 0xFFD4E6F1,
BelizeHole3 = 0xFFA9CCE3,
BelizeHole4 = 0xFF7FB3D5,
BelizeHole5 = 0xFF5499C7,
BelizeHole6 = 0xFF2980B9,
BelizeHole7 = 0xFF2471A3,
BelizeHole8 = 0xFF1F618D,
BelizeHole9 = 0xFF1A5276,
BelizeHole10 = 0xFF154360,
// Peter River
PeterRiver1 = 0xFFEBF5FB,
PeterRiver2 = 0xFFD6EAF8,
PeterRiver3 = 0xFFAED6F1,
PeterRiver4 = 0xFF85C1E9,
PeterRiver5 = 0xFF5DADE2,
PeterRiver6 = 0xFF3498DB,
PeterRiver7 = 0xFF2E86C1,
PeterRiver8 = 0xFF2874A6,
PeterRiver9 = 0xFF21618C,
PeterRiver10 = 0xFF1B4F72,
// Turquoise
{
Color.FromArgb(0xFF, 0xE8, 0xF8, 0xF5),
Color.FromArgb(0xFF, 0xA3, 0xE4, 0xD7),
Color.FromArgb(0xFF, 0x48, 0xC9, 0xB0),
Color.FromArgb(0xFF, 0x17, 0xA5, 0x89),
Color.FromArgb(0xFF, 0x11, 0x78, 0x64),
},
Turquoise1 = 0xFFE8F8F5,
Turquoise2 = 0xFFD1F2EB,
Turquoise3 = 0xFFA3E4D7,
Turquoise4 = 0xFF76D7C4,
Turquoise5 = 0xFF48C9B0,
Turquoise6 = 0xFF1ABC9C,
Turquoise7 = 0xFF17A589,
Turquoise8 = 0xFF148F77,
Turquoise9 = 0xFF117864,
Turquoise10 = 0xFF0E6251,
// Green Sea
GreenSea1 = 0xFFE8F6F3,
GreenSea2 = 0xFFD0ECE7,
GreenSea3 = 0xFFA2D9CE,
GreenSea4 = 0xFF73C6B6,
GreenSea5 = 0xFF45B39D,
GreenSea6 = 0xFF16A085,
GreenSea7 = 0xFF138D75,
GreenSea8 = 0xFF117A65,
GreenSea9 = 0xFF0E6655,
GreenSea10 = 0xFF0B5345,
// Nephritis
{
Color.FromArgb(0xFF, 0xE9, 0xF7, 0xEF),
Color.FromArgb(0xFF, 0xA9, 0xDF, 0xBF),
Color.FromArgb(0xFF, 0x52, 0xBE, 0x80),
Color.FromArgb(0xFF, 0x22, 0x99, 0x54),
Color.FromArgb(0xFF, 0x19, 0x6F, 0x3D),
},
Nephritis1 = 0xFFE9F7EF,
Nephritis2 = 0xFFD4EFDF,
Nephritis3 = 0xFFA9DFBF,
Nephritis4 = 0xFF7DCEA0,
Nephritis5 = 0xFF52BE80,
Nephritis6 = 0xFF27AE60,
Nephritis7 = 0xFF229954,
Nephritis8 = 0xFF1E8449,
Nephritis9 = 0xFF196F3D,
Nephritis10 = 0xFF145A32,
// Emerald
Emerald1 = 0xFFEAFAF1,
Emerald2 = 0xFFD5F5E3,
Emerald3 = 0xFFABEBC6,
Emerald4 = 0xFF82E0AA,
Emerald5 = 0xFF58D68D,
Emerald6 = 0xFF2ECC71,
Emerald7 = 0xFF28B463,
Emerald8 = 0xFF239B56,
Emerald9 = 0xFF1D8348,
Emerald10 = 0xFF186A3B,
// Sunflower
{
Color.FromArgb(0xFF, 0xFE, 0xF9, 0xE7),
Color.FromArgb(0xFF, 0xF9, 0xE7, 0x9F),
Color.FromArgb(0xFF, 0xF4, 0xD0, 0x3F),
Color.FromArgb(0xFF, 0xD4, 0xAC, 0x0D),
Color.FromArgb(0xFF, 0x9A, 0x7D, 0x0A),
},
Sunflower1 = 0xFFFEF9E7,
Sunflower2 = 0xFFFCF3CF,
Sunflower3 = 0xFFF9E79F,
Sunflower4 = 0xFFF7DC6F,
Sunflower5 = 0xFFF4D03F,
Sunflower6 = 0xFFF1C40F,
Sunflower7 = 0xFFD4AC0D,
Sunflower8 = 0xFFB7950B,
Sunflower9 = 0xFF9A7D0A,
Sunflower10 = 0xFF7D6608,
// Orange
Orange1 = 0xFFFEF5E7,
Orange2 = 0xFFFDEBD0,
Orange3 = 0xFFFAD7A0,
Orange4 = 0xFFF8C471,
Orange5 = 0xFFF5B041,
Orange6 = 0xFFF39C12,
Orange7 = 0xFFD68910,
Orange8 = 0xFFB9770E,
Orange9 = 0xFF9C640C,
Orange10 = 0xFF7E5109,
// Carrot
{
Color.FromArgb(0xFF, 0xFD, 0xF2, 0xE9),
Color.FromArgb(0xFF, 0xF5, 0xCB, 0xA7),
Color.FromArgb(0xFF, 0xEB, 0x98, 0x4E),
Color.FromArgb(0xFF, 0xCA, 0x6F, 0x1E),
Color.FromArgb(0xFF, 0x93, 0x51, 0x16),
},
Carrot1 = 0xFFFDF2E9,
Carrot2 = 0xFFFAE5D3,
Carrot3 = 0xFFF5CBA7,
Carrot4 = 0xFFF0B27A,
Carrot5 = 0xFFEB984E,
Carrot6 = 0xFFE67E22,
Carrot7 = 0xFFCA6F1E,
Carrot8 = 0xFFAF601A,
Carrot9 = 0xFF935116,
Carrot10 = 0xFF784212,
// Pumpkin
Pumpkin1 = 0xFFFBEEE6,
Pumpkin2 = 0xFFF6DDCC,
Pumpkin3 = 0xFFEDBB99,
Pumpkin4 = 0xFFE59866,
Pumpkin5 = 0xFFDC7633,
Pumpkin6 = 0xFFD35400,
Pumpkin7 = 0xFFBA4A00,
Pumpkin8 = 0xFFA04000,
Pumpkin9 = 0xFF873600,
Pumpkin10 = 0xFF6E2C00,
// Clouds
{
Color.FromArgb(0xFF, 0xFD, 0xFE, 0xFE),
Color.FromArgb(0xFF, 0xF7, 0xF9, 0xF9),
Color.FromArgb(0xFF, 0xF0, 0xF3, 0xF4),
Color.FromArgb(0xFF, 0xD0, 0xD3, 0xD4),
Color.FromArgb(0xFF, 0x97, 0x9A, 0x9A),
},
Clouds1 = 0xFFFDFEFE,
Clouds2 = 0xFFFBFCFC,
Clouds3 = 0xFFF7F9F9,
Clouds4 = 0xFFF4F6F7,
Clouds5 = 0xFFF0F3F4,
Clouds6 = 0xFFECF0F1,
Clouds7 = 0xFFD0D3D4,
Clouds8 = 0xFFB3B6B7,
Clouds9 = 0xFF979A9A,
Clouds10 = 0xFF7B7D7D,
// Silver
Silver1 = 0xFFF8F9F9,
Silver2 = 0xFFF2F3F4,
Silver3 = 0xFFE5E7E9,
Silver4 = 0xFFD7DBDD,
Silver5 = 0xFFCACFD2,
Silver6 = 0xFFBDC3C7,
Silver7 = 0xFFA6ACAF,
Silver8 = 0xFF909497,
Silver9 = 0xFF797D7F,
Silver10 = 0xFF626567,
// Concrete
{
Color.FromArgb(0xFF, 0xF4, 0xF6, 0xF6),
Color.FromArgb(0xFF, 0xD5, 0xDB, 0xDB),
Color.FromArgb(0xFF, 0xAA, 0xB7, 0xB8),
Color.FromArgb(0xFF, 0x83, 0x91, 0x92),
Color.FromArgb(0xFF, 0x5F, 0x6A, 0x6A),
},
Concrete1 = 0xFFF4F6F6,
Concrete2 = 0xFFEAEDED,
Concrete3 = 0xFFD5DBDB,
Concrete4 = 0xFFBFC9CA,
Concrete5 = 0xFFAAB7B8,
Concrete6 = 0xFF95A5A6,
Concrete7 = 0xFF839192,
Concrete8 = 0xFF717D7E,
Concrete9 = 0xFF5F6A6A,
Concrete10 = 0xFF4D5656,
// Asbestos
Asbestos1 = 0xFFF2F4F4,
Asbestos2 = 0xFFE5E8E8,
Asbestos3 = 0xFFCCD1D1,
Asbestos4 = 0xFFB2BABB,
Asbestos5 = 0xFF99A3A4,
Asbestos6 = 0xFF7F8C8D,
Asbestos7 = 0xFF707B7C,
Asbestos8 = 0xFF616A6B,
Asbestos9 = 0xFF515A5A,
Asbestos10 = 0xFF424949,
// Wet Asphalt
{
Color.FromArgb(0xFF, 0xEB, 0xED, 0xEF),
Color.FromArgb(0xFF, 0xAE, 0xB6, 0xBF),
Color.FromArgb(0xFF, 0x5D, 0x6D, 0x7E),
Color.FromArgb(0xFF, 0x2E, 0x40, 0x53),
Color.FromArgb(0xFF, 0x21, 0x2F, 0x3C),
},
WetAsphalt1 = 0xFFEBEDEF,
WetAsphalt2 = 0xFFD6DBDF,
WetAsphalt3 = 0xFFAEB6BF,
WetAsphalt4 = 0xFF85929E,
WetAsphalt5 = 0xFF5D6D7E,
WetAsphalt6 = 0xFF34495E,
WetAsphalt7 = 0xFF2E4053,
WetAsphalt8 = 0xFF283747,
WetAsphalt9 = 0xFF212F3C,
WetAsphalt10 = 0xFF1B2631,
// Midnight Blue
MidnightBlue1 = 0xFFEAECEE,
MidnightBlue2 = 0xFFD5D8DC,
MidnightBlue3 = 0xFFABB2B9,
MidnightBlue4 = 0xFF808B96,
MidnightBlue5 = 0xFF566573,
MidnightBlue6 = 0xFF2C3E50,
MidnightBlue7 = 0xFF273746,
MidnightBlue8 = 0xFF212F3D,
MidnightBlue9 = 0xFF1C2833,
MidnightBlue10 = 0xFF17202A,
Pomegranate = Pomegranate3,
Alizarin = Alizarin3,
Amethyst = Amethyst3,
Wisteria = Wisteria3,
BelizeHole = BelizeHole3,
PeterRiver = PeterRiver3,
Turquoise = Turquoise3,
GreenSea = GreenSea3,
Nephritis = Nephritis3,
Emerald = Emerald3,
Sunflower = Sunflower3,
Orange = Orange3,
Carrot = Carrot3,
Pumpkin = Pumpkin3,
Clouds = Clouds3,
Silver = Silver3,
Concrete = Concrete3,
Asbestos = Asbestos3,
WetAsphalt = WetAsphalt3,
MidnightBlue = MidnightBlue3,
};
/// <summary>
/// Gets the index of the default shade of colors in this palette.
/// </summary>
public const int DefaultShadeIndex = 2;
/// <summary>
/// The index in the color palette of the 'Pomegranate' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int PomegranateIndex = 0;
/// <summary>
/// The index in the color palette of the 'Amethyst' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int AmethystIndex = 1;
/// <summary>
/// The index in the color palette of the 'BelizeHole' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int BelizeHoleIndex = 2;
/// <summary>
/// The index in the color palette of the 'Turquoise' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int TurquoiseIndex = 3;
/// <summary>
/// The index in the color palette of the 'Nephritis' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int NephritisIndex = 4;
/// <summary>
/// The index in the color palette of the 'Sunflower' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int SunflowerIndex = 5;
/// <summary>
/// The index in the color palette of the 'Carrot' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int CarrotIndex = 6;
/// <summary>
/// The index in the color palette of the 'Clouds' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int CloudsIndex = 7;
/// <summary>
/// The index in the color palette of the 'Concrete' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int ConcreteIndex = 8;
// See: https://htmlcolorcodes.com/assets/downloads/flat-design-colors/flat-design-color-chart.png
protected static Color[,]? _colorChart = null;
protected static object _colorChartMutex = new object();
/// <summary>
/// The index in the color palette of the 'WetAsphalt' color.
/// This index can correspond to multiple color shades.
/// Initializes all color chart colors.
/// </summary>
public const int WetAsphaltIndex = 9;
protected void InitColorChart()
{
lock (_colorChartMutex)
{
if (_colorChart != null)
{
return;
}
_colorChart = new Color[,]
{
// Pomegranate
{
Color.FromUInt32((uint)FlatColor.Pomegranate1),
Color.FromUInt32((uint)FlatColor.Pomegranate2),
Color.FromUInt32((uint)FlatColor.Pomegranate3),
Color.FromUInt32((uint)FlatColor.Pomegranate4),
Color.FromUInt32((uint)FlatColor.Pomegranate5),
Color.FromUInt32((uint)FlatColor.Pomegranate6),
Color.FromUInt32((uint)FlatColor.Pomegranate7),
Color.FromUInt32((uint)FlatColor.Pomegranate8),
Color.FromUInt32((uint)FlatColor.Pomegranate9),
Color.FromUInt32((uint)FlatColor.Pomegranate10),
},
// Alizarin
{
Color.FromUInt32((uint)FlatColor.Alizarin1),
Color.FromUInt32((uint)FlatColor.Alizarin2),
Color.FromUInt32((uint)FlatColor.Alizarin3),
Color.FromUInt32((uint)FlatColor.Alizarin4),
Color.FromUInt32((uint)FlatColor.Alizarin5),
Color.FromUInt32((uint)FlatColor.Alizarin6),
Color.FromUInt32((uint)FlatColor.Alizarin7),
Color.FromUInt32((uint)FlatColor.Alizarin8),
Color.FromUInt32((uint)FlatColor.Alizarin9),
Color.FromUInt32((uint)FlatColor.Alizarin10),
},
// Amethyst
{
Color.FromUInt32((uint)FlatColor.Amethyst1),
Color.FromUInt32((uint)FlatColor.Amethyst2),
Color.FromUInt32((uint)FlatColor.Amethyst3),
Color.FromUInt32((uint)FlatColor.Amethyst4),
Color.FromUInt32((uint)FlatColor.Amethyst5),
Color.FromUInt32((uint)FlatColor.Amethyst6),
Color.FromUInt32((uint)FlatColor.Amethyst7),
Color.FromUInt32((uint)FlatColor.Amethyst8),
Color.FromUInt32((uint)FlatColor.Amethyst9),
Color.FromUInt32((uint)FlatColor.Amethyst10),
},
// Wisteria
{
Color.FromUInt32((uint)FlatColor.Wisteria1),
Color.FromUInt32((uint)FlatColor.Wisteria2),
Color.FromUInt32((uint)FlatColor.Wisteria3),
Color.FromUInt32((uint)FlatColor.Wisteria4),
Color.FromUInt32((uint)FlatColor.Wisteria5),
Color.FromUInt32((uint)FlatColor.Wisteria6),
Color.FromUInt32((uint)FlatColor.Wisteria7),
Color.FromUInt32((uint)FlatColor.Wisteria8),
Color.FromUInt32((uint)FlatColor.Wisteria9),
Color.FromUInt32((uint)FlatColor.Wisteria10),
},
// Belize Hole
{
Color.FromUInt32((uint)FlatColor.BelizeHole1),
Color.FromUInt32((uint)FlatColor.BelizeHole2),
Color.FromUInt32((uint)FlatColor.BelizeHole3),
Color.FromUInt32((uint)FlatColor.BelizeHole4),
Color.FromUInt32((uint)FlatColor.BelizeHole5),
Color.FromUInt32((uint)FlatColor.BelizeHole6),
Color.FromUInt32((uint)FlatColor.BelizeHole7),
Color.FromUInt32((uint)FlatColor.BelizeHole8),
Color.FromUInt32((uint)FlatColor.BelizeHole9),
Color.FromUInt32((uint)FlatColor.BelizeHole10),
},
// Peter River
{
Color.FromUInt32((uint)FlatColor.PeterRiver1),
Color.FromUInt32((uint)FlatColor.PeterRiver2),
Color.FromUInt32((uint)FlatColor.PeterRiver3),
Color.FromUInt32((uint)FlatColor.PeterRiver4),
Color.FromUInt32((uint)FlatColor.PeterRiver5),
Color.FromUInt32((uint)FlatColor.PeterRiver6),
Color.FromUInt32((uint)FlatColor.PeterRiver7),
Color.FromUInt32((uint)FlatColor.PeterRiver8),
Color.FromUInt32((uint)FlatColor.PeterRiver9),
Color.FromUInt32((uint)FlatColor.PeterRiver10),
},
// Turquoise
{
Color.FromUInt32((uint)FlatColor.Turquoise1),
Color.FromUInt32((uint)FlatColor.Turquoise2),
Color.FromUInt32((uint)FlatColor.Turquoise3),
Color.FromUInt32((uint)FlatColor.Turquoise4),
Color.FromUInt32((uint)FlatColor.Turquoise5),
Color.FromUInt32((uint)FlatColor.Turquoise6),
Color.FromUInt32((uint)FlatColor.Turquoise7),
Color.FromUInt32((uint)FlatColor.Turquoise8),
Color.FromUInt32((uint)FlatColor.Turquoise9),
Color.FromUInt32((uint)FlatColor.Turquoise10),
},
// Green Sea
{
Color.FromUInt32((uint)FlatColor.GreenSea1),
Color.FromUInt32((uint)FlatColor.GreenSea2),
Color.FromUInt32((uint)FlatColor.GreenSea3),
Color.FromUInt32((uint)FlatColor.GreenSea4),
Color.FromUInt32((uint)FlatColor.GreenSea5),
Color.FromUInt32((uint)FlatColor.GreenSea6),
Color.FromUInt32((uint)FlatColor.GreenSea7),
Color.FromUInt32((uint)FlatColor.GreenSea8),
Color.FromUInt32((uint)FlatColor.GreenSea9),
Color.FromUInt32((uint)FlatColor.GreenSea10),
},
// Nephritis
{
Color.FromUInt32((uint)FlatColor.Nephritis1),
Color.FromUInt32((uint)FlatColor.Nephritis2),
Color.FromUInt32((uint)FlatColor.Nephritis3),
Color.FromUInt32((uint)FlatColor.Nephritis4),
Color.FromUInt32((uint)FlatColor.Nephritis5),
Color.FromUInt32((uint)FlatColor.Nephritis6),
Color.FromUInt32((uint)FlatColor.Nephritis7),
Color.FromUInt32((uint)FlatColor.Nephritis8),
Color.FromUInt32((uint)FlatColor.Nephritis9),
Color.FromUInt32((uint)FlatColor.Nephritis10),
},
// Emerald
{
Color.FromUInt32((uint)FlatColor.Emerald1),
Color.FromUInt32((uint)FlatColor.Emerald2),
Color.FromUInt32((uint)FlatColor.Emerald3),
Color.FromUInt32((uint)FlatColor.Emerald4),
Color.FromUInt32((uint)FlatColor.Emerald5),
Color.FromUInt32((uint)FlatColor.Emerald6),
Color.FromUInt32((uint)FlatColor.Emerald7),
Color.FromUInt32((uint)FlatColor.Emerald8),
Color.FromUInt32((uint)FlatColor.Emerald9),
Color.FromUInt32((uint)FlatColor.Emerald10),
},
// Sunflower
{
Color.FromUInt32((uint)FlatColor.Sunflower1),
Color.FromUInt32((uint)FlatColor.Sunflower2),
Color.FromUInt32((uint)FlatColor.Sunflower3),
Color.FromUInt32((uint)FlatColor.Sunflower4),
Color.FromUInt32((uint)FlatColor.Sunflower5),
Color.FromUInt32((uint)FlatColor.Sunflower6),
Color.FromUInt32((uint)FlatColor.Sunflower7),
Color.FromUInt32((uint)FlatColor.Sunflower8),
Color.FromUInt32((uint)FlatColor.Sunflower9),
Color.FromUInt32((uint)FlatColor.Sunflower10),
},
// Orange
{
Color.FromUInt32((uint)FlatColor.Orange1),
Color.FromUInt32((uint)FlatColor.Orange2),
Color.FromUInt32((uint)FlatColor.Orange3),
Color.FromUInt32((uint)FlatColor.Orange4),
Color.FromUInt32((uint)FlatColor.Orange5),
Color.FromUInt32((uint)FlatColor.Orange6),
Color.FromUInt32((uint)FlatColor.Orange7),
Color.FromUInt32((uint)FlatColor.Orange8),
Color.FromUInt32((uint)FlatColor.Orange9),
Color.FromUInt32((uint)FlatColor.Orange10),
},
// Carrot
{
Color.FromUInt32((uint)FlatColor.Carrot1),
Color.FromUInt32((uint)FlatColor.Carrot2),
Color.FromUInt32((uint)FlatColor.Carrot3),
Color.FromUInt32((uint)FlatColor.Carrot4),
Color.FromUInt32((uint)FlatColor.Carrot5),
Color.FromUInt32((uint)FlatColor.Carrot6),
Color.FromUInt32((uint)FlatColor.Carrot7),
Color.FromUInt32((uint)FlatColor.Carrot8),
Color.FromUInt32((uint)FlatColor.Carrot9),
Color.FromUInt32((uint)FlatColor.Carrot10),
},
// Pumpkin
{
Color.FromUInt32((uint)FlatColor.Pumpkin1),
Color.FromUInt32((uint)FlatColor.Pumpkin2),
Color.FromUInt32((uint)FlatColor.Pumpkin3),
Color.FromUInt32((uint)FlatColor.Pumpkin4),
Color.FromUInt32((uint)FlatColor.Pumpkin5),
Color.FromUInt32((uint)FlatColor.Pumpkin6),
Color.FromUInt32((uint)FlatColor.Pumpkin7),
Color.FromUInt32((uint)FlatColor.Pumpkin8),
Color.FromUInt32((uint)FlatColor.Pumpkin9),
Color.FromUInt32((uint)FlatColor.Pumpkin10),
},
// Clouds
{
Color.FromUInt32((uint)FlatColor.Clouds1),
Color.FromUInt32((uint)FlatColor.Clouds2),
Color.FromUInt32((uint)FlatColor.Clouds3),
Color.FromUInt32((uint)FlatColor.Clouds4),
Color.FromUInt32((uint)FlatColor.Clouds5),
Color.FromUInt32((uint)FlatColor.Clouds6),
Color.FromUInt32((uint)FlatColor.Clouds7),
Color.FromUInt32((uint)FlatColor.Clouds8),
Color.FromUInt32((uint)FlatColor.Clouds9),
Color.FromUInt32((uint)FlatColor.Clouds10),
},
// Silver
{
Color.FromUInt32((uint)FlatColor.Silver1),
Color.FromUInt32((uint)FlatColor.Silver2),
Color.FromUInt32((uint)FlatColor.Silver3),
Color.FromUInt32((uint)FlatColor.Silver4),
Color.FromUInt32((uint)FlatColor.Silver5),
Color.FromUInt32((uint)FlatColor.Silver6),
Color.FromUInt32((uint)FlatColor.Silver7),
Color.FromUInt32((uint)FlatColor.Silver8),
Color.FromUInt32((uint)FlatColor.Silver9),
Color.FromUInt32((uint)FlatColor.Silver10),
},
// Concrete
{
Color.FromUInt32((uint)FlatColor.Concrete1),
Color.FromUInt32((uint)FlatColor.Concrete2),
Color.FromUInt32((uint)FlatColor.Concrete3),
Color.FromUInt32((uint)FlatColor.Concrete4),
Color.FromUInt32((uint)FlatColor.Concrete5),
Color.FromUInt32((uint)FlatColor.Concrete6),
Color.FromUInt32((uint)FlatColor.Concrete7),
Color.FromUInt32((uint)FlatColor.Concrete8),
Color.FromUInt32((uint)FlatColor.Concrete9),
Color.FromUInt32((uint)FlatColor.Concrete10),
},
// Asbestos
{
Color.FromUInt32((uint)FlatColor.Asbestos1),
Color.FromUInt32((uint)FlatColor.Asbestos2),
Color.FromUInt32((uint)FlatColor.Asbestos3),
Color.FromUInt32((uint)FlatColor.Asbestos4),
Color.FromUInt32((uint)FlatColor.Asbestos5),
Color.FromUInt32((uint)FlatColor.Asbestos6),
Color.FromUInt32((uint)FlatColor.Asbestos7),
Color.FromUInt32((uint)FlatColor.Asbestos8),
Color.FromUInt32((uint)FlatColor.Asbestos9),
Color.FromUInt32((uint)FlatColor.Asbestos10),
},
// Wet Asphalt
{
Color.FromUInt32((uint)FlatColor.WetAsphalt1),
Color.FromUInt32((uint)FlatColor.WetAsphalt2),
Color.FromUInt32((uint)FlatColor.WetAsphalt3),
Color.FromUInt32((uint)FlatColor.WetAsphalt4),
Color.FromUInt32((uint)FlatColor.WetAsphalt5),
Color.FromUInt32((uint)FlatColor.WetAsphalt6),
Color.FromUInt32((uint)FlatColor.WetAsphalt7),
Color.FromUInt32((uint)FlatColor.WetAsphalt8),
Color.FromUInt32((uint)FlatColor.WetAsphalt9),
Color.FromUInt32((uint)FlatColor.WetAsphalt10),
},
// Midnight Blue
{
Color.FromUInt32((uint)FlatColor.MidnightBlue1),
Color.FromUInt32((uint)FlatColor.MidnightBlue2),
Color.FromUInt32((uint)FlatColor.MidnightBlue3),
Color.FromUInt32((uint)FlatColor.MidnightBlue4),
Color.FromUInt32((uint)FlatColor.MidnightBlue5),
Color.FromUInt32((uint)FlatColor.MidnightBlue6),
Color.FromUInt32((uint)FlatColor.MidnightBlue7),
Color.FromUInt32((uint)FlatColor.MidnightBlue8),
Color.FromUInt32((uint)FlatColor.MidnightBlue9),
Color.FromUInt32((uint)FlatColor.MidnightBlue10),
},
};
}
return;
}
/// <inheritdoc/>
public int ColorCount
{
// Table is transposed compared to the reference chart
get => colorChart.GetLength(0);
get => 20;
}
/// <inheritdoc/>
public int ShadeCount
{
// Table is transposed compared to the reference chart
get => colorChart.GetLength(1);
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFC0392B.
/// </summary>
public static Color Pomegranate
{
get => colorChart[PomegranateIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF9B59B6.
/// </summary>
public static Color Amethyst
{
get => colorChart[AmethystIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF2980B9.
/// </summary>
public static Color BelizeHole
{
get => colorChart[BelizeHoleIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF1ABC9C.
/// </summary>
public static Color Turquoise
{
get => colorChart[TurquoiseIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF27AE60.
/// </summary>
public static Color Nephritis
{
get => colorChart[NephritisIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFF1C40F.
/// </summary>
public static Color Sunflower
{
get => colorChart[SunflowerIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFE67E22.
/// </summary>
public static Color Carrot
{
get => colorChart[CarrotIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFECF0F1.
/// </summary>
public static Color Clouds
{
get => colorChart[CloudsIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF95A5A6.
/// </summary>
public static Color Concrete
{
get => colorChart[ConcreteIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF34495E.
/// </summary>
public static Color WetAsphalt
{
get => colorChart[WetAsphaltIndex, DefaultShadeIndex];
get => 10;
}
/// <inheritdoc/>
public Color GetColor(int colorIndex, int shadeIndex)
{
// Table is transposed compared to the reference chart
return colorChart[
MathUtilities.Clamp(colorIndex, 0, colorChart.GetLength(0) - 1),
MathUtilities.Clamp(shadeIndex, 0, colorChart.GetLength(1) - 1)];
if (_colorChart == null)
{
InitColorChart();
}
return _colorChart![
MathUtilities.Clamp(colorIndex, 0, ColorCount - 1),
MathUtilities.Clamp(shadeIndex, 0, ShadeCount - 1)];
}
}
}

150
src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs

@ -0,0 +1,150 @@
using Avalonia.Media;
using Avalonia.Utilities;
using FlatColor = Avalonia.Controls.FlatColorPalette.FlatColor;
namespace Avalonia.Controls
{
/// <summary>
/// Implements half of the <see cref="FlatColorPalette"/> for improved usability.
/// </summary>
/// <inheritdoc cref="FlatColorPalette"/>
public class FlatHalfColorPalette : IColorPalette
{
protected static Color[,]? _colorChart = null;
protected static object _colorChartMutex = new object();
/// <summary>
/// Initializes all color chart colors.
/// </summary>
protected void InitColorChart()
{
lock (_colorChartMutex)
{
if (_colorChart != null)
{
return;
}
_colorChart = new Color[,]
{
// Pomegranate
{
Color.FromUInt32((uint)FlatColor.Pomegranate1),
Color.FromUInt32((uint)FlatColor.Pomegranate3),
Color.FromUInt32((uint)FlatColor.Pomegranate5),
Color.FromUInt32((uint)FlatColor.Pomegranate7),
Color.FromUInt32((uint)FlatColor.Pomegranate9),
},
// Amethyst
{
Color.FromUInt32((uint)FlatColor.Amethyst1),
Color.FromUInt32((uint)FlatColor.Amethyst3),
Color.FromUInt32((uint)FlatColor.Amethyst5),
Color.FromUInt32((uint)FlatColor.Amethyst7),
Color.FromUInt32((uint)FlatColor.Amethyst9),
},
// Belize Hole
{
Color.FromUInt32((uint)FlatColor.BelizeHole1),
Color.FromUInt32((uint)FlatColor.BelizeHole3),
Color.FromUInt32((uint)FlatColor.BelizeHole5),
Color.FromUInt32((uint)FlatColor.BelizeHole7),
Color.FromUInt32((uint)FlatColor.BelizeHole9),
},
// Turquoise
{
Color.FromUInt32((uint)FlatColor.Turquoise1),
Color.FromUInt32((uint)FlatColor.Turquoise3),
Color.FromUInt32((uint)FlatColor.Turquoise5),
Color.FromUInt32((uint)FlatColor.Turquoise7),
Color.FromUInt32((uint)FlatColor.Turquoise9),
},
// Nephritis
{
Color.FromUInt32((uint)FlatColor.Nephritis1),
Color.FromUInt32((uint)FlatColor.Nephritis3),
Color.FromUInt32((uint)FlatColor.Nephritis5),
Color.FromUInt32((uint)FlatColor.Nephritis7),
Color.FromUInt32((uint)FlatColor.Nephritis9),
},
// Sunflower
{
Color.FromUInt32((uint)FlatColor.Sunflower1),
Color.FromUInt32((uint)FlatColor.Sunflower3),
Color.FromUInt32((uint)FlatColor.Sunflower5),
Color.FromUInt32((uint)FlatColor.Sunflower7),
Color.FromUInt32((uint)FlatColor.Sunflower9),
},
// Carrot
{
Color.FromUInt32((uint)FlatColor.Carrot1),
Color.FromUInt32((uint)FlatColor.Carrot3),
Color.FromUInt32((uint)FlatColor.Carrot5),
Color.FromUInt32((uint)FlatColor.Carrot7),
Color.FromUInt32((uint)FlatColor.Carrot9),
},
// Clouds
{
Color.FromUInt32((uint)FlatColor.Clouds1),
Color.FromUInt32((uint)FlatColor.Clouds3),
Color.FromUInt32((uint)FlatColor.Clouds5),
Color.FromUInt32((uint)FlatColor.Clouds7),
Color.FromUInt32((uint)FlatColor.Clouds9),
},
// Concrete
{
Color.FromUInt32((uint)FlatColor.Concrete1),
Color.FromUInt32((uint)FlatColor.Concrete3),
Color.FromUInt32((uint)FlatColor.Concrete5),
Color.FromUInt32((uint)FlatColor.Concrete7),
Color.FromUInt32((uint)FlatColor.Concrete9),
},
// Wet Asphalt
{
Color.FromUInt32((uint)FlatColor.WetAsphalt1),
Color.FromUInt32((uint)FlatColor.WetAsphalt3),
Color.FromUInt32((uint)FlatColor.WetAsphalt5),
Color.FromUInt32((uint)FlatColor.WetAsphalt7),
Color.FromUInt32((uint)FlatColor.WetAsphalt9),
},
};
}
return;
}
/// <inheritdoc/>
public int ColorCount
{
get => 10;
}
/// <inheritdoc/>
public int ShadeCount
{
get => 5;
}
/// <inheritdoc/>
public Color GetColor(int colorIndex, int shadeIndex)
{
if (_colorChart == null)
{
InitColorChart();
}
return _colorChart![
MathUtilities.Clamp(colorIndex, 0, ColorCount - 1),
MathUtilities.Clamp(shadeIndex, 0, ShadeCount - 1)];
}
}
}

662
src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs

@ -21,295 +21,529 @@ namespace Avalonia.Controls
/// </remarks>
public class MaterialColorPalette : IColorPalette
{
/// <summary>
/// Defines all colors in the <see cref="MaterialColorPalette"/>.
/// </summary>
/// <remarks>
/// This is done in an enum to ensure it is compiled into the assembly improving
/// startup performance.
/// </remarks>
public enum MaterialColor : uint
{
// Red
Red50 = 0xFFFFEBEE,
Red100 = 0xFFFFCDD2,
Red200 = 0xFFEF9A9A,
Red300 = 0xFFE57373,
Red400 = 0xFFEF5350,
Red500 = 0xFFF44336,
Red600 = 0xFFE53935,
Red700 = 0xFFD32F2F,
Red800 = 0xFFC62828,
Red900 = 0xFFB71C1C,
// Pink
Pink50 = 0xFFFCE4EC,
Pink100 = 0xFFF8BBD0,
Pink200 = 0xFFF48FB1,
Pink300 = 0xFFF06292,
Pink400 = 0xFFEC407A,
Pink500 = 0xFFE91E63,
Pink600 = 0xFFD81B60,
Pink700 = 0xFFC2185B,
Pink800 = 0xFFAD1457,
Pink900 = 0xFF880E4F,
// Purple
Purple50 = 0xFFF3E5F5,
Purple100 = 0xFFE1BEE7,
Purple200 = 0xFFCE93D8,
Purple300 = 0xFFBA68C8,
Purple400 = 0xFFAB47BC,
Purple500 = 0xFF9C27B0,
Purple600 = 0xFF8E24AA,
Purple700 = 0xFF7B1FA2,
Purple800 = 0xFF6A1B9A,
Purple900 = 0xFF4A148C,
// Deep Purple
DeepPurple50 = 0xFFEDE7F6,
DeepPurple100 = 0xFFD1C4E9,
DeepPurple200 = 0xFFB39DDB,
DeepPurple300 = 0xFF9575CD,
DeepPurple400 = 0xFF7E57C2,
DeepPurple500 = 0xFF673AB7,
DeepPurple600 = 0xFF5E35B1,
DeepPurple700 = 0xFF512DA8,
DeepPurple800 = 0xFF4527A0,
DeepPurple900 = 0xFF311B92,
// Indigo
Indigo50 = 0xFFE8EAF6,
Indigo100 = 0xFFC5CAE9,
Indigo200 = 0xFF9FA8DA,
Indigo300 = 0xFF7986CB,
Indigo400 = 0xFF5C6BC0,
Indigo500 = 0xFF3F51B5,
Indigo600 = 0xFF3949AB,
Indigo700 = 0xFF303F9F,
Indigo800 = 0xFF283593,
Indigo900 = 0xFF1A237E,
// Blue
Blue50 = 0xFFE3F2FD,
Blue100 = 0xFFBBDEFB,
Blue200 = 0xFF90CAF9,
Blue300 = 0xFF64B5F6,
Blue400 = 0xFF42A5F5,
Blue500 = 0xFF2196F3,
Blue600 = 0xFF1E88E5,
Blue700 = 0xFF1976D2,
Blue800 = 0xFF1565C0,
Blue900 = 0xFF0D47A1,
// Light Blue
LightBlue50 = 0xFFE1F5FE,
LightBlue100 = 0xFFB3E5FC,
LightBlue200 = 0xFF81D4FA,
LightBlue300 = 0xFF4FC3F7,
LightBlue400 = 0xFF29B6F6,
LightBlue500 = 0xFF03A9F4,
LightBlue600 = 0xFF039BE5,
LightBlue700 = 0xFF0288D1,
LightBlue800 = 0xFF0277BD,
LightBlue900 = 0xFF01579B,
// Cyan
Cyan50 = 0xFFE0F7FA,
Cyan100 = 0xFFB2EBF2,
Cyan200 = 0xFF80DEEA,
Cyan300 = 0xFF4DD0E1,
Cyan400 = 0xFF26C6DA,
Cyan500 = 0xFF00BCD4,
Cyan600 = 0xFF00ACC1,
Cyan700 = 0xFF0097A7,
Cyan800 = 0xFF00838F,
Cyan900 = 0xFF006064,
// Teal
Teal50 = 0xFFE0F2F1,
Teal100 = 0xFFB2DFDB,
Teal200 = 0xFF80CBC4,
Teal300 = 0xFF4DB6AC,
Teal400 = 0xFF26A69A,
Teal500 = 0xFF009688,
Teal600 = 0xFF00897B,
Teal700 = 0xFF00796B,
Teal800 = 0xFF00695C,
Teal900 = 0xFF004D40,
// Green
Green50 = 0xFFE8F5E9,
Green100 = 0xFFC8E6C9,
Green200 = 0xFFA5D6A7,
Green300 = 0xFF81C784,
Green400 = 0xFF66BB6A,
Green500 = 0xFF4CAF50,
Green600 = 0xFF43A047,
Green700 = 0xFF388E3C,
Green800 = 0xFF2E7D32,
Green900 = 0xFF1B5E20,
// Light Green
LightGreen50 = 0xFFF1F8E9,
LightGreen100 = 0xFFDCEDC8,
LightGreen200 = 0xFFC5E1A5,
LightGreen300 = 0xFFAED581,
LightGreen400 = 0xFF9CCC65,
LightGreen500 = 0xFF8BC34A,
LightGreen600 = 0xFF7CB342,
LightGreen700 = 0xFF689F38,
LightGreen800 = 0xFF558B2F,
LightGreen900 = 0xFF33691E,
// Lime
Lime50 = 0xFFF9FBE7,
Lime100 = 0xFFF0F4C3,
Lime200 = 0xFFE6EE9C,
Lime300 = 0xFFDCE775,
Lime400 = 0xFFD4E157,
Lime500 = 0xFFCDDC39,
Lime600 = 0xFFC0CA33,
Lime700 = 0xFFAFB42B,
Lime800 = 0xFF9E9D24,
Lime900 = 0xFF827717,
// Yellow
Yellow50 = 0xFFFFFDE7,
Yellow100 = 0xFFFFF9C4,
Yellow200 = 0xFFFFF59D,
Yellow300 = 0xFFFFF176,
Yellow400 = 0xFFFFEE58,
Yellow500 = 0xFFFFEB3B,
Yellow600 = 0xFFFDD835,
Yellow700 = 0xFFFBC02D,
Yellow800 = 0xFFF9A825,
Yellow900 = 0xFFF57F17,
// Amber
Amber50 = 0xFFFFF8E1,
Amber100 = 0xFFFFECB3,
Amber200 = 0xFFFFE082,
Amber300 = 0xFFFFD54F,
Amber400 = 0xFFFFCA28,
Amber500 = 0xFFFFC107,
Amber600 = 0xFFFFB300,
Amber700 = 0xFFFFA000,
Amber800 = 0xFFFF8F00,
Amber900 = 0xFFFF6F00,
// Orange
Orange50 = 0xFFFFF3E0,
Orange100 = 0xFFFFE0B2,
Orange200 = 0xFFFFCC80,
Orange300 = 0xFFFFB74D,
Orange400 = 0xFFFFA726,
Orange500 = 0xFFFF9800,
Orange600 = 0xFFFB8C00,
Orange700 = 0xFFF57C00,
Orange800 = 0xFFEF6C00,
Orange900 = 0xFFE65100,
// Deep Orange
DeepOrange50 = 0xFFFBE9E7,
DeepOrange100 = 0xFFFFCCBC,
DeepOrange200 = 0xFFFFAB91,
DeepOrange300 = 0xFFFF8A65,
DeepOrange400 = 0xFFFF7043,
DeepOrange500 = 0xFFFF5722,
DeepOrange600 = 0xFFF4511E,
DeepOrange700 = 0xFFE64A19,
DeepOrange800 = 0xFFD84315,
DeepOrange900 = 0xFFBF360C,
// Brown
Brown50 = 0xFFEFEBE9,
Brown100 = 0xFFD7CCC8,
Brown200 = 0xFFBCAAA4,
Brown300 = 0xFFA1887F,
Brown400 = 0xFF8D6E63,
Brown500 = 0xFF795548,
Brown600 = 0xFF6D4C41,
Brown700 = 0xFF5D4037,
Brown800 = 0xFF4E342E,
Brown900 = 0xFF3E2723,
// Gray
Gray50 = 0xFFFAFAFA,
Gray100 = 0xFFF5F5F5,
Gray200 = 0xFFEEEEEE,
Gray300 = 0xFFE0E0E0,
Gray400 = 0xFFBDBDBD,
Gray500 = 0xFF9E9E9E,
Gray600 = 0xFF757575,
Gray700 = 0xFF616161,
Gray800 = 0xFF424242,
Gray900 = 0xFF212121,
// Blue Gray
BlueGray50 = 0xFFECEFF1,
BlueGray100 = 0xFFCFD8DC,
BlueGray200 = 0xFFB0BEC5,
BlueGray300 = 0xFF90A4AE,
BlueGray400 = 0xFF78909C,
BlueGray500 = 0xFF607D8B,
BlueGray600 = 0xFF546E7A,
BlueGray700 = 0xFF455A64,
BlueGray800 = 0xFF37474F,
BlueGray900 = 0xFF263238,
}
// See: https://material.io/design/color/the-color-system.html#tools-for-picking-colors
// This is a reduced palette for uniformity
private static Color[,]? _colorChart = null;
private static int _colorChartColorCount = 0;
private static int _colorChartShadeCount = 0;
private static object _colorChartMutex = new object();
protected static Color[,]? _colorChart = null;
protected static object _colorChartMutex = new object();
/// <summary>
/// Initializes all color chart colors.
/// </summary>
/// <remarks>
/// This is pulled out separately to lazy load for performance.
/// If no material color palette is ever used, no colors will be created.
/// </remarks>
private void InitColorChart()
protected void InitColorChart()
{
lock (_colorChartMutex)
{
if (_colorChart != null)
{
return;
}
_colorChart = new Color[,]
{
// Red
{
Color.FromArgb(0xFF, 0xFF, 0xEB, 0xEE),
Color.FromArgb(0xFF, 0xFF, 0xCD, 0xD2),
Color.FromArgb(0xFF, 0xEF, 0x9A, 0x9A),
Color.FromArgb(0xFF, 0xE5, 0x73, 0x73),
Color.FromArgb(0xFF, 0xEF, 0x53, 0x50),
Color.FromArgb(0xFF, 0xF4, 0x43, 0x36),
Color.FromArgb(0xFF, 0xE5, 0x39, 0x35),
Color.FromArgb(0xFF, 0xD3, 0x2F, 0x2F),
Color.FromArgb(0xFF, 0xC6, 0x28, 0x28),
Color.FromArgb(0xFF, 0xB7, 0x1C, 0x1C),
Color.FromUInt32((uint)MaterialColor.Red50),
Color.FromUInt32((uint)MaterialColor.Red100),
Color.FromUInt32((uint)MaterialColor.Red200),
Color.FromUInt32((uint)MaterialColor.Red300),
Color.FromUInt32((uint)MaterialColor.Red400),
Color.FromUInt32((uint)MaterialColor.Red500),
Color.FromUInt32((uint)MaterialColor.Red600),
Color.FromUInt32((uint)MaterialColor.Red700),
Color.FromUInt32((uint)MaterialColor.Red800),
Color.FromUInt32((uint)MaterialColor.Red900),
},
// Pink
{
Color.FromArgb(0xFF, 0xFC, 0xE4, 0xEC),
Color.FromArgb(0xFF, 0xF8, 0xBB, 0xD0),
Color.FromArgb(0xFF, 0xF4, 0x8F, 0xB1),
Color.FromArgb(0xFF, 0xF0, 0x62, 0x92),
Color.FromArgb(0xFF, 0xEC, 0x40, 0x7A),
Color.FromArgb(0xFF, 0xE9, 0x1E, 0x63),
Color.FromArgb(0xFF, 0xD8, 0x1B, 0x60),
Color.FromArgb(0xFF, 0xC2, 0x18, 0x5B),
Color.FromArgb(0xFF, 0xAD, 0x14, 0x57),
Color.FromArgb(0xFF, 0x88, 0x0E, 0x4F),
Color.FromUInt32((uint)MaterialColor.Pink50),
Color.FromUInt32((uint)MaterialColor.Pink100),
Color.FromUInt32((uint)MaterialColor.Pink200),
Color.FromUInt32((uint)MaterialColor.Pink300),
Color.FromUInt32((uint)MaterialColor.Pink400),
Color.FromUInt32((uint)MaterialColor.Pink500),
Color.FromUInt32((uint)MaterialColor.Pink600),
Color.FromUInt32((uint)MaterialColor.Pink700),
Color.FromUInt32((uint)MaterialColor.Pink800),
Color.FromUInt32((uint)MaterialColor.Pink900),
},
// Purple
{
Color.FromArgb(0xFF, 0xF3, 0xE5, 0xF5),
Color.FromArgb(0xFF, 0xE1, 0xBE, 0xE7),
Color.FromArgb(0xFF, 0xCE, 0x93, 0xD8),
Color.FromArgb(0xFF, 0xBA, 0x68, 0xC8),
Color.FromArgb(0xFF, 0xAB, 0x47, 0xBC),
Color.FromArgb(0xFF, 0x9C, 0x27, 0xB0),
Color.FromArgb(0xFF, 0x8E, 0x24, 0xAA),
Color.FromArgb(0xFF, 0x7B, 0x1F, 0xA2),
Color.FromArgb(0xFF, 0x6A, 0x1B, 0x9A),
Color.FromArgb(0xFF, 0x4A, 0x14, 0x8C),
Color.FromUInt32((uint)MaterialColor.Purple50),
Color.FromUInt32((uint)MaterialColor.Purple100),
Color.FromUInt32((uint)MaterialColor.Purple200),
Color.FromUInt32((uint)MaterialColor.Purple300),
Color.FromUInt32((uint)MaterialColor.Purple400),
Color.FromUInt32((uint)MaterialColor.Purple500),
Color.FromUInt32((uint)MaterialColor.Purple600),
Color.FromUInt32((uint)MaterialColor.Purple700),
Color.FromUInt32((uint)MaterialColor.Purple800),
Color.FromUInt32((uint)MaterialColor.Purple900),
},
// Deep Purple
{
Color.FromArgb(0xFF, 0xED, 0xE7, 0xF6),
Color.FromArgb(0xFF, 0xD1, 0xC4, 0xE9),
Color.FromArgb(0xFF, 0xB3, 0x9D, 0xDB),
Color.FromArgb(0xFF, 0x95, 0x75, 0xCD),
Color.FromArgb(0xFF, 0x7E, 0x57, 0xC2),
Color.FromArgb(0xFF, 0x67, 0x3A, 0xB7),
Color.FromArgb(0xFF, 0x5E, 0x35, 0xB1),
Color.FromArgb(0xFF, 0x51, 0x2D, 0xA8),
Color.FromArgb(0xFF, 0x45, 0x27, 0xA0),
Color.FromArgb(0xFF, 0x31, 0x1B, 0x92),
Color.FromUInt32((uint)MaterialColor.DeepPurple50),
Color.FromUInt32((uint)MaterialColor.DeepPurple100),
Color.FromUInt32((uint)MaterialColor.DeepPurple200),
Color.FromUInt32((uint)MaterialColor.DeepPurple300),
Color.FromUInt32((uint)MaterialColor.DeepPurple400),
Color.FromUInt32((uint)MaterialColor.DeepPurple500),
Color.FromUInt32((uint)MaterialColor.DeepPurple600),
Color.FromUInt32((uint)MaterialColor.DeepPurple700),
Color.FromUInt32((uint)MaterialColor.DeepPurple800),
Color.FromUInt32((uint)MaterialColor.DeepPurple900),
},
// Indigo
{
Color.FromArgb(0xFF, 0xE8, 0xEA, 0xF6),
Color.FromArgb(0xFF, 0xC5, 0xCA, 0xE9),
Color.FromArgb(0xFF, 0x9F, 0xA8, 0xDA),
Color.FromArgb(0xFF, 0x79, 0x86, 0xCB),
Color.FromArgb(0xFF, 0x5C, 0x6B, 0xC0),
Color.FromArgb(0xFF, 0x3F, 0x51, 0xB5),
Color.FromArgb(0xFF, 0x39, 0x49, 0xAB),
Color.FromArgb(0xFF, 0x30, 0x3F, 0x9F),
Color.FromArgb(0xFF, 0x28, 0x35, 0x93),
Color.FromArgb(0xFF, 0x1A, 0x23, 0x7E),
Color.FromUInt32((uint)MaterialColor.Indigo50),
Color.FromUInt32((uint)MaterialColor.Indigo100),
Color.FromUInt32((uint)MaterialColor.Indigo200),
Color.FromUInt32((uint)MaterialColor.Indigo300),
Color.FromUInt32((uint)MaterialColor.Indigo400),
Color.FromUInt32((uint)MaterialColor.Indigo500),
Color.FromUInt32((uint)MaterialColor.Indigo600),
Color.FromUInt32((uint)MaterialColor.Indigo700),
Color.FromUInt32((uint)MaterialColor.Indigo800),
Color.FromUInt32((uint)MaterialColor.Indigo900),
},
// Blue
{
Color.FromArgb(0xFF, 0xE3, 0xF2, 0xFD),
Color.FromArgb(0xFF, 0xBB, 0xDE, 0xFB),
Color.FromArgb(0xFF, 0x90, 0xCA, 0xF9),
Color.FromArgb(0xFF, 0x64, 0xB5, 0xF6),
Color.FromArgb(0xFF, 0x42, 0xA5, 0xF5),
Color.FromArgb(0xFF, 0x21, 0x96, 0xF3),
Color.FromArgb(0xFF, 0x1E, 0x88, 0xE5),
Color.FromArgb(0xFF, 0x19, 0x76, 0xD2),
Color.FromArgb(0xFF, 0x15, 0x65, 0xC0),
Color.FromArgb(0xFF, 0x0D, 0x47, 0xA1),
Color.FromUInt32((uint)MaterialColor.Blue50),
Color.FromUInt32((uint)MaterialColor.Blue100),
Color.FromUInt32((uint)MaterialColor.Blue200),
Color.FromUInt32((uint)MaterialColor.Blue300),
Color.FromUInt32((uint)MaterialColor.Blue400),
Color.FromUInt32((uint)MaterialColor.Blue500),
Color.FromUInt32((uint)MaterialColor.Blue600),
Color.FromUInt32((uint)MaterialColor.Blue700),
Color.FromUInt32((uint)MaterialColor.Blue800),
Color.FromUInt32((uint)MaterialColor.Blue900),
},
// Light Blue
{
Color.FromArgb(0xFF, 0xE1, 0xF5, 0xFE),
Color.FromArgb(0xFF, 0xB3, 0xE5, 0xFC),
Color.FromArgb(0xFF, 0x81, 0xD4, 0xFA),
Color.FromArgb(0xFF, 0x4F, 0xC3, 0xF7),
Color.FromArgb(0xFF, 0x29, 0xB6, 0xF6),
Color.FromArgb(0xFF, 0x03, 0xA9, 0xF4),
Color.FromArgb(0xFF, 0x03, 0x9B, 0xE5),
Color.FromArgb(0xFF, 0x02, 0x88, 0xD1),
Color.FromArgb(0xFF, 0x02, 0x77, 0xBD),
Color.FromArgb(0xFF, 0x01, 0x57, 0x9B),
Color.FromUInt32((uint)MaterialColor.LightBlue50),
Color.FromUInt32((uint)MaterialColor.LightBlue100),
Color.FromUInt32((uint)MaterialColor.LightBlue200),
Color.FromUInt32((uint)MaterialColor.LightBlue300),
Color.FromUInt32((uint)MaterialColor.LightBlue400),
Color.FromUInt32((uint)MaterialColor.LightBlue500),
Color.FromUInt32((uint)MaterialColor.LightBlue600),
Color.FromUInt32((uint)MaterialColor.LightBlue700),
Color.FromUInt32((uint)MaterialColor.LightBlue800),
Color.FromUInt32((uint)MaterialColor.LightBlue900),
},
// Cyan
{
Color.FromArgb(0xFF, 0xE0, 0xF7, 0xFA),
Color.FromArgb(0xFF, 0xB2, 0xEB, 0xF2),
Color.FromArgb(0xFF, 0x80, 0xDE, 0xEA),
Color.FromArgb(0xFF, 0x4D, 0xD0, 0xE1),
Color.FromArgb(0xFF, 0x26, 0xC6, 0xDA),
Color.FromArgb(0xFF, 0x00, 0xBC, 0xD4),
Color.FromArgb(0xFF, 0x00, 0xAC, 0xC1),
Color.FromArgb(0xFF, 0x00, 0x97, 0xA7),
Color.FromArgb(0xFF, 0x00, 0x83, 0x8F),
Color.FromArgb(0xFF, 0x00, 0x60, 0x64),
Color.FromUInt32((uint)MaterialColor.Cyan50),
Color.FromUInt32((uint)MaterialColor.Cyan100),
Color.FromUInt32((uint)MaterialColor.Cyan200),
Color.FromUInt32((uint)MaterialColor.Cyan300),
Color.FromUInt32((uint)MaterialColor.Cyan400),
Color.FromUInt32((uint)MaterialColor.Cyan500),
Color.FromUInt32((uint)MaterialColor.Cyan600),
Color.FromUInt32((uint)MaterialColor.Cyan700),
Color.FromUInt32((uint)MaterialColor.Cyan800),
Color.FromUInt32((uint)MaterialColor.Cyan900),
},
// Teal
{
Color.FromArgb(0xFF, 0xE0, 0xF2, 0xF1),
Color.FromArgb(0xFF, 0xB2, 0xDF, 0xDB),
Color.FromArgb(0xFF, 0x80, 0xCB, 0xC4),
Color.FromArgb(0xFF, 0x4D, 0xB6, 0xAC),
Color.FromArgb(0xFF, 0x26, 0xA6, 0x9A),
Color.FromArgb(0xFF, 0x00, 0x96, 0x88),
Color.FromArgb(0xFF, 0x00, 0x89, 0x7B),
Color.FromArgb(0xFF, 0x00, 0x79, 0x6B),
Color.FromArgb(0xFF, 0x00, 0x69, 0x5C),
Color.FromArgb(0xFF, 0x00, 0x4D, 0x40),
Color.FromUInt32((uint)MaterialColor.Teal50),
Color.FromUInt32((uint)MaterialColor.Teal100),
Color.FromUInt32((uint)MaterialColor.Teal200),
Color.FromUInt32((uint)MaterialColor.Teal300),
Color.FromUInt32((uint)MaterialColor.Teal400),
Color.FromUInt32((uint)MaterialColor.Teal500),
Color.FromUInt32((uint)MaterialColor.Teal600),
Color.FromUInt32((uint)MaterialColor.Teal700),
Color.FromUInt32((uint)MaterialColor.Teal800),
Color.FromUInt32((uint)MaterialColor.Teal900),
},
// Green
{
Color.FromArgb(0xFF, 0xE8, 0xF5, 0xE9),
Color.FromArgb(0xFF, 0xC8, 0xE6, 0xC9),
Color.FromArgb(0xFF, 0xA5, 0xD6, 0xA7),
Color.FromArgb(0xFF, 0x81, 0xC7, 0x84),
Color.FromArgb(0xFF, 0x66, 0xBB, 0x6A),
Color.FromArgb(0xFF, 0x4C, 0xAF, 0x50),
Color.FromArgb(0xFF, 0x43, 0xA0, 0x47),
Color.FromArgb(0xFF, 0x38, 0x8E, 0x3C),
Color.FromArgb(0xFF, 0x2E, 0x7D, 0x32),
Color.FromArgb(0xFF, 0x1B, 0x5E, 0x20),
Color.FromUInt32((uint)MaterialColor.Green50),
Color.FromUInt32((uint)MaterialColor.Green100),
Color.FromUInt32((uint)MaterialColor.Green200),
Color.FromUInt32((uint)MaterialColor.Green300),
Color.FromUInt32((uint)MaterialColor.Green400),
Color.FromUInt32((uint)MaterialColor.Green500),
Color.FromUInt32((uint)MaterialColor.Green600),
Color.FromUInt32((uint)MaterialColor.Green700),
Color.FromUInt32((uint)MaterialColor.Green800),
Color.FromUInt32((uint)MaterialColor.Green900),
},
// Light Green
{
Color.FromArgb(0xFF, 0xF1, 0xF8, 0xE9),
Color.FromArgb(0xFF, 0xDC, 0xED, 0xC8),
Color.FromArgb(0xFF, 0xC5, 0xE1, 0xA5),
Color.FromArgb(0xFF, 0xAE, 0xD5, 0x81),
Color.FromArgb(0xFF, 0x9C, 0xCC, 0x65),
Color.FromArgb(0xFF, 0x8B, 0xC3, 0x4A),
Color.FromArgb(0xFF, 0x7C, 0xB3, 0x42),
Color.FromArgb(0xFF, 0x68, 0x9F, 0x38),
Color.FromArgb(0xFF, 0x55, 0x8B, 0x2F),
Color.FromArgb(0xFF, 0x33, 0x69, 0x1E),
Color.FromUInt32((uint)MaterialColor.LightGreen50),
Color.FromUInt32((uint)MaterialColor.LightGreen100),
Color.FromUInt32((uint)MaterialColor.LightGreen200),
Color.FromUInt32((uint)MaterialColor.LightGreen300),
Color.FromUInt32((uint)MaterialColor.LightGreen400),
Color.FromUInt32((uint)MaterialColor.LightGreen500),
Color.FromUInt32((uint)MaterialColor.LightGreen600),
Color.FromUInt32((uint)MaterialColor.LightGreen700),
Color.FromUInt32((uint)MaterialColor.LightGreen800),
Color.FromUInt32((uint)MaterialColor.LightGreen900),
},
// Lime
{
Color.FromArgb(0xFF, 0xF9, 0xFB, 0xE7),
Color.FromArgb(0xFF, 0xF0, 0xF4, 0xC3),
Color.FromArgb(0xFF, 0xE6, 0xEE, 0x9C),
Color.FromArgb(0xFF, 0xDC, 0xE7, 0x75),
Color.FromArgb(0xFF, 0xD4, 0xE1, 0x57),
Color.FromArgb(0xFF, 0xCD, 0xDC, 0x39),
Color.FromArgb(0xFF, 0xC0, 0xCA, 0x33),
Color.FromArgb(0xFF, 0xAF, 0xB4, 0x2B),
Color.FromArgb(0xFF, 0x9E, 0x9D, 0x24),
Color.FromArgb(0xFF, 0x82, 0x77, 0x17),
Color.FromUInt32((uint)MaterialColor.Lime50),
Color.FromUInt32((uint)MaterialColor.Lime100),
Color.FromUInt32((uint)MaterialColor.Lime200),
Color.FromUInt32((uint)MaterialColor.Lime300),
Color.FromUInt32((uint)MaterialColor.Lime400),
Color.FromUInt32((uint)MaterialColor.Lime500),
Color.FromUInt32((uint)MaterialColor.Lime600),
Color.FromUInt32((uint)MaterialColor.Lime700),
Color.FromUInt32((uint)MaterialColor.Lime800),
Color.FromUInt32((uint)MaterialColor.Lime900),
},
// Yellow
{
Color.FromArgb(0xFF, 0xFF, 0xFD, 0xE7),
Color.FromArgb(0xFF, 0xFF, 0xF9, 0xC4),
Color.FromArgb(0xFF, 0xFF, 0xF5, 0x9D),
Color.FromArgb(0xFF, 0xFF, 0xF1, 0x76),
Color.FromArgb(0xFF, 0xFF, 0xEE, 0x58),
Color.FromArgb(0xFF, 0xFF, 0xEB, 0x3B),
Color.FromArgb(0xFF, 0xFD, 0xD8, 0x35),
Color.FromArgb(0xFF, 0xFB, 0xC0, 0x2D),
Color.FromArgb(0xFF, 0xF9, 0xA8, 0x25),
Color.FromArgb(0xFF, 0xF5, 0x7F, 0x17),
Color.FromUInt32((uint)MaterialColor.Yellow50),
Color.FromUInt32((uint)MaterialColor.Yellow100),
Color.FromUInt32((uint)MaterialColor.Yellow200),
Color.FromUInt32((uint)MaterialColor.Yellow300),
Color.FromUInt32((uint)MaterialColor.Yellow400),
Color.FromUInt32((uint)MaterialColor.Yellow500),
Color.FromUInt32((uint)MaterialColor.Yellow600),
Color.FromUInt32((uint)MaterialColor.Yellow700),
Color.FromUInt32((uint)MaterialColor.Yellow800),
Color.FromUInt32((uint)MaterialColor.Yellow900),
},
// Amber
{
Color.FromArgb(0xFF, 0xFF, 0xF8, 0xE1),
Color.FromArgb(0xFF, 0xFF, 0xEC, 0xB3),
Color.FromArgb(0xFF, 0xFF, 0xE0, 0x82),
Color.FromArgb(0xFF, 0xFF, 0xD5, 0x4F),
Color.FromArgb(0xFF, 0xFF, 0xCA, 0x28),
Color.FromArgb(0xFF, 0xFF, 0xC1, 0x07),
Color.FromArgb(0xFF, 0xFF, 0xB3, 0x00),
Color.FromArgb(0xFF, 0xFF, 0xA0, 0x00),
Color.FromArgb(0xFF, 0xFF, 0x8F, 0x00),
Color.FromArgb(0xFF, 0xFF, 0x6F, 0x00),
Color.FromUInt32((uint)MaterialColor.Amber50),
Color.FromUInt32((uint)MaterialColor.Amber100),
Color.FromUInt32((uint)MaterialColor.Amber200),
Color.FromUInt32((uint)MaterialColor.Amber300),
Color.FromUInt32((uint)MaterialColor.Amber400),
Color.FromUInt32((uint)MaterialColor.Amber500),
Color.FromUInt32((uint)MaterialColor.Amber600),
Color.FromUInt32((uint)MaterialColor.Amber700),
Color.FromUInt32((uint)MaterialColor.Amber800),
Color.FromUInt32((uint)MaterialColor.Amber900),
},
// Orange
{
Color.FromArgb(0xFF, 0xFF, 0xF3, 0xE0),
Color.FromArgb(0xFF, 0xFF, 0xE0, 0xB2),
Color.FromArgb(0xFF, 0xFF, 0xCC, 0x80),
Color.FromArgb(0xFF, 0xFF, 0xB7, 0x4D),
Color.FromArgb(0xFF, 0xFF, 0xA7, 0x26),
Color.FromArgb(0xFF, 0xFF, 0x98, 0x00),
Color.FromArgb(0xFF, 0xFB, 0x8C, 0x00),
Color.FromArgb(0xFF, 0xF5, 0x7C, 0x00),
Color.FromArgb(0xFF, 0xEF, 0x6C, 0x00),
Color.FromArgb(0xFF, 0xE6, 0x51, 0x00),
Color.FromUInt32((uint)MaterialColor.Orange50),
Color.FromUInt32((uint)MaterialColor.Orange100),
Color.FromUInt32((uint)MaterialColor.Orange200),
Color.FromUInt32((uint)MaterialColor.Orange300),
Color.FromUInt32((uint)MaterialColor.Orange400),
Color.FromUInt32((uint)MaterialColor.Orange500),
Color.FromUInt32((uint)MaterialColor.Orange600),
Color.FromUInt32((uint)MaterialColor.Orange700),
Color.FromUInt32((uint)MaterialColor.Orange800),
Color.FromUInt32((uint)MaterialColor.Orange900),
},
// Deep Orange
{
Color.FromArgb(0xFF, 0xFB, 0xE9, 0xE7),
Color.FromArgb(0xFF, 0xFF, 0xCC, 0xBC),
Color.FromArgb(0xFF, 0xFF, 0xAB, 0x91),
Color.FromArgb(0xFF, 0xFF, 0x8A, 0x65),
Color.FromArgb(0xFF, 0xFF, 0x70, 0x43),
Color.FromArgb(0xFF, 0xFF, 0x57, 0x22),
Color.FromArgb(0xFF, 0xF4, 0x51, 0x1E),
Color.FromArgb(0xFF, 0xE6, 0x4A, 0x19),
Color.FromArgb(0xFF, 0xD8, 0x43, 0x15),
Color.FromArgb(0xFF, 0xBF, 0x36, 0x0C),
Color.FromUInt32((uint)MaterialColor.DeepOrange50),
Color.FromUInt32((uint)MaterialColor.DeepOrange100),
Color.FromUInt32((uint)MaterialColor.DeepOrange200),
Color.FromUInt32((uint)MaterialColor.DeepOrange300),
Color.FromUInt32((uint)MaterialColor.DeepOrange400),
Color.FromUInt32((uint)MaterialColor.DeepOrange500),
Color.FromUInt32((uint)MaterialColor.DeepOrange600),
Color.FromUInt32((uint)MaterialColor.DeepOrange700),
Color.FromUInt32((uint)MaterialColor.DeepOrange800),
Color.FromUInt32((uint)MaterialColor.DeepOrange900),
},
// Brown
{
Color.FromArgb(0xFF, 0xEF, 0xEB, 0xE9),
Color.FromArgb(0xFF, 0xD7, 0xCC, 0xC8),
Color.FromArgb(0xFF, 0xBC, 0xAA, 0xA4),
Color.FromArgb(0xFF, 0xA1, 0x88, 0x7F),
Color.FromArgb(0xFF, 0x8D, 0x6E, 0x63),
Color.FromArgb(0xFF, 0x79, 0x55, 0x48),
Color.FromArgb(0xFF, 0x6D, 0x4C, 0x41),
Color.FromArgb(0xFF, 0x5D, 0x40, 0x37),
Color.FromArgb(0xFF, 0x4E, 0x34, 0x2E),
Color.FromArgb(0xFF, 0x3E, 0x27, 0x23),
Color.FromUInt32((uint)MaterialColor.Brown50),
Color.FromUInt32((uint)MaterialColor.Brown100),
Color.FromUInt32((uint)MaterialColor.Brown200),
Color.FromUInt32((uint)MaterialColor.Brown300),
Color.FromUInt32((uint)MaterialColor.Brown400),
Color.FromUInt32((uint)MaterialColor.Brown500),
Color.FromUInt32((uint)MaterialColor.Brown600),
Color.FromUInt32((uint)MaterialColor.Brown700),
Color.FromUInt32((uint)MaterialColor.Brown800),
Color.FromUInt32((uint)MaterialColor.Brown900),
},
// Gray
{
Color.FromArgb(0xFF, 0xFA, 0xFA, 0xFA),
Color.FromArgb(0xFF, 0xF5, 0xF5, 0xF5),
Color.FromArgb(0xFF, 0xEE, 0xEE, 0xEE),
Color.FromArgb(0xFF, 0xE0, 0xE0, 0xE0),
Color.FromArgb(0xFF, 0xBD, 0xBD, 0xBD),
Color.FromArgb(0xFF, 0x9E, 0x9E, 0x9E),
Color.FromArgb(0xFF, 0x75, 0x75, 0x75),
Color.FromArgb(0xFF, 0x61, 0x61, 0x61),
Color.FromArgb(0xFF, 0x42, 0x42, 0x42),
Color.FromArgb(0xFF, 0x21, 0x21, 0x21),
Color.FromUInt32((uint)MaterialColor.Gray50),
Color.FromUInt32((uint)MaterialColor.Gray100),
Color.FromUInt32((uint)MaterialColor.Gray200),
Color.FromUInt32((uint)MaterialColor.Gray300),
Color.FromUInt32((uint)MaterialColor.Gray400),
Color.FromUInt32((uint)MaterialColor.Gray500),
Color.FromUInt32((uint)MaterialColor.Gray600),
Color.FromUInt32((uint)MaterialColor.Gray700),
Color.FromUInt32((uint)MaterialColor.Gray800),
Color.FromUInt32((uint)MaterialColor.Gray900),
},
// Blue Gray
{
Color.FromArgb(0xFF, 0xEC, 0xEF, 0xF1),
Color.FromArgb(0xFF, 0xCF, 0xD8, 0xDC),
Color.FromArgb(0xFF, 0xB0, 0xBE, 0xC5),
Color.FromArgb(0xFF, 0x90, 0xA4, 0xAE),
Color.FromArgb(0xFF, 0x78, 0x90, 0x9C),
Color.FromArgb(0xFF, 0x60, 0x7D, 0x8B),
Color.FromArgb(0xFF, 0x54, 0x6E, 0x7A),
Color.FromArgb(0xFF, 0x45, 0x5A, 0x64),
Color.FromArgb(0xFF, 0x37, 0x47, 0x4F),
Color.FromArgb(0xFF, 0x26, 0x32, 0x38),
Color.FromUInt32((uint)MaterialColor.BlueGray50),
Color.FromUInt32((uint)MaterialColor.BlueGray100),
Color.FromUInt32((uint)MaterialColor.BlueGray200),
Color.FromUInt32((uint)MaterialColor.BlueGray300),
Color.FromUInt32((uint)MaterialColor.BlueGray400),
Color.FromUInt32((uint)MaterialColor.BlueGray500),
Color.FromUInt32((uint)MaterialColor.BlueGray600),
Color.FromUInt32((uint)MaterialColor.BlueGray700),
Color.FromUInt32((uint)MaterialColor.BlueGray800),
Color.FromUInt32((uint)MaterialColor.BlueGray900),
},
};
_colorChartColorCount = _colorChart.GetLength(0);
_colorChartShadeCount = _colorChart.GetLength(1);
}
return;
@ -318,29 +552,13 @@ namespace Avalonia.Controls
/// <inheritdoc/>
public int ColorCount
{
get
{
if (_colorChart == null)
{
InitColorChart();
}
return _colorChartColorCount;
}
get => 19;
}
/// <inheritdoc/>
public int ShadeCount
{
get
{
if (_colorChart == null)
{
InitColorChart();
}
return _colorChartShadeCount;
}
get => 10;
}
/// <inheritdoc/>
@ -352,8 +570,8 @@ namespace Avalonia.Controls
}
return _colorChart![
MathUtilities.Clamp(colorIndex, 0, _colorChartColorCount - 1),
MathUtilities.Clamp(shadeIndex, 0, _colorChartShadeCount - 1)];
MathUtilities.Clamp(colorIndex, 0, ColorCount - 1),
MathUtilities.Clamp(shadeIndex, 0, ShadeCount - 1)];
}
}
}

150
src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs

@ -0,0 +1,150 @@
using Avalonia.Media;
using Avalonia.Utilities;
using MaterialColor = Avalonia.Controls.MaterialColorPalette.MaterialColor;
namespace Avalonia.Controls
{
/// <summary>
/// Implements half of the <see cref="MaterialColorPalette"/> for improved usability.
/// </summary>
/// <inheritdoc cref="MaterialColorPalette"/>
public class MaterialHalfColorPalette : IColorPalette
{
protected static Color[,]? _colorChart = null;
protected static object _colorChartMutex = new object();
/// <summary>
/// Initializes all color chart colors.
/// </summary>
protected void InitColorChart()
{
lock (_colorChartMutex)
{
if (_colorChart != null)
{
return;
}
_colorChart = new Color[,]
{
// Red
{
Color.FromUInt32((uint)MaterialColor.Red50),
Color.FromUInt32((uint)MaterialColor.Red200),
Color.FromUInt32((uint)MaterialColor.Red400),
Color.FromUInt32((uint)MaterialColor.Red600),
Color.FromUInt32((uint)MaterialColor.Red800),
},
// Purple
{
Color.FromUInt32((uint)MaterialColor.Purple50),
Color.FromUInt32((uint)MaterialColor.Purple200),
Color.FromUInt32((uint)MaterialColor.Purple400),
Color.FromUInt32((uint)MaterialColor.Purple600),
Color.FromUInt32((uint)MaterialColor.Purple800),
},
// Indigo
{
Color.FromUInt32((uint)MaterialColor.Indigo50),
Color.FromUInt32((uint)MaterialColor.Indigo200),
Color.FromUInt32((uint)MaterialColor.Indigo400),
Color.FromUInt32((uint)MaterialColor.Indigo600),
Color.FromUInt32((uint)MaterialColor.Indigo800),
},
// Light Blue
{
Color.FromUInt32((uint)MaterialColor.LightBlue50),
Color.FromUInt32((uint)MaterialColor.LightBlue200),
Color.FromUInt32((uint)MaterialColor.LightBlue400),
Color.FromUInt32((uint)MaterialColor.LightBlue600),
Color.FromUInt32((uint)MaterialColor.LightBlue800),
},
// Teal
{
Color.FromUInt32((uint)MaterialColor.Teal50),
Color.FromUInt32((uint)MaterialColor.Teal200),
Color.FromUInt32((uint)MaterialColor.Teal400),
Color.FromUInt32((uint)MaterialColor.Teal600),
Color.FromUInt32((uint)MaterialColor.Teal800),
},
// Light Green
{
Color.FromUInt32((uint)MaterialColor.LightGreen50),
Color.FromUInt32((uint)MaterialColor.LightGreen200),
Color.FromUInt32((uint)MaterialColor.LightGreen400),
Color.FromUInt32((uint)MaterialColor.LightGreen600),
Color.FromUInt32((uint)MaterialColor.LightGreen800),
},
// Yellow
{
Color.FromUInt32((uint)MaterialColor.Yellow50),
Color.FromUInt32((uint)MaterialColor.Yellow200),
Color.FromUInt32((uint)MaterialColor.Yellow400),
Color.FromUInt32((uint)MaterialColor.Yellow600),
Color.FromUInt32((uint)MaterialColor.Yellow800),
},
// Orange
{
Color.FromUInt32((uint)MaterialColor.Orange50),
Color.FromUInt32((uint)MaterialColor.Orange200),
Color.FromUInt32((uint)MaterialColor.Orange400),
Color.FromUInt32((uint)MaterialColor.Orange600),
Color.FromUInt32((uint)MaterialColor.Orange800),
},
// Brown
{
Color.FromUInt32((uint)MaterialColor.Brown50),
Color.FromUInt32((uint)MaterialColor.Brown200),
Color.FromUInt32((uint)MaterialColor.Brown400),
Color.FromUInt32((uint)MaterialColor.Brown600),
Color.FromUInt32((uint)MaterialColor.Brown800),
},
// Blue Gray
{
Color.FromUInt32((uint)MaterialColor.BlueGray50),
Color.FromUInt32((uint)MaterialColor.BlueGray200),
Color.FromUInt32((uint)MaterialColor.BlueGray400),
Color.FromUInt32((uint)MaterialColor.BlueGray600),
Color.FromUInt32((uint)MaterialColor.BlueGray800),
},
};
}
return;
}
/// <inheritdoc/>
public int ColorCount
{
get => 10;
}
/// <inheritdoc/>
public int ShadeCount
{
get => 5;
}
/// <inheritdoc/>
public Color GetColor(int colorIndex, int shadeIndex)
{
if (_colorChart == null)
{
InitColorChart();
}
return _colorChart![
MathUtilities.Clamp(colorIndex, 0, ColorCount - 1),
MathUtilities.Clamp(shadeIndex, 0, ShadeCount - 1)];
}
}
}

229
src/Avalonia.Controls.ColorPicker/ColorPalettes/SixteenColorPalette.cs

@ -50,107 +50,6 @@ namespace Avalonia.Controls
}
};
/// <summary>
/// Gets the index of the default shade of colors in this palette.
/// </summary>
public const int DefaultShadeIndex = 0;
/// <summary>
/// The index in the color palette of the 'White' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int WhiteIndex = 0;
/// <summary>
/// The index in the color palette of the 'Silver' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int SilverIndex = 1;
/// <summary>
/// The index in the color palette of the 'Gray' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int GrayIndex = 2;
/// <summary>
/// The index in the color palette of the 'Black' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int BlackIndex = 3;
/// <summary>
/// The index in the color palette of the 'Red' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int RedIndex = 4;
/// <summary>
/// The index in the color palette of the 'Maroon' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int MaroonIndex = 5;
/// <summary>
/// The index in the color palette of the 'Yellow' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int YellowIndex = 6;
/// <summary>
/// The index in the color palette of the 'Olive' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int OliveIndex = 7;
/// <summary>
/// The index in the color palette of the 'Lime' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int LimeIndex = 8;
/// <summary>
/// The index in the color palette of the 'Green' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int GreenIndex = 9;
/// <summary>
/// The index in the color palette of the 'Aqua' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int AquaIndex = 10;
/// <summary>
/// The index in the color palette of the 'Teal' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int TealIndex = 11;
/// <summary>
/// The index in the color palette of the 'Blue' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int BlueIndex = 12;
/// <summary>
/// The index in the color palette of the 'Navy' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int NavyIndex = 13;
/// <summary>
/// The index in the color palette of the 'Fuchsia' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int FuchsiaIndex = 14;
/// <summary>
/// The index in the color palette of the 'Purple' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int PurpleIndex = 15;
/// <inheritdoc/>
public int ColorCount
{
@ -163,134 +62,6 @@ namespace Avalonia.Controls
get => colorChart.GetLength(1);
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFFFFFFF.
/// </summary>
public static Color White
{
get => colorChart[WhiteIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFC0C0C0.
/// </summary>
public static Color Silver
{
get => colorChart[SilverIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF808080.
/// </summary>
public static Color Gray
{
get => colorChart[GrayIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF000000.
/// </summary>
public static Color Black
{
get => colorChart[BlackIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFFF0000.
/// </summary>
public static Color Red
{
get => colorChart[RedIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF800000.
/// </summary>
public static Color Maroon
{
get => colorChart[MaroonIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFFFFF00.
/// </summary>
public static Color Yellow
{
get => colorChart[YellowIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF808000.
/// </summary>
public static Color Olive
{
get => colorChart[OliveIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF00FF00.
/// </summary>
public static Color Lime
{
get => colorChart[LimeIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF008000.
/// </summary>
public static Color Green
{
get => colorChart[GreenIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF00FFFF.
/// </summary>
public static Color Aqua
{
get => colorChart[AquaIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF008080.
/// </summary>
public static Color Teal
{
get => colorChart[TealIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF0000FF.
/// </summary>
public static Color Blue
{
get => colorChart[BlueIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF000080.
/// </summary>
public static Color Navy
{
get => colorChart[NavyIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFFF00FF.
/// </summary>
public static Color Fuchsia
{
get => colorChart[FuchsiaIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF800080.
/// </summary>
public static Color Purple
{
get => colorChart[PurpleIndex, DefaultShadeIndex];
}
/// <inheritdoc/>
public Color GetColor(int colorIndex, int shadeIndex)
{

33
src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs

@ -1,4 +1,6 @@
namespace Avalonia.Controls
using Avalonia.Controls.Primitives;
namespace Avalonia.Controls
{
/// <summary>
/// Presents a color for user editing using a spectrum, palette and component sliders within a drop down.
@ -11,8 +13,33 @@
/// </summary>
public ColorPicker() : base()
{
// Completely ignore property changes here
// The ColorView in the control template is responsible to manage this
}
/// <inheritdoc/>
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
// Until this point the ColorPicker itself is responsible to process property updates.
// This, for example, syncs Color with HsvColor and updates primitive controls.
//
// However, when the template is created, hand-off this change processing to the
// ColorView within the control template itself. Remember ColorPicker derives from
// ColorView so we don't want two instances of the same logic fighting each other.
// It is best to hand-off to the ColorView in the control template because that is the
// primary point of user-interaction for the overall control. It also simplifies binding.
//
// Keep in mind this hand-off is not possible until the template controls are created
// which is done after the ColorPicker is instantiated. The ColorPicker must still
// process updates before the template is applied to ensure all property changes in
// XAML or object initializers are handled correctly. Otherwise, there can be bugs
// such as setting the Color property doesn't work because the HsvColor is never updated
// and then the Color value is lost once the template loads (and the template ColorView
// takes over).
//
// In order to complete this hand-off, completely ignore property changes here in the
// ColorPicker. This means the ColorView in the control template is now responsible to
// process property changes and handle primary calculations.
base.ignorePropertyChanged = true;
}
}

53
src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs

@ -2,6 +2,7 @@
using Avalonia.Controls.Metadata;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Utilities;
namespace Avalonia.Controls.Primitives
@ -31,6 +32,8 @@ namespace Avalonia.Controls.Primitives
protected bool ignorePropertyChanged = false;
private WriteableBitmap? _backgroundBitmap;
/// <summary>
/// Initializes a new instance of the <see cref="ColorSlider"/> class.
/// </summary>
@ -38,6 +41,18 @@ namespace Avalonia.Controls.Primitives
{
}
/// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
}
/// <inheritdoc/>
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
}
/// <summary>
/// Updates the visual state of the control by applying latest PseudoClasses.
/// </summary>
@ -98,7 +113,7 @@ namespace Avalonia.Controls.Primitives
if (pixelWidth != 0 && pixelHeight != 0)
{
var bitmap = await ColorPickerHelpers.CreateComponentBitmapAsync(
ArrayList<byte> bgraPixelData = await ColorPickerHelpers.CreateComponentBitmapAsync(
pixelWidth,
pixelHeight,
Orientation,
@ -108,9 +123,27 @@ namespace Avalonia.Controls.Primitives
IsAlphaMaxForced,
IsSaturationValueMaxForced);
if (bitmap != null)
if (bgraPixelData != null)
{
Background = new ImageBrush(ColorPickerHelpers.CreateBitmapFromPixelData(bitmap, pixelWidth, pixelHeight));
if (_backgroundBitmap != null)
{
// TODO: CURRENTLY DISABLED DUE TO INTERMITTENT CRASHES IN SKIA/RENDERER
//
// Re-use the existing WriteableBitmap
// This assumes the height, width and byte counts are the same and must be set to null
// elsewhere if that assumption is ever not true.
// ColorPickerHelpers.UpdateBitmapFromPixelData(_backgroundBitmap, bgraPixelData);
// TODO: ALSO DISABLED DISPOSE DUE TO INTERMITTENT CRASHES
//_backgroundBitmap?.Dispose();
_backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraPixelData, pixelWidth, pixelHeight);
}
else
{
_backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraPixelData, pixelWidth, pixelHeight);
}
Background = new ImageBrush(_backgroundBitmap);
}
}
}
@ -350,11 +383,11 @@ namespace Avalonia.Controls.Primitives
return;
}
// Always keep the two color properties in sync
if (change.Property == ColorProperty)
{
ignorePropertyChanged = true;
// Always keep the two color properties in sync
HsvColor = Color.ToHsv();
SetColorToSliderValues();
@ -367,7 +400,10 @@ namespace Avalonia.Controls.Primitives
ignorePropertyChanged = false;
}
else if (change.Property == ColorModelProperty)
else if (change.Property == ColorComponentProperty ||
change.Property == ColorModelProperty ||
change.Property == IsAlphaMaxForcedProperty ||
change.Property == IsSaturationValueMaxForcedProperty)
{
ignorePropertyChanged = true;
@ -381,6 +417,7 @@ namespace Avalonia.Controls.Primitives
{
ignorePropertyChanged = true;
// Always keep the two color properties in sync
Color = HsvColor.ToRgb();
SetColorToSliderValues();
@ -399,7 +436,13 @@ namespace Avalonia.Controls.Primitives
}
else if (change.Property == BoundsProperty)
{
// If the control's overall dimensions have changed the background bitmap size also needs to change.
// This means the existing bitmap must be released to be recreated correctly in UpdateBackground().
_backgroundBitmap?.Dispose();
_backgroundBitmap = null;
UpdateBackground();
UpdatePseudoClasses();
}
else if (change.Property == ValueProperty ||
change.Property == MinimumProperty ||

24
src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs

@ -93,6 +93,14 @@ namespace Avalonia.Controls.Primitives
nameof(Shape),
ColorSpectrumShape.Box);
/// <summary>
/// Defines the <see cref="ThirdComponent"/> property.
/// </summary>
public static readonly StyledProperty<ColorComponent> ThirdComponentProperty =
AvaloniaProperty.Register<ColorSpectrum, ColorComponent>(
nameof(ThirdComponent),
ColorComponent.Component3); // Value
/// <summary>
/// Gets or sets the currently selected color in the RGB color model.
/// </summary>
@ -218,5 +226,21 @@ namespace Avalonia.Controls.Primitives
get => GetValue(ShapeProperty);
set => SetValue(ShapeProperty, value);
}
/// <summary>
/// Gets the third HSV color component that is NOT displayed by the spectrum.
/// This is automatically calculated from the <see cref="Components"/> property.
/// </summary>
/// <remarks>
/// This property should be used for any external color slider that represents the
/// third component of the color. Note that this property uses the generic
/// <see cref="ColorComponent"/> type instead of the more accurate <see cref="HsvComponent"/>
/// to allow direct usage by the generalized color sliders.
/// </remarks>
public ColorComponent ThirdComponent
{
get => GetValue(ThirdComponentProperty);
private set => SetValue(ThirdComponentProperty, value);
}
}
}

117
src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs

@ -73,6 +73,8 @@ namespace Avalonia.Controls.Primitives
private WriteableBitmap? _saturationMaximumBitmap;
private WriteableBitmap? _valueBitmap;
private WriteableBitmap? _minBitmap;
private WriteableBitmap? _maxBitmap;
// Fields used by UpdateEllipse() to ensure that it's using the data
// associated with the last call to CreateBitmapsAndColorMap(),
@ -95,7 +97,7 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// Initializes a new instance of the <see cref="ColorSpectrum"/> class.
/// </summary>
public ColorSpectrum()
public ColorSpectrum() : base()
{
_shapeFromLastBitmapCreation = Shape;
_componentsFromLastBitmapCreation = Components;
@ -171,6 +173,18 @@ namespace Avalonia.Controls.Primitives
{
base.OnAttachedToVisualTree(e);
// If the color was updated while this ColorSpectrum was not part of the visual tree,
// the selection ellipse may be in an incorrect position. This is because the spectrum
// renders based on layout scaling to avoid color banding; however, layout scale is only
// available when the control is attached to the visual tree. The ColorSpectrum's color
// may be updated from code-behind or from binding with another control when it's not
// part of the visual tree.
//
// See discussion: https://github.com/AvaloniaUI/Avalonia/discussions/9077
//
// To work-around this issue the selection ellipse is refreshed here.
UpdateEllipse();
// OnAttachedToVisualTree is called after OnApplyTemplate so events cannot be connected here
}
@ -489,6 +503,23 @@ namespace Avalonia.Controls.Primitives
}
else if (change.Property == ComponentsProperty)
{
// Calculate and update the ThirdComponent value
switch (Components)
{
case ColorSpectrumComponents.HueSaturation:
case ColorSpectrumComponents.SaturationHue:
ThirdComponent = (ColorComponent)HsvComponent.Value;
break;
case ColorSpectrumComponents.HueValue:
case ColorSpectrumComponents.ValueHue:
ThirdComponent = (ColorComponent)HsvComponent.Saturation;
break;
case ColorSpectrumComponents.SaturationValue:
case ColorSpectrumComponents.ValueSaturation:
ThirdComponent = (ColorComponent)HsvComponent.Hue;
break;
}
CreateBitmapsAndColorMap();
}
@ -588,6 +619,10 @@ namespace Avalonia.Controls.Primitives
RaiseColorChanged();
}
/// <summary>
/// Updates the selected <see cref="HsvColor"/> and <see cref="Color"/> based on a point within the color spectrum.
/// </summary>
/// <param name="point">The point on the spectrum representing the color.</param>
private void UpdateColorFromPoint(PointerPoint point)
{
// If we haven't initialized our HSV value array yet, then we should just ignore any user input -
@ -664,6 +699,9 @@ namespace Avalonia.Controls.Primitives
UpdateColor(hsvAtPoint);
}
/// <summary>
/// Updates the position of the selection ellipse on the spectrum which indicates the selected color.
/// </summary>
private void UpdateEllipse()
{
if (_selectionEllipsePanel == null)
@ -832,6 +870,8 @@ namespace Avalonia.Controls.Primitives
}
// Remember the bitmap size follows physical device pixels
// Warning: LayoutHelper.GetLayoutScale() doesn't work unless the control is visible
// This will not be true in all cases if the color is updated from another control or code-behind
var scale = LayoutHelper.GetLayoutScale(this);
Canvas.SetLeft(_selectionEllipsePanel, (xPosition / scale) - (_selectionEllipsePanel.Width / 2));
Canvas.SetTop(_selectionEllipsePanel, (yPosition / scale) - (_selectionEllipsePanel.Height / 2));
@ -973,13 +1013,13 @@ namespace Avalonia.Controls.Primitives
// The middle 4 are only needed and used in the case of hue as the third dimension.
// Saturation and luminosity need only a min and max.
List<byte> bgraMinPixelData = new List<byte>();
List<byte> bgraMiddle1PixelData = new List<byte>();
List<byte> bgraMiddle2PixelData = new List<byte>();
List<byte> bgraMiddle3PixelData = new List<byte>();
List<byte> bgraMiddle4PixelData = new List<byte>();
List<byte> bgraMaxPixelData = new List<byte>();
List<Hsv> newHsvValues = new List<Hsv>();
ArrayList<byte> bgraMinPixelData;
ArrayList<byte> bgraMiddle1PixelData;
ArrayList<byte> bgraMiddle2PixelData;
ArrayList<byte> bgraMiddle3PixelData;
ArrayList<byte> bgraMiddle4PixelData;
ArrayList<byte> bgraMaxPixelData;
List<Hsv> newHsvValues;
// In Avalonia, Bounds returns the actual device-independent pixel size of a control.
// However, this is not necessarily the size of the control rendered on a display.
@ -990,20 +1030,27 @@ namespace Avalonia.Controls.Primitives
int pixelDimension = (int)Math.Round(minDimension * scale);
var pixelCount = pixelDimension * pixelDimension;
var pixelDataSize = pixelCount * 4;
bgraMinPixelData.Capacity = pixelDataSize;
bgraMinPixelData = new ArrayList<byte>(pixelDataSize);
bgraMaxPixelData = new ArrayList<byte>(pixelDataSize);
newHsvValues = new List<Hsv>(pixelCount);
// We'll only save pixel data for the middle bitmaps if our third dimension is hue.
if (components == ColorSpectrumComponents.ValueSaturation ||
components == ColorSpectrumComponents.SaturationValue)
{
bgraMiddle1PixelData.Capacity = pixelDataSize;
bgraMiddle2PixelData.Capacity = pixelDataSize;
bgraMiddle3PixelData.Capacity = pixelDataSize;
bgraMiddle4PixelData.Capacity = pixelDataSize;
bgraMiddle1PixelData = new ArrayList<byte>(pixelDataSize);
bgraMiddle2PixelData = new ArrayList<byte>(pixelDataSize);
bgraMiddle3PixelData = new ArrayList<byte>(pixelDataSize);
bgraMiddle4PixelData = new ArrayList<byte>(pixelDataSize);
}
else
{
bgraMiddle1PixelData = new ArrayList<byte>(0);
bgraMiddle2PixelData = new ArrayList<byte>(0);
bgraMiddle3PixelData = new ArrayList<byte>(0);
bgraMiddle4PixelData = new ArrayList<byte>(0);
}
bgraMaxPixelData.Capacity = pixelDataSize;
newHsvValues.Capacity = pixelCount;
await Task.Run(() =>
{
@ -1056,28 +1103,28 @@ namespace Avalonia.Controls.Primitives
ColorSpectrumComponents components2 = Components;
WriteableBitmap minBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMinPixelData, pixelWidth, pixelHeight);
WriteableBitmap maxBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMaxPixelData, pixelWidth, pixelHeight);
_minBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMinPixelData, pixelWidth, pixelHeight);
_maxBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMaxPixelData, pixelWidth, pixelHeight);
switch (components2)
{
case ColorSpectrumComponents.HueValue:
case ColorSpectrumComponents.ValueHue:
_saturationMinimumBitmap = minBitmap;
_saturationMaximumBitmap = maxBitmap;
_saturationMinimumBitmap = _minBitmap;
_saturationMaximumBitmap = _maxBitmap;
break;
case ColorSpectrumComponents.HueSaturation:
case ColorSpectrumComponents.SaturationHue:
_valueBitmap = maxBitmap;
_valueBitmap = _maxBitmap;
break;
case ColorSpectrumComponents.ValueSaturation:
case ColorSpectrumComponents.SaturationValue:
_hueRedBitmap = minBitmap;
_hueRedBitmap = _minBitmap;
_hueYellowBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle1PixelData, pixelWidth, pixelHeight);
_hueGreenBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle2PixelData, pixelWidth, pixelHeight);
_hueCyanBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle3PixelData, pixelWidth, pixelHeight);
_hueBlueBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle4PixelData, pixelWidth, pixelHeight);
_huePurpleBitmap = maxBitmap;
_huePurpleBitmap = _maxBitmap;
break;
}
@ -1111,12 +1158,12 @@ namespace Avalonia.Controls.Primitives
double maxSaturation,
double minValue,
double maxValue,
List<byte> bgraMinPixelData,
List<byte> bgraMiddle1PixelData,
List<byte> bgraMiddle2PixelData,
List<byte> bgraMiddle3PixelData,
List<byte> bgraMiddle4PixelData,
List<byte> bgraMaxPixelData,
ArrayList<byte> bgraMinPixelData,
ArrayList<byte> bgraMiddle1PixelData,
ArrayList<byte> bgraMiddle2PixelData,
ArrayList<byte> bgraMiddle3PixelData,
ArrayList<byte> bgraMiddle4PixelData,
ArrayList<byte> bgraMaxPixelData,
List<Hsv> newHsvValues)
{
double hMin = minHue;
@ -1271,12 +1318,12 @@ namespace Avalonia.Controls.Primitives
double maxSaturation,
double minValue,
double maxValue,
List<byte> bgraMinPixelData,
List<byte> bgraMiddle1PixelData,
List<byte> bgraMiddle2PixelData,
List<byte> bgraMiddle3PixelData,
List<byte> bgraMiddle4PixelData,
List<byte> bgraMaxPixelData,
ArrayList<byte> bgraMinPixelData,
ArrayList<byte> bgraMiddle1PixelData,
ArrayList<byte> bgraMiddle2PixelData,
ArrayList<byte> bgraMiddle3PixelData,
ArrayList<byte> bgraMiddle4PixelData,
ArrayList<byte> bgraMaxPixelData,
List<Hsv> newHsvValues)
{
double hMin = minHue;

53
src/Avalonia.Controls.ColorPicker/Converters/ThirdComponentConverter.cs

@ -1,53 +0,0 @@
using System;
using System.Globalization;
using Avalonia.Data.Converters;
namespace Avalonia.Controls.Primitives.Converters
{
/// <summary>
/// Gets the third <see cref="ColorComponent"/> corresponding with a given
/// <see cref="ColorSpectrumComponents"/> that represents the other two components.
/// </summary>
/// <remarks>
/// This is a highly-specialized converter for the color picker.
/// </remarks>
public class ThirdComponentConverter : IValueConverter
{
/// <inheritdoc/>
public object? Convert(
object? value,
Type targetType,
object? parameter,
CultureInfo culture)
{
if (value is ColorSpectrumComponents components)
{
// Note: Alpha is not relevant here
switch (components)
{
case ColorSpectrumComponents.HueSaturation:
case ColorSpectrumComponents.SaturationHue:
return (ColorComponent)HsvComponent.Value;
case ColorSpectrumComponents.HueValue:
case ColorSpectrumComponents.ValueHue:
return (ColorComponent)HsvComponent.Saturation;
case ColorSpectrumComponents.SaturationValue:
case ColorSpectrumComponents.ValueSaturation:
return (ColorComponent)HsvComponent.Hue;
}
}
return AvaloniaProperty.UnsetValue;
}
/// <inheritdoc/>
public object? ConvertBack(
object? value,
Type targetType,
object? parameter,
CultureInfo culture)
{
return AvaloniaProperty.UnsetValue;
}
}
}

71
src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs

@ -0,0 +1,71 @@
namespace Avalonia.Controls.Primitives
{
/// <summary>
/// A thin wrapper over an <see cref="System.Array"/> that allows some additional list-like functionality.
/// </summary>
/// <remarks>
/// This is only for internal ColorPicker-related functionality and should not be used elsewhere.
/// It is added for performance to enjoy the simplicity of the IList.Add() method without requiring
/// an additional copy to turn a list into an array for bitmaps.
/// </remarks>
/// <typeparam name="T">The type of items in the array.</typeparam>
internal class ArrayList<T>
{
private int _nextIndex = 0;
/// <summary>
/// Initializes a new instance of the <see cref="ArrayList{T}"/> class.
/// </summary>
public ArrayList(int capacity)
{
Capacity = capacity;
Array = new T[capacity];
}
/// <summary>
/// Provides access to the underlying array by index.
/// This exists for simplification and the <see cref="Array"/> property
/// may also be used.
/// </summary>
/// <param name="i">The index of the item to get or set.</param>
/// <returns>The item at the given index.</returns>
public T this[int i]
{
get => Array[i];
set => Array[i] = value;
}
/// <summary>
/// Gets the underlying array.
/// </summary>
public T[] Array { get; private set; }
/// <summary>
/// Gets the fixed capacity/size of the array.
/// This must be set during construction.
/// </summary>
public int Capacity { get; private set; }
/// <summary>
/// Adds the given item to the array at the next available index.
/// WARNING: This must be used carefully and only once, in sequence.
/// </summary>
/// <param name="item">The item to add.</param>
public void Add(T item)
{
if (_nextIndex >= 0 &&
_nextIndex < Capacity)
{
Array[_nextIndex] = item;
_nextIndex++;
}
else
{
// If necessary an exception could be thrown here
// throw new IndexOutOfRangeException();
}
return;
}
}
}

34
src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs

@ -37,7 +37,7 @@ namespace Avalonia.Controls.Primitives
/// during calculation with the HSVA color model.
/// This will ensure colors are always discernible regardless of saturation/value.</param>
/// <returns>A new bitmap representing a gradient of color component values.</returns>
public static async Task<byte[]> CreateComponentBitmapAsync(
public static async Task<ArrayList<byte>> CreateComponentBitmapAsync(
int width,
int height,
Orientation orientation,
@ -49,14 +49,14 @@ namespace Avalonia.Controls.Primitives
{
if (width == 0 || height == 0)
{
return Array.Empty<byte>();
return new ArrayList<byte>(0);
}
var bitmap = await Task.Run<byte[]>(() =>
var bitmap = await Task.Run<ArrayList<byte>>(() =>
{
int pixelDataIndex = 0;
double componentStep;
byte[] bgraPixelData;
ArrayList<byte> bgraPixelData;
Color baseRgbColor = Colors.White;
Color rgbColor;
int bgraPixelDataHeight;
@ -64,7 +64,7 @@ namespace Avalonia.Controls.Primitives
// Allocate the buffer
// BGRA formatted color components 1 byte each (4 bytes in a pixel)
bgraPixelData = new byte[width * height * 4];
bgraPixelData = new ArrayList<byte>(width * height * 4);
bgraPixelDataHeight = height * 4;
bgraPixelDataWidth = width * 4;
@ -604,7 +604,7 @@ namespace Avalonia.Controls.Primitives
/// <param name="pixelHeight">The pixel height of the bitmap.</param>
/// <returns>A new <see cref="WriteableBitmap"/>.</returns>
public static WriteableBitmap CreateBitmapFromPixelData(
IList<byte> bgraPixelData,
ArrayList<byte> bgraPixelData,
int pixelWidth,
int pixelHeight)
{
@ -617,13 +617,31 @@ namespace Avalonia.Controls.Primitives
PixelFormat.Bgra8888,
AlphaFormat.Premul);
// Warning: This is highly questionable
using (var frameBuffer = bitmap.Lock())
{
Marshal.Copy(bgraPixelData.ToArray(), 0, frameBuffer.Address, bgraPixelData.Count);
Marshal.Copy(bgraPixelData.Array, 0, frameBuffer.Address, bgraPixelData.Array.Length);
}
return bitmap;
}
/// <summary>
/// Updates the given <see cref="WriteableBitmap"/> with new, raw BGRA pre-multiplied alpha pixel data.
/// TODO: THIS METHOD IS CURRENTLY PROVIDED AS REFERENCE BUT CAUSES INTERMITTENT CRASHES IF USED.
/// WARNING: The bitmap's width, height and byte count MUST not have changed and MUST be enforced externally.
/// </summary>
/// <param name="bitmap">The existing <see cref="WriteableBitmap"/> to update.</param>
/// <param name="bgraPixelData">The bitmap (in raw BGRA pre-multiplied alpha pixels).</param>
public static void UpdateBitmapFromPixelData(
WriteableBitmap bitmap,
ArrayList<byte> bgraPixelData)
{
using (var frameBuffer = bitmap.Lock())
{
Marshal.Copy(bgraPixelData.Array, 0, frameBuffer.Address, bgraPixelData.Array.Length);
}
return;
}
}
}

33
src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml

@ -8,7 +8,6 @@
x:CompileBindings="True">
<pc:ContrastBrushConverter x:Key="ContrastBrushConverter" />
<pc:ThirdComponentConverter x:Key="ThirdComponentConverter" />
<converters:ColorToDisplayNameConverter x:Key="ColorToDisplayNameConverter" />
<converters:ColorToHexConverter x:Key="ColorToHexConverter" />
<converters:DoNothingForNullConverter x:Key="DoNothingForNullConverter" />
@ -241,23 +240,21 @@
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<ControlTemplate>
<Border
Name="PART_LayoutRoot"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Padding="{TemplateBinding Padding}">
<Border Name="PART_LayoutRoot"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Padding="{TemplateBinding Padding}">
<Panel>
<ContentPresenter
Name="PART_ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}" />
<ContentPresenter Name="PART_ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}" />
<Border Name="PART_SelectedPipe"
Height="{DynamicResource TabItemPipeThickness}"
Margin="0,0,0,2"
@ -370,7 +367,7 @@
IsSaturationValueMaxForced="False"
Orientation="Vertical"
ColorModel="Hsva"
ColorComponent="{Binding Components, ElementName=ColorSpectrum, Converter={StaticResource ThirdComponentConverter}}"
ColorComponent="{Binding ThirdComponent, ElementName=ColorSpectrum}"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum}"
HorizontalAlignment="Center"
VerticalAlignment="Stretch"

33
src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml

@ -8,7 +8,6 @@
x:CompileBindings="True">
<pc:ContrastBrushConverter x:Key="ContrastBrushConverter" />
<pc:ThirdComponentConverter x:Key="ThirdComponentConverter" />
<converters:ColorToDisplayNameConverter x:Key="ColorToDisplayNameConverter" />
<converters:ColorToHexConverter x:Key="ColorToHexConverter" />
<converters:DoNothingForNullConverter x:Key="DoNothingForNullConverter" />
@ -215,23 +214,21 @@
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<ControlTemplate>
<Border
Name="PART_LayoutRoot"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Padding="{TemplateBinding Padding}">
<Border Name="PART_LayoutRoot"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Padding="{TemplateBinding Padding}">
<Panel>
<ContentPresenter
Name="PART_ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}" />
<ContentPresenter Name="PART_ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}" />
<Border Name="PART_SelectedPipe"
Height="2"
Margin="0,0,0,2"
@ -332,7 +329,7 @@
IsSaturationValueMaxForced="False"
Orientation="Vertical"
ColorModel="Hsva"
ColorComponent="{Binding Components, ElementName=ColorSpectrum, Converter={StaticResource ThirdComponentConverter}}"
ColorComponent="{Binding ThirdComponent, ElementName=ColorSpectrum}"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum}"
HorizontalAlignment="Center"
VerticalAlignment="Stretch"

463
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs

@ -0,0 +1,463 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Controls.Templates;
using Avalonia.Data;
namespace Avalonia.Controls
{
public partial class AutoCompleteBox
{
public static readonly StyledProperty<string?> WatermarkProperty =
TextBox.WatermarkProperty.AddOwner<AutoCompleteBox>();
/// <summary>
/// Identifies the <see cref="MinimumPrefixLength" /> property.
/// </summary>
/// <value>The identifier for the <see cref="MinimumPrefixLength" /> property.</value>
public static readonly StyledProperty<int> MinimumPrefixLengthProperty =
AvaloniaProperty.Register<AutoCompleteBox, int>(
nameof(MinimumPrefixLength), 1,
validate: IsValidMinimumPrefixLength);
/// <summary>
/// Identifies the <see cref="MinimumPopulateDelay" /> property.
/// </summary>
/// <value>The identifier for the <see cref="MinimumPopulateDelay" /> property.</value>
public static readonly StyledProperty<TimeSpan> MinimumPopulateDelayProperty =
AvaloniaProperty.Register<AutoCompleteBox, TimeSpan>(
nameof(MinimumPopulateDelay),
TimeSpan.Zero,
validate: IsValidMinimumPopulateDelay);
/// <summary>
/// Identifies the <see cref="MaxDropDownHeight" /> property.
/// </summary>
/// <value>The identifier for the <see cref="MaxDropDownHeight" /> property.</value>
public static readonly StyledProperty<double> MaxDropDownHeightProperty =
AvaloniaProperty.Register<AutoCompleteBox, double>(
nameof(MaxDropDownHeight),
double.PositiveInfinity,
validate: IsValidMaxDropDownHeight);
/// <summary>
/// Identifies the <see cref="IsTextCompletionEnabled" /> property.
/// </summary>
/// <value>The identifier for the <see cref="IsTextCompletionEnabled" /> property.</value>
public static readonly StyledProperty<bool> IsTextCompletionEnabledProperty =
AvaloniaProperty.Register<AutoCompleteBox, bool>(nameof(IsTextCompletionEnabled));
/// <summary>
/// Identifies the <see cref="ItemTemplate" /> property.
/// </summary>
/// <value>The identifier for the <see cref="ItemTemplate" /> property.</value>
public static readonly StyledProperty<IDataTemplate> ItemTemplateProperty =
AvaloniaProperty.Register<AutoCompleteBox, IDataTemplate>(nameof(ItemTemplate));
/// <summary>
/// Identifies the <see cref="IsDropDownOpen" /> property.
/// </summary>
/// <value>The identifier for the <see cref="IsDropDownOpen" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, bool> IsDropDownOpenProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, bool>(
nameof(IsDropDownOpen),
o => o.IsDropDownOpen,
(o, v) => o.IsDropDownOpen = v);
/// <summary>
/// Identifies the <see cref="SelectedItem" /> property.
/// </summary>
/// <value>The identifier the <see cref="SelectedItem" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, object?> SelectedItemProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, object?>(
nameof(SelectedItem),
o => o.SelectedItem,
(o, v) => o.SelectedItem = v,
defaultBindingMode: BindingMode.TwoWay,
enableDataValidation: true);
/// <summary>
/// Identifies the <see cref="Text" /> property.
/// </summary>
/// <value>The identifier for the <see cref="Text" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, string?> TextProperty =
TextBlock.TextProperty.AddOwnerWithDataValidation<AutoCompleteBox>(
o => o.Text,
(o, v) => o.Text = v,
defaultBindingMode: BindingMode.TwoWay,
enableDataValidation: true);
/// <summary>
/// Identifies the <see cref="SearchText" /> property.
/// </summary>
/// <value>The identifier for the <see cref="SearchText" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, string?> SearchTextProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, string?>(
nameof(SearchText),
o => o.SearchText,
unsetValue: string.Empty);
/// <summary>
/// Gets the identifier for the <see cref="FilterMode" /> property.
/// </summary>
public static readonly StyledProperty<AutoCompleteFilterMode> FilterModeProperty =
AvaloniaProperty.Register<AutoCompleteBox, AutoCompleteFilterMode>(
nameof(FilterMode),
defaultValue: AutoCompleteFilterMode.StartsWith,
validate: IsValidFilterMode);
/// <summary>
/// Identifies the <see cref="ItemFilter" /> property.
/// </summary>
/// <value>The identifier for the <see cref="ItemFilter" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteFilterPredicate<object?>?> ItemFilterProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteFilterPredicate<object?>?>(
nameof(ItemFilter),
o => o.ItemFilter,
(o, v) => o.ItemFilter = v);
/// <summary>
/// Identifies the <see cref="TextFilter" /> property.
/// </summary>
/// <value>The identifier for the <see cref="TextFilter" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteFilterPredicate<string?>?> TextFilterProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteFilterPredicate<string?>?>(
nameof(TextFilter),
o => o.TextFilter,
(o, v) => o.TextFilter = v,
unsetValue: AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith));
/// <summary>
/// Identifies the <see cref="ItemSelector" /> property.
/// </summary>
/// <value>The identifier for the <see cref="ItemSelector" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<object>?> ItemSelectorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<object>?>(
nameof(ItemSelector),
o => o.ItemSelector,
(o, v) => o.ItemSelector = v);
/// <summary>
/// Identifies the <see cref="TextSelector" /> property.
/// </summary>
/// <value>The identifier for the <see cref="TextSelector" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<string?>?> TextSelectorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<string?>?>(
nameof(TextSelector),
o => o.TextSelector,
(o, v) => o.TextSelector = v);
/// <summary>
/// Identifies the <see cref="Items" /> property.
/// </summary>
/// <value>The identifier for the <see cref="Items" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, IEnumerable?> ItemsProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, IEnumerable?>(
nameof(Items),
o => o.Items,
(o, v) => o.Items = v);
public static readonly DirectProperty<AutoCompleteBox, Func<string?, CancellationToken, Task<IEnumerable<object>>>?> AsyncPopulatorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, Func<string?, CancellationToken, Task<IEnumerable<object>>>?>(
nameof(AsyncPopulator),
o => o.AsyncPopulator,
(o, v) => o.AsyncPopulator = v);
/// <summary>
/// Gets or sets the minimum number of characters required to be entered
/// in the text box before the <see cref="AutoCompleteBox" /> displays possible matches.
/// </summary>
/// <value>
/// The minimum number of characters to be entered in the text box
/// before the <see cref="AutoCompleteBox" />
/// displays possible matches. The default is 1.
/// </value>
/// <remarks>
/// If you set MinimumPrefixLength to -1, the AutoCompleteBox will
/// not provide possible matches. There is no maximum value, but
/// setting MinimumPrefixLength to value that is too large will
/// prevent the AutoCompleteBox from providing possible matches as well.
/// </remarks>
public int MinimumPrefixLength
{
get => GetValue(MinimumPrefixLengthProperty);
set => SetValue(MinimumPrefixLengthProperty, value);
}
/// <summary>
/// Gets or sets a value indicating whether the first possible match
/// found during the filtering process will be displayed automatically
/// in the text box.
/// </summary>
/// <value>
/// True if the first possible match found will be displayed
/// automatically in the text box; otherwise, false. The default is
/// false.
/// </value>
public bool IsTextCompletionEnabled
{
get => GetValue(IsTextCompletionEnabledProperty);
set => SetValue(IsTextCompletionEnabledProperty, value);
}
/// <summary>
/// Gets or sets the <see cref="T:Avalonia.DataTemplate" /> used
/// to display each item in the drop-down portion of the control.
/// </summary>
/// <value>The <see cref="T:Avalonia.DataTemplate" /> used to
/// display each item in the drop-down. The default is null.</value>
/// <remarks>
/// You use the ItemTemplate property to specify the visualization
/// of the data objects in the drop-down portion of the AutoCompleteBox
/// control. If your AutoCompleteBox is bound to a collection and you
/// do not provide specific display instructions by using a
/// DataTemplate, the resulting UI of each item is a string
/// representation of each object in the underlying collection.
/// </remarks>
public IDataTemplate ItemTemplate
{
get => GetValue(ItemTemplateProperty);
set => SetValue(ItemTemplateProperty, value);
}
/// <summary>
/// Gets or sets the minimum delay, after text is typed
/// in the text box before the
/// <see cref="AutoCompleteBox" /> control
/// populates the list of possible matches in the drop-down.
/// </summary>
/// <value>The minimum delay, after text is typed in
/// the text box, but before the
/// <see cref="AutoCompleteBox" /> populates
/// the list of possible matches in the drop-down. The default is 0.</value>
public TimeSpan MinimumPopulateDelay
{
get => GetValue(MinimumPopulateDelayProperty);
set => SetValue(MinimumPopulateDelayProperty, value);
}
/// <summary>
/// Gets or sets the maximum height of the drop-down portion of the
/// <see cref="AutoCompleteBox" /> control.
/// </summary>
/// <value>The maximum height of the drop-down portion of the
/// <see cref="AutoCompleteBox" /> control.
/// The default is <see cref="F:System.Double.PositiveInfinity" />.</value>
/// <exception cref="T:System.ArgumentException">The specified value is less than 0.</exception>
public double MaxDropDownHeight
{
get => GetValue(MaxDropDownHeightProperty);
set => SetValue(MaxDropDownHeightProperty, value);
}
/// <summary>
/// Gets or sets a value indicating whether the drop-down portion of
/// the control is open.
/// </summary>
/// <value>
/// True if the drop-down is open; otherwise, false. The default is
/// false.
/// </value>
public bool IsDropDownOpen
{
get => _isDropDownOpen;
set => SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value);
}
/// <summary>
/// Gets or sets the <see cref="T:Avalonia.Data.Binding" /> that
/// is used to get the values for display in the text portion of
/// the <see cref="AutoCompleteBox" />
/// control.
/// </summary>
/// <value>The <see cref="T:Avalonia.Data.IBinding" /> object used
/// when binding to a collection property.</value>
[AssignBinding]
public IBinding? ValueMemberBinding
{
get => _valueBindingEvaluator?.ValueBinding;
set
{
if (ValueMemberBinding != value)
{
_valueBindingEvaluator = new BindingEvaluator<string>(value);
OnValueMemberBindingChanged(value);
}
}
}
/// <summary>
/// Gets or sets the selected item in the drop-down.
/// </summary>
/// <value>The selected item in the drop-down.</value>
/// <remarks>
/// If the IsTextCompletionEnabled property is true and text typed by
/// the user matches an item in the ItemsSource collection, which is
/// then displayed in the text box, the SelectedItem property will be
/// a null reference.
/// </remarks>
public object? SelectedItem
{
get => _selectedItem;
set => SetAndRaise(SelectedItemProperty, ref _selectedItem, value);
}
/// <summary>
/// Gets or sets the text in the text box portion of the
/// <see cref="AutoCompleteBox" /> control.
/// </summary>
/// <value>The text in the text box portion of the
/// <see cref="AutoCompleteBox" /> control.</value>
public string? Text
{
get => _text;
set => SetAndRaise(TextProperty, ref _text, value);
}
/// <summary>
/// Gets the text that is used to filter items in the
/// <see cref="Items" /> item collection.
/// </summary>
/// <value>The text that is used to filter items in the
/// <see cref="Items" /> item collection.</value>
/// <remarks>
/// The SearchText value is typically the same as the
/// Text property, but is set after the TextChanged event occurs
/// and before the Populating event.
/// </remarks>
public string? SearchText
{
get => _searchText;
private set
{
try
{
_allowWrite = true;
SetAndRaise(SearchTextProperty, ref _searchText, value);
}
finally
{
_allowWrite = false;
}
}
}
/// <summary>
/// Gets or sets how the text in the text box is used to filter items
/// specified by the <see cref="Items" />
/// property for display in the drop-down.
/// </summary>
/// <value>One of the <see cref="AutoCompleteFilterMode" />
/// values The default is <see cref="AutoCompleteFilterMode.StartsWith" />.</value>
/// <exception cref="T:System.ArgumentException">The specified value is not a valid
/// <see cref="AutoCompleteFilterMode" />.</exception>
/// <remarks>
/// Use the FilterMode property to specify how possible matches are
/// filtered. For example, possible matches can be filtered in a
/// predefined or custom way. The search mode is automatically set to
/// Custom if you set the ItemFilter property.
/// </remarks>
public AutoCompleteFilterMode FilterMode
{
get => GetValue(FilterModeProperty);
set => SetValue(FilterModeProperty, value);
}
public string? Watermark
{
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
}
/// <summary>
/// Gets or sets the custom method that uses user-entered text to filter
/// the items specified by the <see cref="Items" />
/// property for display in the drop-down.
/// </summary>
/// <value>The custom method that uses the user-entered text to filter
/// the items specified by the <see cref="Items" />
/// property. The default is null.</value>
/// <remarks>
/// The filter mode is automatically set to Custom if you set the
/// ItemFilter property.
/// </remarks>
public AutoCompleteFilterPredicate<object?>? ItemFilter
{
get => _itemFilter;
set => SetAndRaise(ItemFilterProperty, ref _itemFilter, value);
}
/// <summary>
/// Gets or sets the custom method that uses the user-entered text to
/// filter items specified by the <see cref="Items" />
/// property in a text-based way for display in the drop-down.
/// </summary>
/// <value>The custom method that uses the user-entered text to filter
/// items specified by the <see cref="Items" />
/// property in a text-based way for display in the drop-down.</value>
/// <remarks>
/// The search mode is automatically set to Custom if you set the
/// TextFilter property.
/// </remarks>
public AutoCompleteFilterPredicate<string?>? TextFilter
{
get => _textFilter;
set => SetAndRaise(TextFilterProperty, ref _textFilter, value);
}
/// <summary>
/// Gets or sets the custom method that combines the user-entered
/// text and one of the items specified by the <see cref="Items" />.
/// </summary>
/// <value>
/// The custom method that combines the user-entered
/// text and one of the items specified by the <see cref="Items" />.
/// </value>
public AutoCompleteSelector<object>? ItemSelector
{
get => _itemSelector;
set => SetAndRaise(ItemSelectorProperty, ref _itemSelector, value);
}
/// <summary>
/// Gets or sets the custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="Items" /> in a text-based way.
/// </summary>
/// <value>
/// The custom method that combines the user-entered
/// text and one of the items specified by the <see cref="Items" />
/// in a text-based way.
/// </value>
public AutoCompleteSelector<string?>? TextSelector
{
get => _textSelector;
set => SetAndRaise(TextSelectorProperty, ref _textSelector, value);
}
public Func<string?, CancellationToken, Task<IEnumerable<object>>>? AsyncPopulator
{
get => _asyncPopulator;
set => SetAndRaise(AsyncPopulatorProperty, ref _asyncPopulator, value);
}
/// <summary>
/// Gets or sets a collection that is used to generate the items for the
/// drop-down portion of the <see cref="AutoCompleteBox" /> control.
/// </summary>
/// <value>The collection that is used to generate the items of the
/// drop-down portion of the <see cref="AutoCompleteBox" /> control.</value>
public IEnumerable? Items
{
get => _itemsEnumerable;
set => SetAndRaise(ItemsProperty, ref _itemsEnumerable, value);
}
}
}

754
src/Avalonia.Controls/AutoCompleteBox.cs → src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs

@ -26,65 +26,6 @@ using Avalonia.VisualTree;
namespace Avalonia.Controls
{
/// <summary>
/// Provides data for the
/// <see cref="E:Avalonia.Controls.AutoCompleteBox.Populated" />
/// event.
/// </summary>
public class PopulatedEventArgs : EventArgs
{
/// <summary>
/// Gets the list of possible matches added to the drop-down portion of
/// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />
/// control.
/// </summary>
/// <value>The list of possible matches added to the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" />.</value>
public IEnumerable Data { get; private set; }
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:Avalonia.Controls.PopulatedEventArgs" />.
/// </summary>
/// <param name="data">The list of possible matches added to the
/// drop-down portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</param>
public PopulatedEventArgs(IEnumerable data)
{
Data = data;
}
}
/// <summary>
/// Provides data for the
/// <see cref="E:Avalonia.Controls.AutoCompleteBox.Populating" />
/// event.
/// </summary>
public class PopulatingEventArgs : CancelEventArgs
{
/// <summary>
/// Gets the text that is used to determine which items to display in
/// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />
/// control.
/// </summary>
/// <value>The text that is used to determine which items to display in
/// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />.</value>
public string? Parameter { get; private set; }
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:Avalonia.Controls.PopulatingEventArgs" />.
/// </summary>
/// <param name="parameter">The value of the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.SearchText" />
/// property, which is used to filter items for the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</param>
public PopulatingEventArgs(string? parameter)
{
Parameter = parameter;
}
}
/// <summary>
/// Represents the filter used by the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control to
@ -100,132 +41,6 @@ namespace Avalonia.Controls
/// be either a string or an object.</typeparam>
public delegate bool AutoCompleteFilterPredicate<T>(string? search, T item);
/// <summary>
/// Specifies how text in the text box portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control is used
/// to filter items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// property for display in the drop-down.
/// </summary>
public enum AutoCompleteFilterMode
{
/// <summary>
/// Specifies that no filter is used. All items are returned.
/// </summary>
None = 0,
/// <summary>
/// Specifies a culture-sensitive, case-insensitive filter where the
/// returned items start with the specified text. The filter uses the
/// <see cref="M:System.String.StartsWith(System.String,System.StringComparison)" />
/// method, specifying
/// <see cref="P:System.StringComparer.CurrentCultureIgnoreCase" /> as
/// the string comparison criteria.
/// </summary>
StartsWith = 1,
/// <summary>
/// Specifies a culture-sensitive, case-sensitive filter where the
/// returned items start with the specified text. The filter uses the
/// <see cref="M:System.String.StartsWith(System.String,System.StringComparison)" />
/// method, specifying
/// <see cref="P:System.StringComparer.CurrentCulture" /> as the string
/// comparison criteria.
/// </summary>
StartsWithCaseSensitive = 2,
/// <summary>
/// Specifies an ordinal, case-insensitive filter where the returned
/// items start with the specified text. The filter uses the
/// <see cref="M:System.String.StartsWith(System.String,System.StringComparison)" />
/// method, specifying
/// <see cref="P:System.StringComparer.OrdinalIgnoreCase" /> as the
/// string comparison criteria.
/// </summary>
StartsWithOrdinal = 3,
/// <summary>
/// Specifies an ordinal, case-sensitive filter where the returned items
/// start with the specified text. The filter uses the
/// <see cref="M:System.String.StartsWith(System.String,System.StringComparison)" />
/// method, specifying <see cref="P:System.StringComparer.Ordinal" /> as
/// the string comparison criteria.
/// </summary>
StartsWithOrdinalCaseSensitive = 4,
/// <summary>
/// Specifies a culture-sensitive, case-insensitive filter where the
/// returned items contain the specified text.
/// </summary>
Contains = 5,
/// <summary>
/// Specifies a culture-sensitive, case-sensitive filter where the
/// returned items contain the specified text.
/// </summary>
ContainsCaseSensitive = 6,
/// <summary>
/// Specifies an ordinal, case-insensitive filter where the returned
/// items contain the specified text.
/// </summary>
ContainsOrdinal = 7,
/// <summary>
/// Specifies an ordinal, case-sensitive filter where the returned items
/// contain the specified text.
/// </summary>
ContainsOrdinalCaseSensitive = 8,
/// <summary>
/// Specifies a culture-sensitive, case-insensitive filter where the
/// returned items equal the specified text. The filter uses the
/// <see cref="M:System.String.Equals(System.String,System.StringComparison)" />
/// method, specifying
/// <see cref="P:System.StringComparer.CurrentCultureIgnoreCase" /> as
/// the search comparison criteria.
/// </summary>
Equals = 9,
/// <summary>
/// Specifies a culture-sensitive, case-sensitive filter where the
/// returned items equal the specified text. The filter uses the
/// <see cref="M:System.String.Equals(System.String,System.StringComparison)" />
/// method, specifying
/// <see cref="P:System.StringComparer.CurrentCulture" /> as the string
/// comparison criteria.
/// </summary>
EqualsCaseSensitive = 10,
/// <summary>
/// Specifies an ordinal, case-insensitive filter where the returned
/// items equal the specified text. The filter uses the
/// <see cref="M:System.String.Equals(System.String,System.StringComparison)" />
/// method, specifying
/// <see cref="P:System.StringComparer.OrdinalIgnoreCase" /> as the
/// string comparison criteria.
/// </summary>
EqualsOrdinal = 11,
/// <summary>
/// Specifies an ordinal, case-sensitive filter where the returned items
/// equal the specified text. The filter uses the
/// <see cref="M:System.String.Equals(System.String,System.StringComparison)" />
/// method, specifying <see cref="P:System.StringComparer.Ordinal" /> as
/// the string comparison criteria.
/// </summary>
EqualsOrdinalCaseSensitive = 12,
/// <summary>
/// Specifies that a custom filter is used. This mode is used when the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextFilter" />
/// or
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemFilter" />
/// properties are set.
/// </summary>
Custom = 13,
}
/// <summary>
/// Represents the selector used by the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control to
@ -257,7 +72,7 @@ namespace Avalonia.Controls
[TemplatePart(ElementSelectionAdapter, typeof(ISelectionAdapter))]
[TemplatePart(ElementTextBox, typeof(TextBox))]
[PseudoClasses(":dropdownopen")]
public class AutoCompleteBox : TemplatedControl
public partial class AutoCompleteBox : TemplatedControl
{
/// <summary>
/// Specifies the name of the selection adapter TemplatePart.
@ -394,221 +209,22 @@ namespace Avalonia.Controls
private readonly EventHandler _populateDropDownHandler;
public static readonly RoutedEvent<SelectionChangedEventArgs> SelectionChangedEvent =
RoutedEvent.Register<SelectionChangedEventArgs>(nameof(SelectionChanged), RoutingStrategies.Bubble, typeof(AutoCompleteBox));
public static readonly StyledProperty<string?> WatermarkProperty =
TextBox.WatermarkProperty.AddOwner<AutoCompleteBox>();
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.MinimumPrefixLength" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.MinimumPrefixLength" />
/// dependency property.</value>
public static readonly StyledProperty<int> MinimumPrefixLengthProperty =
AvaloniaProperty.Register<AutoCompleteBox, int>(
nameof(MinimumPrefixLength), 1,
validate: IsValidMinimumPrefixLength);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.MinimumPopulateDelay" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.MinimumPopulateDelay" />
/// dependency property.</value>
public static readonly StyledProperty<TimeSpan> MinimumPopulateDelayProperty =
AvaloniaProperty.Register<AutoCompleteBox, TimeSpan>(
nameof(MinimumPopulateDelay),
TimeSpan.Zero,
validate: IsValidMinimumPopulateDelay);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.MaxDropDownHeight" />
/// dependency property.
///
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.MaxDropDownHeight" />
/// dependency property.</value>
public static readonly StyledProperty<double> MaxDropDownHeightProperty =
AvaloniaProperty.Register<AutoCompleteBox, double>(
nameof(MaxDropDownHeight),
double.PositiveInfinity,
validate: IsValidMaxDropDownHeight);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsTextCompletionEnabled" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsTextCompletionEnabled" />
/// dependency property.</value>
public static readonly StyledProperty<bool> IsTextCompletionEnabledProperty =
AvaloniaProperty.Register<AutoCompleteBox, bool>(nameof(IsTextCompletionEnabled));
public static readonly RoutedEvent<SelectionChangedEventArgs> SelectionChangedEvent =
RoutedEvent.Register<SelectionChangedEventArgs>(
nameof(SelectionChanged),
RoutingStrategies.Bubble,
typeof(AutoCompleteBox));
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemTemplate" />
/// dependency property.
/// Defines the <see cref="TextChanged"/> event.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemTemplate" />
/// dependency property.</value>
public static readonly StyledProperty<IDataTemplate> ItemTemplateProperty =
AvaloniaProperty.Register<AutoCompleteBox, IDataTemplate>(nameof(ItemTemplate));
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsDropDownOpen" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsDropDownOpen" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, bool> IsDropDownOpenProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, bool>(
nameof(IsDropDownOpen),
o => o.IsDropDownOpen,
(o, v) => o.IsDropDownOpen = v);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.SelectedItem" />
/// dependency property.
/// </summary>
/// <value>The identifier the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.SelectedItem" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, object?> SelectedItemProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, object?>(
nameof(SelectedItem),
o => o.SelectedItem,
(o, v) => o.SelectedItem = v,
defaultBindingMode: BindingMode.TwoWay,
enableDataValidation: true);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.Text" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.Text" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, string?> TextProperty =
TextBlock.TextProperty.AddOwnerWithDataValidation<AutoCompleteBox>(
o => o.Text,
(o, v) => o.Text = v,
defaultBindingMode: BindingMode.TwoWay,
enableDataValidation: true);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.SearchText" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.SearchText" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, string?> SearchTextProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, string?>(
nameof(SearchText),
o => o.SearchText,
unsetValue: string.Empty);
/// <summary>
/// Gets the identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.FilterMode" />
/// dependency property.
/// </summary>
public static readonly StyledProperty<AutoCompleteFilterMode> FilterModeProperty =
AvaloniaProperty.Register<AutoCompleteBox, AutoCompleteFilterMode>(
nameof(FilterMode),
defaultValue: AutoCompleteFilterMode.StartsWith,
validate: IsValidFilterMode);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemFilter" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemFilter" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteFilterPredicate<object?>?> ItemFilterProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteFilterPredicate<object?>?>(
nameof(ItemFilter),
o => o.ItemFilter,
(o, v) => o.ItemFilter = v);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextFilter" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextFilter" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteFilterPredicate<string?>?> TextFilterProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteFilterPredicate<string?>?>(
nameof(TextFilter),
o => o.TextFilter,
(o, v) => o.TextFilter = v,
unsetValue: AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith));
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemSelector" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemSelector" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<object>?> ItemSelectorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<object>?>(
nameof(ItemSelector),
o => o.ItemSelector,
(o, v) => o.ItemSelector = v);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextSelector" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextSelector" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<string?>?> TextSelectorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<string?>?>(
nameof(TextSelector),
o => o.TextSelector,
(o, v) => o.TextSelector = v);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, IEnumerable?> ItemsProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, IEnumerable?>(
nameof(Items),
o => o.Items,
(o, v) => o.Items = v);
public static readonly DirectProperty<AutoCompleteBox, Func<string?, CancellationToken, Task<IEnumerable<object>>>?> AsyncPopulatorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, Func<string?, CancellationToken, Task<IEnumerable<object>>>?>(
nameof(AsyncPopulator),
o => o.AsyncPopulator,
(o, v) => o.AsyncPopulator = v);
public static readonly RoutedEvent<TextChangedEventArgs> TextChangedEvent =
RoutedEvent.Register<AutoCompleteBox, TextChangedEventArgs>(
nameof(TextChanged),
RoutingStrategies.Bubble);
private static bool IsValidMinimumPrefixLength(int value) => value >= -1;
@ -871,315 +487,6 @@ namespace Avalonia.Controls
ClearView();
}
/// <summary>
/// Gets or sets the minimum number of characters required to be entered
/// in the text box before the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> displays
/// possible matches.
/// matches.
/// </summary>
/// <value>
/// The minimum number of characters to be entered in the text box
/// before the <see cref="T:Avalonia.Controls.AutoCompleteBox" />
/// displays possible matches. The default is 1.
/// </value>
/// <remarks>
/// If you set MinimumPrefixLength to -1, the AutoCompleteBox will
/// not provide possible matches. There is no maximum value, but
/// setting MinimumPrefixLength to value that is too large will
/// prevent the AutoCompleteBox from providing possible matches as well.
/// </remarks>
public int MinimumPrefixLength
{
get { return GetValue(MinimumPrefixLengthProperty); }
set { SetValue(MinimumPrefixLengthProperty, value); }
}
/// <summary>
/// Gets or sets a value indicating whether the first possible match
/// found during the filtering process will be displayed automatically
/// in the text box.
/// </summary>
/// <value>
/// True if the first possible match found will be displayed
/// automatically in the text box; otherwise, false. The default is
/// false.
/// </value>
public bool IsTextCompletionEnabled
{
get { return GetValue(IsTextCompletionEnabledProperty); }
set { SetValue(IsTextCompletionEnabledProperty, value); }
}
/// <summary>
/// Gets or sets the <see cref="T:Avalonia.DataTemplate" /> used
/// to display each item in the drop-down portion of the control.
/// </summary>
/// <value>The <see cref="T:Avalonia.DataTemplate" /> used to
/// display each item in the drop-down. The default is null.</value>
/// <remarks>
/// You use the ItemTemplate property to specify the visualization
/// of the data objects in the drop-down portion of the AutoCompleteBox
/// control. If your AutoCompleteBox is bound to a collection and you
/// do not provide specific display instructions by using a
/// DataTemplate, the resulting UI of each item is a string
/// representation of each object in the underlying collection.
/// </remarks>
public IDataTemplate ItemTemplate
{
get { return GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
/// <summary>
/// Gets or sets the minimum delay, after text is typed
/// in the text box before the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control
/// populates the list of possible matches in the drop-down.
/// </summary>
/// <value>The minimum delay, after text is typed in
/// the text box, but before the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> populates
/// the list of possible matches in the drop-down. The default is 0.</value>
public TimeSpan MinimumPopulateDelay
{
get { return GetValue(MinimumPopulateDelayProperty); }
set { SetValue(MinimumPopulateDelayProperty, value); }
}
/// <summary>
/// Gets or sets the maximum height of the drop-down portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.
/// </summary>
/// <value>The maximum height of the drop-down portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.
/// The default is <see cref="F:System.Double.PositiveInfinity" />.</value>
/// <exception cref="T:System.ArgumentException">The specified value is less than 0.</exception>
public double MaxDropDownHeight
{
get { return GetValue(MaxDropDownHeightProperty); }
set { SetValue(MaxDropDownHeightProperty, value); }
}
/// <summary>
/// Gets or sets a value indicating whether the drop-down portion of
/// the control is open.
/// </summary>
/// <value>
/// True if the drop-down is open; otherwise, false. The default is
/// false.
/// </value>
public bool IsDropDownOpen
{
get { return _isDropDownOpen; }
set { SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value); }
}
/// <summary>
/// Gets or sets the <see cref="T:Avalonia.Data.Binding" /> that
/// is used to get the values for display in the text portion of
/// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />
/// control.
/// </summary>
/// <value>The <see cref="T:Avalonia.Data.IBinding" /> object used
/// when binding to a collection property.</value>
[AssignBinding]
public IBinding? ValueMemberBinding
{
get { return _valueBindingEvaluator?.ValueBinding; }
set
{
if (ValueMemberBinding != value)
{
_valueBindingEvaluator = new BindingEvaluator<string>(value);
OnValueMemberBindingChanged(value);
}
}
}
/// <summary>
/// Gets or sets the selected item in the drop-down.
/// </summary>
/// <value>The selected item in the drop-down.</value>
/// <remarks>
/// If the IsTextCompletionEnabled property is true and text typed by
/// the user matches an item in the ItemsSource collection, which is
/// then displayed in the text box, the SelectedItem property will be
/// a null reference.
/// </remarks>
public object? SelectedItem
{
get { return _selectedItem; }
set { SetAndRaise(SelectedItemProperty, ref _selectedItem, value); }
}
/// <summary>
/// Gets or sets the text in the text box portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.
/// </summary>
/// <value>The text in the text box portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</value>
public string? Text
{
get { return _text; }
set { SetAndRaise(TextProperty, ref _text, value); }
}
/// <summary>
/// Gets the text that is used to filter items in the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// item collection.
/// </summary>
/// <value>The text that is used to filter items in the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// item collection.</value>
/// <remarks>
/// The SearchText value is typically the same as the
/// Text property, but is set after the TextChanged event occurs
/// and before the Populating event.
/// </remarks>
public string? SearchText
{
get { return _searchText; }
private set
{
try
{
_allowWrite = true;
SetAndRaise(SearchTextProperty, ref _searchText, value);
}
finally
{
_allowWrite = false;
}
}
}
/// <summary>
/// Gets or sets how the text in the text box is used to filter items
/// specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// property for display in the drop-down.
/// </summary>
/// <value>One of the
/// <see cref="T:Avalonia.Controls.AutoCompleteFilterMode" />
/// values The default is
/// <see cref="F:Avalonia.Controls.AutoCompleteFilterMode.StartsWith" />.</value>
/// <exception cref="T:System.ArgumentException">The specified value is
/// not a valid
/// <see cref="T:Avalonia.Controls.AutoCompleteFilterMode" />.</exception>
/// <remarks>
/// Use the FilterMode property to specify how possible matches are
/// filtered. For example, possible matches can be filtered in a
/// predefined or custom way. The search mode is automatically set to
/// Custom if you set the ItemFilter property.
/// </remarks>
public AutoCompleteFilterMode FilterMode
{
get { return GetValue(FilterModeProperty); }
set { SetValue(FilterModeProperty, value); }
}
public string? Watermark
{
get { return GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value); }
}
/// <summary>
/// Gets or sets the custom method that uses user-entered text to filter
/// the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// property for display in the drop-down.
/// </summary>
/// <value>The custom method that uses the user-entered text to filter
/// the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// property. The default is null.</value>
/// <remarks>
/// The filter mode is automatically set to Custom if you set the
/// ItemFilter property.
/// </remarks>
public AutoCompleteFilterPredicate<object?>? ItemFilter
{
get { return _itemFilter; }
set { SetAndRaise(ItemFilterProperty, ref _itemFilter, value); }
}
/// <summary>
/// Gets or sets the custom method that uses the user-entered text to
/// filter items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// property in a text-based way for display in the drop-down.
/// </summary>
/// <value>The custom method that uses the user-entered text to filter
/// items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// property in a text-based way for display in the drop-down.</value>
/// <remarks>
/// The search mode is automatically set to Custom if you set the
/// TextFilter property.
/// </remarks>
public AutoCompleteFilterPredicate<string?>? TextFilter
{
get { return _textFilter; }
set { SetAndRaise(TextFilterProperty, ref _textFilter, value); }
}
/// <summary>
/// Gets or sets the custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />.
/// </summary>
/// <value>
/// The custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />.
/// </value>
public AutoCompleteSelector<object>? ItemSelector
{
get { return _itemSelector; }
set { SetAndRaise(ItemSelectorProperty, ref _itemSelector, value); }
}
/// <summary>
/// Gets or sets the custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// in a text-based way.
/// </summary>
/// <value>
/// The custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// in a text-based way.
/// </value>
public AutoCompleteSelector<string?>? TextSelector
{
get { return _textSelector; }
set { SetAndRaise(TextSelectorProperty, ref _textSelector, value); }
}
public Func<string?, CancellationToken, Task<IEnumerable<object>>>? AsyncPopulator
{
get { return _asyncPopulator; }
set { SetAndRaise(AsyncPopulatorProperty, ref _asyncPopulator, value); }
}
/// <summary>
/// Gets or sets a collection that is used to generate the items for the
/// drop-down portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.
/// </summary>
/// <value>The collection that is used to generate the items of the
/// drop-down portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</value>
public IEnumerable? Items
{
get { return _itemsEnumerable; }
set { SetAndRaise(ItemsProperty, ref _itemsEnumerable, value); }
}
/// <summary>
/// Gets or sets the drop down popup control.
/// </summary>
@ -1190,7 +497,7 @@ namespace Avalonia.Controls
/// </summary>
private TextBox? TextBox
{
get { return _textBox; }
get => _textBox;
set
{
_textBoxSubscriptions?.Dispose();
@ -1254,7 +561,7 @@ namespace Avalonia.Controls
/// </remarks>
protected ISelectionAdapter? SelectionAdapter
{
get { return _adapter; }
get => _adapter;
set
{
if (_adapter != null)
@ -1529,10 +836,14 @@ namespace Avalonia.Controls
}
/// <summary>
/// Occurs when the text in the text box portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> changes.
/// Occurs asynchronously when the text in the <see cref="TextBox"/> portion of the
/// <see cref="AutoCompleteBox" /> changes.
/// </summary>
public event EventHandler? TextChanged;
public event EventHandler<TextChangedEventArgs>? TextChanged
{
add => AddHandler(TextChangedEvent, value);
remove => RemoveHandler(TextChangedEvent, value);
}
/// <summary>
/// Occurs when the
@ -1690,15 +1001,12 @@ namespace Avalonia.Controls
}
/// <summary>
/// Raises the
/// <see cref="E:Avalonia.Controls.AutoCompleteBox.TextChanged" />
/// event.
/// Raises the <see cref="TextChanged" /> event.
/// </summary>
/// <param name="e">A <see cref="T:Avalonia.RoutedEventArgs" />
/// that contains the event data.</param>
protected virtual void OnTextChanged(RoutedEventArgs e)
/// <param name="e">A <see cref="TextChangedEventArgs" /> that contains the event data.</param>
protected virtual void OnTextChanged(TextChangedEventArgs e)
{
TextChanged?.Invoke(this, e);
RaiseEvent(e);
}
/// <summary>
@ -1985,7 +1293,7 @@ namespace Avalonia.Controls
if (callTextChanged)
{
OnTextChanged(new RoutedEventArgs());
OnTextChanged(new TextChangedEventArgs(TextChangedEvent));
}
}
@ -2740,8 +2048,6 @@ namespace Avalonia.Controls
/// </summary>
private IBinding? _binding;
#region public T Value
/// <summary>
/// Identifies the Value dependency property.
/// </summary>
@ -2753,18 +2059,16 @@ namespace Avalonia.Controls
/// </summary>
public T Value
{
get { return GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
get => GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
#endregion public string Value
/// <summary>
/// Gets or sets the value binding.
/// </summary>
public IBinding? ValueBinding
{
get { return _binding; }
get => _binding;
set
{
_binding = value;

131
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs

@ -0,0 +1,131 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;
namespace Avalonia.Controls
{
/// <summary>
/// Specifies how text in the text box portion of the <see cref="AutoCompleteBox" />
/// control is used to filter items specified by the <see cref="AutoCompleteBox.Items" />
/// property for display in the drop-down.
/// </summary>
public enum AutoCompleteFilterMode
{
/// <summary>
/// Specifies that no filter is used. All items are returned.
/// </summary>
None = 0,
/// <summary>
/// Specifies a culture-sensitive, case-insensitive filter where the
/// returned items start with the specified text. The filter uses the
/// <see cref="String.StartsWith(String,StringComparison)" />
/// method, specifying
/// <see cref="StringComparer.CurrentCultureIgnoreCase" /> as
/// the string comparison criteria.
/// </summary>
StartsWith = 1,
/// <summary>
/// Specifies a culture-sensitive, case-sensitive filter where the
/// returned items start with the specified text. The filter uses the
/// <see cref="String.StartsWith(String,StringComparison)" />
/// method, specifying
/// <see cref="StringComparer.CurrentCulture" /> as the string
/// comparison criteria.
/// </summary>
StartsWithCaseSensitive = 2,
/// <summary>
/// Specifies an ordinal, case-insensitive filter where the returned
/// items start with the specified text. The filter uses the
/// <see cref="String.StartsWith(String,StringComparison)" />
/// method, specifying
/// <see cref="StringComparer.OrdinalIgnoreCase" /> as the
/// string comparison criteria.
/// </summary>
StartsWithOrdinal = 3,
/// <summary>
/// Specifies an ordinal, case-sensitive filter where the returned items
/// start with the specified text. The filter uses the
/// <see cref="String.StartsWith(String,StringComparison)" />
/// method, specifying <see cref="StringComparer.Ordinal" /> as
/// the string comparison criteria.
/// </summary>
StartsWithOrdinalCaseSensitive = 4,
/// <summary>
/// Specifies a culture-sensitive, case-insensitive filter where the
/// returned items contain the specified text.
/// </summary>
Contains = 5,
/// <summary>
/// Specifies a culture-sensitive, case-sensitive filter where the
/// returned items contain the specified text.
/// </summary>
ContainsCaseSensitive = 6,
/// <summary>
/// Specifies an ordinal, case-insensitive filter where the returned
/// items contain the specified text.
/// </summary>
ContainsOrdinal = 7,
/// <summary>
/// Specifies an ordinal, case-sensitive filter where the returned items
/// contain the specified text.
/// </summary>
ContainsOrdinalCaseSensitive = 8,
/// <summary>
/// Specifies a culture-sensitive, case-insensitive filter where the
/// returned items equal the specified text. The filter uses the
/// <see cref="String.Equals(String,StringComparison)" />
/// method, specifying
/// <see cref="StringComparer.CurrentCultureIgnoreCase" /> as
/// the search comparison criteria.
/// </summary>
Equals = 9,
/// <summary>
/// Specifies a culture-sensitive, case-sensitive filter where the
/// returned items equal the specified text. The filter uses the
/// <see cref="String.Equals(String,StringComparison)" />
/// method, specifying
/// <see cref="StringComparer.CurrentCulture" /> as the string
/// comparison criteria.
/// </summary>
EqualsCaseSensitive = 10,
/// <summary>
/// Specifies an ordinal, case-insensitive filter where the returned
/// items equal the specified text. The filter uses the
/// <see cref="String.Equals(String,StringComparison)" />
/// method, specifying
/// <see cref="StringComparer.OrdinalIgnoreCase" /> as the
/// string comparison criteria.
/// </summary>
EqualsOrdinal = 11,
/// <summary>
/// Specifies an ordinal, case-sensitive filter where the returned items
/// equal the specified text. The filter uses the
/// <see cref="String.Equals(String,StringComparison)" />
/// method, specifying <see cref="StringComparer.Ordinal" /> as
/// the string comparison criteria.
/// </summary>
EqualsOrdinalCaseSensitive = 12,
/// <summary>
/// Specifies that a custom filter is used. This mode is used when the
/// <see cref="AutoCompleteBox.TextFilter" /> or <see cref="AutoCompleteBox.ItemFilter" />
/// properties are set.
/// </summary>
Custom = 13,
}
}

39
src/Avalonia.Controls/AutoCompleteBox/PopulatedEventArgs.cs

@ -0,0 +1,39 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;
using System.Collections;
namespace Avalonia.Controls
{
/// <summary>
/// Provides data for the
/// <see cref="E:Avalonia.Controls.AutoCompleteBox.Populated" />
/// event.
/// </summary>
public class PopulatedEventArgs : EventArgs
{
/// <summary>
/// Gets the list of possible matches added to the drop-down portion of
/// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />
/// control.
/// </summary>
/// <value>The list of possible matches added to the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" />.</value>
public IEnumerable Data { get; private set; }
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:Avalonia.Controls.PopulatedEventArgs" />.
/// </summary>
/// <param name="data">The list of possible matches added to the
/// drop-down portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</param>
public PopulatedEventArgs(IEnumerable data)
{
Data = data;
}
}
}

39
src/Avalonia.Controls/AutoCompleteBox/PopulatingEventArgs.cs

@ -0,0 +1,39 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System.ComponentModel;
namespace Avalonia.Controls
{
/// <summary>
/// Provides data for the
/// <see cref="E:Avalonia.Controls.AutoCompleteBox.Populating" />
/// event.
/// </summary>
public class PopulatingEventArgs : CancelEventArgs
{
/// <summary>
/// Gets the text that is used to determine which items to display in
/// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />
/// control.
/// </summary>
/// <value>The text that is used to determine which items to display in
/// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />.</value>
public string? Parameter { get; private set; }
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:Avalonia.Controls.PopulatingEventArgs" />.
/// </summary>
/// <param name="parameter">The value of the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.SearchText" />
/// property, which is used to filter items for the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</param>
public PopulatingEventArgs(string? parameter)
{
Parameter = parameter;
}
}
}

2
src/Avalonia.Controls/Documents/IInlineHost.cs

@ -4,8 +4,6 @@ namespace Avalonia.Controls.Documents
{
internal interface IInlineHost : ILogical
{
void AddVisualChild(IControl child);
void Invalidate();
}
}

42
src/Avalonia.Controls/Documents/InlineRun.cs

@ -0,0 +1,42 @@
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Avalonia.Utilities;
namespace Avalonia.Controls.Documents
{
internal class EmbeddedControlRun : DrawableTextRun
{
public EmbeddedControlRun(IControl control, TextRunProperties properties)
{
Control = control;
Properties = properties;
}
public IControl Control { get; }
public override TextRunProperties? Properties { get; }
public override Size Size => Control.DesiredSize;
public override double Baseline
{
get
{
double baseline = Size.Height;
double baselineOffsetValue = Control.GetValue<double>(TextBlock.BaselineOffsetProperty);
if (!MathUtilities.IsZero(baselineOffsetValue))
{
baseline = baselineOffsetValue;
}
return -baseline;
}
}
public override void Draw(DrawingContext drawingContext, Point origin)
{
//noop
}
}
}

48
src/Avalonia.Controls/Documents/InlineUIContainer.cs

@ -3,7 +3,6 @@ using System.Text;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Avalonia.Metadata;
using Avalonia.Utilities;
namespace Avalonia.Controls.Documents
{
@ -59,56 +58,11 @@ namespace Avalonia.Controls.Documents
internal override void BuildTextRun(IList<TextRun> textRuns)
{
if(InlineHost == null)
{
return;
}
((ISetLogicalParent)Child).SetParent(InlineHost);
InlineHost.AddVisualChild(Child);
textRuns.Add(new InlineRun(Child, CreateTextRunProperties()));
textRuns.Add(new EmbeddedControlRun(Child, CreateTextRunProperties()));
}
internal override void AppendText(StringBuilder stringBuilder)
{
}
private class InlineRun : DrawableTextRun
{
public InlineRun(IControl control, TextRunProperties properties)
{
Control = control;
Properties = properties;
}
public IControl Control { get; }
public override TextRunProperties? Properties { get; }
public override Size Size => Control.DesiredSize;
public override double Baseline
{
get
{
double baseline = Size.Height;
double baselineOffsetValue = Control.GetValue<double>(TextBlock.BaselineOffsetProperty);
if (!MathUtilities.IsZero(baselineOffsetValue))
{
baseline = baselineOffsetValue;
}
return -baseline;
}
}
public override void Draw(DrawingContext drawingContext, Point origin)
{
//noop
}
}
}
}

2
src/Avalonia.Controls/Flyouts/FlyoutBase.cs

@ -597,7 +597,7 @@ namespace Avalonia.Controls.Primitives
for (int i = presenter.Classes.Count - 1; i >= 0; i--)
{
if (!classes.Contains(presenter.Classes[i]) &&
!presenter.Classes[i].Contains(":"))
!presenter.Classes[i].Contains(':'))
{
presenter.Classes.RemoveAt(i);
}

4
src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs

@ -1151,8 +1151,8 @@ namespace Avalonia.Controls
if (PIndex >= 0)
{
//stringToTest contains a "P" between 2 "'", it's considered as text, not percent
var isText = stringToTest.Substring(0, PIndex).Contains("'")
&& stringToTest.Substring(PIndex, FormatString.Length - PIndex).Contains("'");
var isText = stringToTest.Substring(0, PIndex).Contains('\'')
&& stringToTest.Substring(PIndex, FormatString.Length - PIndex).Contains('\'');
return !isText;
}

2
src/Avalonia.Controls/Primitives/RangeBase.cs

@ -175,7 +175,7 @@ namespace Avalonia.Controls.Primitives
/// <param name="value">The value.</param>
private static bool ValidateDouble(double value)
{
return !double.IsInfinity(value) || !double.IsNaN(value);
return !double.IsInfinity(value) && !double.IsNaN(value);
}
/// <summary>

88
src/Avalonia.Controls/RichTextBlock.cs

@ -61,6 +61,7 @@ namespace Avalonia.Controls
private int _selectionStart;
private int _selectionEnd;
private int _wordSelectionStart = -1;
private IReadOnlyList<TextRun>? _textRuns;
static RichTextBlock()
{
@ -277,8 +278,8 @@ namespace Avalonia.Controls
protected override void SetText(string? text)
{
var oldValue = GetText();
AddText(text);
AddText(text);
RaisePropertyChanged(TextProperty, oldValue, text);
}
@ -301,18 +302,9 @@ namespace Avalonia.Controls
ITextSource textSource;
if (HasComplexContent)
if (_textRuns != null)
{
var inlines = Inlines!;
var textRuns = new List<TextRun>();
foreach (var inline in inlines)
{
inline.BuildTextRun(textRuns);
}
textSource = new InlinesTextSource(textRuns);
textSource = new InlinesTextSource(_textRuns);
}
else
{
@ -546,27 +538,73 @@ namespace Avalonia.Controls
protected override Size MeasureOverride(Size availableSize)
{
foreach (var child in VisualChildren)
if(_textRuns != null)
{
if (child is Control control)
LogicalChildren.Clear();
VisualChildren.Clear();
_textRuns = null;
}
if (Inlines != null && Inlines.Count > 0)
{
var inlines = Inlines;
var textRuns = new List<TextRun>();
foreach (var inline in inlines)
{
inline.BuildTextRun(textRuns);
}
foreach (var textRun in textRuns)
{
control.Measure(Size.Infinity);
if (textRun is EmbeddedControlRun controlRun &&
controlRun.Control is Control control)
{
LogicalChildren.Add(control);
VisualChildren.Add(control);
control.Measure(Size.Infinity);
}
}
_textRuns = textRuns;
}
return base.MeasureOverride(availableSize);
}
protected override Size ArrangeOverride(Size finalSize)
{
foreach (var child in VisualChildren)
if (HasComplexContent)
{
if (child is Control control)
var currentY = 0.0;
foreach (var textLine in TextLayout.TextLines)
{
control.Arrange(new Rect(control.DesiredSize));
var currentX = textLine.Start;
foreach (var run in textLine.TextRuns)
{
if (run is DrawableTextRun drawable)
{
if (drawable is EmbeddedControlRun controlRun
&& controlRun.Control is Control control)
{
control.Arrange(new Rect(new Point(currentX, currentY), control.DesiredSize));
}
currentX += drawable.Size.Width;
}
}
currentY += textLine.Height;
}
}
return base.ArrangeOverride(finalSize);
}
@ -618,14 +656,6 @@ namespace Avalonia.Controls
}
}
void IInlineHost.AddVisualChild(IControl child)
{
if (child.VisualParent == null)
{
VisualChildren.Add(child);
}
}
void IInlineHost.Invalidate()
{
InvalidateTextLayout();

3
src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml

@ -3,8 +3,7 @@
xmlns:views="clr-namespace:Avalonia.Diagnostics.Views"
xmlns:diag="clr-namespace:Avalonia.Diagnostics"
Title="Avalonia DevTools"
x:Class="Avalonia.Diagnostics.Views.MainWindow"
Theme="{StaticResource {x:Type Window}}">
x:Class="Avalonia.Diagnostics.Views.MainWindow">
<Window.DataTemplates>
<diag:ViewLocator/>
</Window.DataTemplates>

5
src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs

@ -28,6 +28,11 @@ namespace Avalonia.Diagnostics.Views
{
InitializeComponent();
// Apply the SimpleTheme.Window theme; this must be done after the XAML is parsed as
// the theme is included in the MainWindow's XAML.
if (Theme is null && this.FindResource(typeof(Window)) is ControlTheme windowTheme)
Theme = windowTheme;
_keySubscription = InputManager.Instance?.Process
.OfType<RawKeyEventArgs>()
.Where(x => x.Type == RawKeyEventType.KeyDown)

14
src/Avalonia.Remote.Protocol/MetsysBson.cs

@ -1364,13 +1364,13 @@ namespace Metsys.Bson
var optionsString = ReadName();
var options = RegexOptions.None;
if (optionsString.Contains("e")) options = options | RegexOptions.ECMAScript;
if (optionsString.Contains("i")) options = options | RegexOptions.IgnoreCase;
if (optionsString.Contains("l")) options = options | RegexOptions.CultureInvariant;
if (optionsString.Contains("m")) options = options | RegexOptions.Multiline;
if (optionsString.Contains("s")) options = options | RegexOptions.Singleline;
if (optionsString.Contains("w")) options = options | RegexOptions.IgnorePatternWhitespace;
if (optionsString.Contains("x")) options = options | RegexOptions.ExplicitCapture;
if (optionsString.Contains('e')) options = options | RegexOptions.ECMAScript;
if (optionsString.Contains('i')) options = options | RegexOptions.IgnoreCase;
if (optionsString.Contains('l')) options = options | RegexOptions.CultureInvariant;
if (optionsString.Contains('m')) options = options | RegexOptions.Multiline;
if (optionsString.Contains('s')) options = options | RegexOptions.Singleline;
if (optionsString.Contains('w')) options = options | RegexOptions.IgnorePatternWhitespace;
if (optionsString.Contains('x')) options = options | RegexOptions.ExplicitCapture;
return new Regex(pattern, options);
}

6
src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml

@ -83,14 +83,14 @@
<Style Selector="^:maximized /template/ Path#RestoreButtonPath">
<Setter Property="Data" Value="M2048 410h-410v-410h-1638v1638h410v410h1638v-1638zM1434 1434h-1229v-1229h1229v1229zM1843 1843h-1229v-205h1024v-1024h205v1229z" />
</Style>
<Style Selector="^:fullscreen /template/ Path#PART_FullScreenButtonPath">
<Style Selector="^:fullscreen /template/ Path#FullScreenButtonPath">
<Setter Property="IsVisible" Value="True" />
<Setter Property="Data" Value="M205 1024h819v-819h-205v469l-674 -674l-145 145l674 674h-469v205zM1374 1229h469v-205h-819v819h205v-469l674 674l145 -145z" />
</Style>
<Style Selector="^:fullscreen /template/ Panel#PART_RestoreButton">
<Style Selector="^:fullscreen /template/ Button#PART_RestoreButton">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^:fullscreen /template/ Panel#PART_MinimiseButton">
<Style Selector="^:fullscreen /template/ Button#PART_MinimiseButton">
<Setter Property="IsVisible" Value="False" />
</Style>
</ControlTheme>

2
src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml

@ -135,7 +135,7 @@
</Style>
<!-- NormalState -->
<Style Selector="^:not(:dragging) /template/ Grid#MovingKnobs">
<Style Selector="^:not(:dragging) /template/ Grid#PART_MovingKnobs">
<Setter Property="Transitions">
<Transitions>
<DoubleTransition

6
src/Avalonia.Themes.Simple/Controls/CaptionButtons.xaml

@ -92,14 +92,14 @@
<Style Selector="^:maximized /template/ Path#RestoreButtonPath">
<Setter Property="Data" Value="M2048 410h-410v-410h-1638v1638h410v410h1638v-1638zM1434 1434h-1229v-1229h1229v1229zM1843 1843h-1229v-205h1024v-1024h205v1229z" />
</Style>
<Style Selector="^:fullscreen /template/ Path#PART_FullScreenButtonPath">
<Style Selector="^:fullscreen /template/ Path#FullScreenButtonPath">
<Setter Property="IsVisible" Value="True" />
<Setter Property="Data" Value="M205 1024h819v-819h-205v469l-674 -674l-145 145l674 674h-469v205zM1374 1229h469v-205h-819v819h205v-469l674 674l145 -145z" />
</Style>
<Style Selector="^:fullscreen /template/ Panel#PART_RestoreButton">
<Style Selector="^:fullscreen /template/ Button#PART_RestoreButton">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^:fullscreen /template/ Panel#PART_MinimiseButton">
<Style Selector="^:fullscreen /template/ Button#PART_MinimiseButton">
<Setter Property="IsVisible" Value="False" />
</Style>
</ControlTheme>

4
src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs

@ -15,7 +15,7 @@ namespace Avalonia.Markup.Xaml.Converters
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var valueStr = (string)value;
if (!valueStr.Contains(":"))
if (!valueStr.Contains(':'))
{
// shorthand seconds format (ie. "0.25")
var secs = double.Parse(valueStr, CultureInfo.InvariantCulture);
@ -25,4 +25,4 @@ namespace Avalonia.Markup.Xaml.Converters
return base.ConvertFrom(context, culture, value);
}
}
}
}

2
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs

@ -56,7 +56,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
// We need to implement compile-time merging of resource dictionaries and this
// hack can be removed.
if (previousWasControlTheme &&
parent is ResourceDictionary hack &&
parent is IResourceProvider hack &&
hack.Owner?.GetType().FullName == "Avalonia.Diagnostics.Views.MainWindow" &&
hack.Owner.TryGetResource(ResourceKey, out value))
{

36
tests/Avalonia.Controls.UnitTests/RichTextBlockTests.cs

@ -1,4 +1,6 @@
using Avalonia.Controls.Documents;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Media;
using Avalonia.UnitTests;
using Xunit;
@ -92,5 +94,39 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(target, run.Parent);
}
}
[Fact]
public void InlineUIContainer_Child_Schould_Be_Arranged()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var target = new RichTextBlock();
var button = new Button { Content = "12345678" };
button.Template = new FuncControlTemplate<Button>((parent, scope) =>
new TextBlock
{
Name = "PART_ContentPresenter",
[!TextBlock.TextProperty] = parent[!ContentControl.ContentProperty],
}.RegisterInNameScope(scope)
);
target.Inlines!.Add("123456");
target.Inlines.Add(new InlineUIContainer(button));
target.Inlines.Add("123456");
target.Measure(Size.Infinity);
Assert.True(button.IsMeasureValid);
Assert.Equal(80, button.DesiredSize.Width);
target.Arrange(new Rect(new Size(200, 50)));
Assert.True(button.IsArrangeValid);
Assert.Equal(60, button.Bounds.Left);
}
}
}
}

Loading…
Cancel
Save