diff --git a/.editorconfig b/.editorconfig
index 09f0d3e6ac..9ae52b8bbd 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -139,10 +139,14 @@ dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomme
# CA1802: Use literals where appropriate
dotnet_diagnostic.CA1802.severity = warning
-# CA1825: Avoid zero-length array allocations
-dotnet_diagnostic.CA1825.severity = warning
# CA1820: Test for empty strings using string length
dotnet_diagnostic.CA1820.severity = warning
+# CA1821: Remove empty finalizers
+dotnet_diagnostic.CA1821.severity = warning
+# CA1825: Avoid zero-length array allocations
+dotnet_diagnostic.CA1825.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
diff --git a/dirs.proj b/dirs.proj
index a2544ef951..f1eaae8a4a 100644
--- a/dirs.proj
+++ b/dirs.proj
@@ -9,21 +9,18 @@
-
-
-
-
-
-
-
+
+
+
+
-
+
diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml b/samples/ControlCatalog/Pages/ColorPickerPage.xaml
index ad54eb95fc..69ceaea328 100644
--- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml
+++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml
@@ -11,16 +11,17 @@
x:Class="ControlCatalog.Pages.ColorPickerPage">
-
-
@@ -56,8 +57,5 @@
IsAccentColorsVisible="False"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" />
-
-
diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs b/samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs
index 6e017e381f..4671bbdb7c 100644
--- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs
+++ b/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("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()
diff --git a/samples/ControlCatalog/Pages/DateTimePickerPage.xaml b/samples/ControlCatalog/Pages/DateTimePickerPage.xaml
index 29cd939520..6217d39b21 100644
--- a/samples/ControlCatalog/Pages/DateTimePickerPage.xaml
+++ b/samples/ControlCatalog/Pages/DateTimePickerPage.xaml
@@ -13,17 +13,17 @@
Margin="16"
HorizontalAlignment="Stretch"
Spacing="16">
- A simple DatePicker with a header
+ A simple DatePicker
-
+
- <DatePicker Header="Pick a date" />
+ <DatePicker/>
@@ -33,7 +33,7 @@
-
+
@@ -79,24 +79,24 @@
-
+
- A TimePicker with a header and minute increments specified.
+ A TimePicker with minute increments specified.
-
+
- <TimePicker Header="Arrival time" MinuteIncrement="15" />
+ <TimePicker MinuteIncrement="15" />
@@ -107,13 +107,13 @@
-
+
- <TimePicker ClockIdentifier="12HourClock" Header="12 hour clock" />
+ <TimePicker ClockIdentifier="12HourClock" />
@@ -124,13 +124,13 @@
-
+
- <TimePicker ClockIdentifier="24HourClock" Header="24 hour clock" />
+ <TimePicker ClockIdentifier="24HourClock" />
diff --git a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
index 674ed8e61f..5c31e138e0 100644
--- a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
+++ b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
@@ -22,7 +22,7 @@ namespace RenderDemo.Pages
public class GlyphRunControl : Control
{
- private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
+ private IGlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
private readonly Random _rand = new Random();
private ushort[] _glyphIndices = new ushort[1];
private char[] _characters = new char[1];
@@ -81,7 +81,7 @@ namespace RenderDemo.Pages
public class GlyphRunGeometryControl : Control
{
- private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
+ private IGlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
private readonly Random _rand = new Random();
private ushort[] _glyphIndices = new ushort[1];
private char[] _characters = new char[1];
diff --git a/src/Android/Avalonia.Android/AvaloniaMainActivity.cs b/src/Android/Avalonia.Android/AvaloniaMainActivity.cs
index 705fa3c59d..de8d02f188 100644
--- a/src/Android/Avalonia.Android/AvaloniaMainActivity.cs
+++ b/src/Android/Avalonia.Android/AvaloniaMainActivity.cs
@@ -9,11 +9,11 @@ using AndroidX.Lifecycle;
namespace Avalonia.Android
{
- public abstract class AvaloniaMainActivity : AppCompatActivity
+ public abstract class AvaloniaMainActivity : AppCompatActivity, IActivityResultHandler
{
internal static object ViewContent;
- internal Action ActivityResult;
+ public Action ActivityResult { get; set; }
internal AvaloniaView View;
protected override void OnCreate(Bundle savedInstanceState)
@@ -24,8 +24,6 @@ namespace Avalonia.Android
View.Content = ViewContent;
}
- View.Prepare();
-
if (Avalonia.Application.Current.ApplicationLifetime is SingleViewLifetime lifetime)
{
lifetime.View = View;
diff --git a/src/Android/Avalonia.Android/AvaloniaView.cs b/src/Android/Avalonia.Android/AvaloniaView.cs
index 94e863210b..5267843bfc 100644
--- a/src/Android/Avalonia.Android/AvaloniaView.cs
+++ b/src/Android/Avalonia.Android/AvaloniaView.cs
@@ -21,10 +21,7 @@ namespace Avalonia.Android
{
_view = new ViewImpl(this);
AddView(_view.View);
- }
- internal void Prepare ()
- {
_root = new EmbeddableControlRoot(_view);
_root.Prepare();
}
diff --git a/src/Android/Avalonia.Android/IActivityResultHandler.cs b/src/Android/Avalonia.Android/IActivityResultHandler.cs
new file mode 100644
index 0000000000..14094ee185
--- /dev/null
+++ b/src/Android/Avalonia.Android/IActivityResultHandler.cs
@@ -0,0 +1,11 @@
+using System;
+using Android.App;
+using Android.Content;
+
+namespace Avalonia.Android
+{
+ public interface IActivityResultHandler
+ {
+ public Action ActivityResult { get; set; }
+ }
+}
diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
index 7ce72aaca5..984eb775b5 100644
--- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
+++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
-
+using Android.App;
using Android.Content;
using Android.Graphics;
using Android.Runtime;
@@ -52,7 +52,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_view.Resources.DisplayMetrics.HeightPixels).ToSize(RenderScaling);
NativeControlHost = new AndroidNativeControlHostImpl(avaloniaView);
- StorageProvider = new AndroidStorageProvider((AvaloniaMainActivity)avaloniaView.Context);
+ StorageProvider = new AndroidStorageProvider((Activity)avaloniaView.Context);
}
public virtual Point GetAvaloniaPointFromEvent(MotionEvent e, int pointerIndex) =>
diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs
index ce385ebe34..6d0e6be0ad 100644
--- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs
+++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs
@@ -15,7 +15,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
internal class AndroidMotionEventsHelper : IDisposable
{
private static readonly PooledList s_intermediatePointsPooledList = new(ClearMode.Never);
- private static readonly float s_radiansToDegree = (float)(180f * Math.PI);
+ private const float RadiansToDegree = (float)(180f * Math.PI);
private readonly TouchDevice _touchDevice;
private readonly MouseDevice _mouseDevice;
private readonly PenDevice _penDevice;
@@ -223,7 +223,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
{
Position = new Point(e.GetX(index), e.GetY(index)) / _view.RenderScaling,
Pressure = Math.Min(e.GetPressure(index), 1), // android pressure can depend on the device, can be mixed up with "GetSize", may be larger than 1.0f on some devices
- Twist = e.GetOrientation(index) * s_radiansToDegree
+ Twist = e.GetOrientation(index) * RadiansToDegree
};
}
@@ -233,7 +233,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
{
Position = new Point(e.GetHistoricalX(index, pos), e.GetHistoricalY(index, pos)) / _view.RenderScaling,
Pressure = Math.Min(e.GetHistoricalPressure(index, pos), 1),
- Twist = e.GetHistoricalOrientation(index, pos) * s_radiansToDegree
+ Twist = e.GetHistoricalOrientation(index, pos) * RadiansToDegree
};
}
diff --git a/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs b/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs
index 3a1a9e76ea..62e43ff2ef 100644
--- a/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs
+++ b/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs
@@ -14,10 +14,10 @@ namespace Avalonia.Android.Platform.Storage;
internal class AndroidStorageProvider : IStorageProvider
{
- private readonly AvaloniaMainActivity _activity;
+ private readonly Activity _activity;
private int _lastRequestCode = 20000;
- public AndroidStorageProvider(AvaloniaMainActivity activity)
+ public AndroidStorageProvider(Activity activity)
{
_activity = activity;
}
@@ -119,7 +119,10 @@ internal class AndroidStorageProvider : IStorageProvider
var tcs = new TaskCompletionSource();
var currentRequestCode = _lastRequestCode++;
- _activity.ActivityResult += OnActivityResult;
+ if (_activity is IActivityResultHandler mainActivity)
+ {
+ mainActivity.ActivityResult += OnActivityResult;
+ }
_activity.StartActivityForResult(pickerIntent, currentRequestCode);
var result = await tcs.Task;
@@ -158,7 +161,11 @@ internal class AndroidStorageProvider : IStorageProvider
return;
}
- _activity.ActivityResult -= OnActivityResult;
+
+ if (_activity is IActivityResultHandler mainActivity)
+ {
+ mainActivity.ActivityResult -= OnActivityResult;
+ }
_ = tcs.TrySetResult(resultCode == Result.Ok ? data : null);
}
diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index 1cb29e4e37..402bc3a099 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -21,6 +21,9 @@
+
+
+
@@ -38,7 +41,7 @@
-
+
diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs
index fd43ced196..46ba4082fb 100644
--- a/src/Avalonia.Base/AvaloniaProperty.cs
+++ b/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.");
}
diff --git a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
index d86e723b38..62265d3c59 100644
--- a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
+++ b/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.");
}
diff --git a/src/Avalonia.Base/Compatibility/StringCompatibilityExtensions.cs b/src/Avalonia.Base/Compatibility/StringCompatibilityExtensions.cs
new file mode 100644
index 0000000000..45e41b44d6
--- /dev/null
+++ b/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
diff --git a/src/Avalonia.Base/Media/FontManager.cs b/src/Avalonia.Base/Media/FontManager.cs
index 37091b82e3..d92d003c2a 100644
--- a/src/Avalonia.Base/Media/FontManager.cs
+++ b/src/Avalonia.Base/Media/FontManager.cs
@@ -13,8 +13,8 @@ namespace Avalonia.Media
///
public sealed class FontManager
{
- private readonly ConcurrentDictionary _glyphTypefaceCache =
- new ConcurrentDictionary();
+ private readonly ConcurrentDictionary _glyphTypefaceCache =
+ new ConcurrentDictionary();
private readonly FontFamily _defaultFontFamily;
private readonly IReadOnlyList? _fontFallbacks;
@@ -81,13 +81,13 @@ namespace Avalonia.Media
PlatformImpl.GetInstalledFontFamilyNames(checkForUpdates);
///
- /// Returns a new , or an existing one if a matching exists.
+ /// Returns a new , or an existing one if a matching exists.
///
/// The typeface.
///
- /// The .
+ /// The .
///
- public GlyphTypeface GetOrAddGlyphTypeface(Typeface typeface)
+ public IGlyphTypeface GetOrAddGlyphTypeface(Typeface typeface)
{
while (true)
{
@@ -96,7 +96,7 @@ namespace Avalonia.Media
return glyphTypeface;
}
- glyphTypeface = new GlyphTypeface(typeface);
+ glyphTypeface = PlatformImpl.CreateGlyphTypeface(typeface);
if (_glyphTypefaceCache.TryAdd(typeface, glyphTypeface))
{
diff --git a/src/Avalonia.Base/Media/FontMetrics.cs b/src/Avalonia.Base/Media/FontMetrics.cs
new file mode 100644
index 0000000000..1cd01675db
--- /dev/null
+++ b/src/Avalonia.Base/Media/FontMetrics.cs
@@ -0,0 +1,58 @@
+namespace Avalonia.Media
+{
+ ///
+ /// The font metrics is holding information about a font's ascent, descent, etc. in design em units.
+ ///
+ public readonly struct FontMetrics
+ {
+ ///
+ /// Gets the font design units per em.
+ ///
+ public short DesignEmHeight { get; init; }
+
+ ///
+ /// A value indicating whether all glyphs in the font have the same advancement.
+ ///
+ public bool IsFixedPitch { get; init; }
+
+ ///
+ /// Gets the recommended distance above the baseline in design em size.
+ ///
+ public int Ascent { get; init; }
+
+ ///
+ /// Gets the recommended distance under the baseline in design em size.
+ ///
+ public int Descent { get; init; }
+
+ ///
+ /// Gets the recommended additional space between two lines of text in design em size.
+ ///
+ public int LineGap { get; init; }
+
+ ///
+ /// Gets the recommended line spacing of a formed text line.
+ ///
+ public int LineSpacing => Descent - Ascent + LineGap;
+
+ ///
+ /// Gets a value that indicates the distance of the underline from the baseline in design em size.
+ ///
+ public int UnderlinePosition { get; init; }
+
+ ///
+ /// Gets a value that indicates the thickness of the underline in design em size.
+ ///
+ public int UnderlineThickness { get; init; }
+
+ ///
+ /// Gets a value that indicates the distance of the strikethrough from the baseline in design em size.
+ ///
+ public int StrikethroughPosition { get; init; }
+
+ ///
+ /// Gets a value that indicates the thickness of the underline in design em size.
+ ///
+ public int StrikethroughThickness { get; init; }
+ }
+}
diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs
index 2289f98228..a1cb00e209 100644
--- a/src/Avalonia.Base/Media/GlyphRun.cs
+++ b/src/Avalonia.Base/Media/GlyphRun.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Drawing;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Platform;
using Avalonia.Utilities;
@@ -15,7 +16,7 @@ namespace Avalonia.Media
private static readonly IComparer s_descendingComparer = new ReverseComparer();
private IGlyphRunImpl? _glyphRunImpl;
- private GlyphTypeface _glyphTypeface;
+ private IGlyphTypeface _glyphTypeface;
private double _fontRenderingEmSize;
private int _biDiLevel;
private Point? _baselineOrigin;
@@ -42,7 +43,7 @@ namespace Avalonia.Media
/// The glyph clusters.
/// The bidi level.
public GlyphRun(
- GlyphTypeface glyphTypeface,
+ IGlyphTypeface glyphTypeface,
double fontRenderingEmSize,
ReadOnlySlice characters,
IReadOnlyList glyphIndices,
@@ -69,9 +70,9 @@ namespace Avalonia.Media
}
///
- /// Gets the for the .
+ /// Gets the for the .
///
- public GlyphTypeface GlyphTypeface => _glyphTypeface;
+ public IGlyphTypeface GlyphTypeface => _glyphTypeface;
///
/// Gets or sets the em size used for rendering the .
@@ -171,7 +172,7 @@ namespace Avalonia.Media
///
/// Gets the scale of the current
///
- internal double Scale => FontRenderingEmSize / GlyphTypeface.DesignEmHeight;
+ internal double Scale => FontRenderingEmSize / GlyphTypeface.Metrics.DesignEmHeight;
///
/// Returns true if the text direction is left-to-right. Otherwise, returns false.
@@ -612,7 +613,7 @@ namespace Avalonia.Media
/// The baseline origin.
private Point CalculateBaselineOrigin()
{
- return new Point(0, -GlyphTypeface.Ascent * Scale);
+ return new Point(0, -GlyphTypeface.Metrics.Ascent * Scale);
}
private GlyphRunMetrics CreateGlyphRunMetrics()
@@ -636,7 +637,7 @@ namespace Avalonia.Media
}
var isReversed = firstCluster > lastCluster;
- var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale;
+ var height = GlyphTypeface.Metrics.LineSpacing * Scale;
var widthIncludingTrailingWhitespace = 0d;
var trailingWhitespaceLength = GetTrailingWhitespaceLength(isReversed, out var newLineLength, out var glyphCount);
@@ -854,9 +855,87 @@ namespace Avalonia.Media
throw new InvalidOperationException();
}
+ _glyphRunImpl = CreateGlyphRunImpl();
+ }
+
+ private IGlyphRunImpl CreateGlyphRunImpl()
+ {
+ IGlyphRunImpl glyphRunImpl;
+
var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService();
+ var count = GlyphIndices.Count;
+ var scale = (float)(FontRenderingEmSize / GlyphTypeface.Metrics.DesignEmHeight);
+
+ if (GlyphOffsets == null)
+ {
+ if (GlyphTypeface.Metrics.IsFixedPitch)
+ {
+ var buffer = platformRenderInterface.AllocateGlyphRun(GlyphTypeface, (float)FontRenderingEmSize, count);
+
+ var glyphs = buffer.GlyphIndices;
+
+ for (int i = 0; i < glyphs.Length; i++)
+ {
+ glyphs[i] = GlyphIndices[i];
+ }
+
+ glyphRunImpl = buffer.Build();
+ }
+ else
+ {
+ var buffer = platformRenderInterface.AllocateHorizontalGlyphRun(GlyphTypeface, (float)FontRenderingEmSize, count);
+ var glyphs = buffer.GlyphIndices;
+ var positions = buffer.GlyphPositions;
+ var width = 0d;
+
+ for (var i = 0; i < count; i++)
+ {
+ positions[i] = (float)width;
+
+ if (GlyphAdvances == null)
+ {
+ width += GlyphTypeface.GetGlyphAdvance(GlyphIndices[i]) * scale;
+ }
+ else
+ {
+ width += GlyphAdvances[i];
+ }
+
+ glyphs[i] = GlyphIndices[i];
+ }
+
+ glyphRunImpl = buffer.Build();
+ }
+ }
+ else
+ {
+ var buffer = platformRenderInterface.AllocatePositionedGlyphRun(GlyphTypeface, (float)FontRenderingEmSize, count);
+ var glyphs = buffer.GlyphIndices;
+ var glyphPositions = buffer.GlyphPositions;
+ var currentX = 0.0;
+
+ for (var i = 0; i < count; i++)
+ {
+ var glyphOffset = GlyphOffsets[i];
+
+ glyphPositions[i] = new PointF((float)(currentX + glyphOffset.X), (float)glyphOffset.Y);
+
+ if (GlyphAdvances == null)
+ {
+ currentX += GlyphTypeface.GetGlyphAdvance(GlyphIndices[i]) * scale;
+ }
+ else
+ {
+ currentX += GlyphAdvances[i];
+ }
+
+ glyphs[i] = GlyphIndices[i];
+ }
+
+ glyphRunImpl = buffer.Build();
+ }
- _glyphRunImpl = platformRenderInterface.CreateGlyphRun(this);
+ return glyphRunImpl;
}
void IDisposable.Dispose()
diff --git a/src/Avalonia.Base/Media/GlyphTypeface.cs b/src/Avalonia.Base/Media/GlyphTypeface.cs
deleted file mode 100644
index 45ef04e77f..0000000000
--- a/src/Avalonia.Base/Media/GlyphTypeface.cs
+++ /dev/null
@@ -1,125 +0,0 @@
-using System;
-using Avalonia.Platform;
-
-namespace Avalonia.Media
-{
- public sealed class GlyphTypeface : IDisposable
- {
- public GlyphTypeface(Typeface typeface)
- : this(FontManager.Current.PlatformImpl.CreateGlyphTypeface(typeface))
- {
- }
-
- public GlyphTypeface(IGlyphTypefaceImpl platformImpl)
- {
- PlatformImpl = platformImpl;
- }
-
- public IGlyphTypefaceImpl PlatformImpl { get; }
-
- ///
- /// Gets the font design units per em.
- ///
- public short DesignEmHeight => PlatformImpl.DesignEmHeight;
-
- ///
- /// Gets the recommended distance above the baseline in design em size.
- ///
- public int Ascent => PlatformImpl.Ascent;
-
- ///
- /// Gets the recommended distance under the baseline in design em size.
- ///
- public int Descent => PlatformImpl.Descent;
-
- ///
- /// Gets the recommended additional space between two lines of text in design em size.
- ///
- public int LineGap => PlatformImpl.LineGap;
-
- ///
- /// Gets the recommended line height.
- ///
- public int LineHeight => Descent - Ascent + LineGap;
-
- ///
- /// Gets a value that indicates the distance of the underline from the baseline in design em size.
- ///
- public int UnderlinePosition => PlatformImpl.UnderlinePosition;
-
- ///
- /// Gets a value that indicates the thickness of the underline in design em size.
- ///
- public int UnderlineThickness => PlatformImpl.UnderlineThickness;
-
- ///
- /// Gets a value that indicates the distance of the strikethrough from the baseline in design em size.
- ///
- public int StrikethroughPosition => PlatformImpl.StrikethroughPosition;
-
- ///
- /// Gets a value that indicates the thickness of the underline in design em size.
- ///
- public int StrikethroughThickness => PlatformImpl.StrikethroughThickness;
-
- ///
- /// A value indicating whether all glyphs in the font have the same advancement.
- ///
- public bool IsFixedPitch => PlatformImpl.IsFixedPitch;
-
- ///
- /// Returns an glyph index for the specified codepoint.
- ///
- ///
- /// Returns a replacement glyph if a glyph isn't found.
- ///
- /// The codepoint.
- ///
- /// A glyph index.
- ///
- public ushort GetGlyph(uint codepoint) => PlatformImpl.GetGlyph(codepoint);
-
- ///
- /// Tries to get an glyph index for specified codepoint.
- ///
- /// The codepoint.
- /// A glyph index.
- ///
- /// true if an glyph index was found, false otherwise.
- ///
- public bool TryGetGlyph(uint codepoint, out ushort glyph)
- {
- glyph = PlatformImpl.GetGlyph(codepoint);
-
- return glyph != 0;
- }
-
- ///
- /// Returns an array of glyph indices. Codepoints that are not represented by the font are returned as 0.
- ///
- /// The codepoints to map.
- ///
- public ushort[] GetGlyphs(ReadOnlySpan codepoints) => PlatformImpl.GetGlyphs(codepoints);
-
- ///
- /// Returns the glyph advance for the specified glyph.
- ///
- /// The glyph.
- ///
- /// The advance.
- ///
- public int GetGlyphAdvance(ushort glyph) => PlatformImpl.GetGlyphAdvance(glyph);
-
- ///
- /// Returns an array of glyph advances in design em size.
- ///
- /// The glyph indices.
- ///
- public int[] GetGlyphAdvances(ReadOnlySpan glyphs) => PlatformImpl.GetGlyphAdvances(glyphs);
-
- void IDisposable.Dispose()
- {
- PlatformImpl?.Dispose();
- }
- }
-}
diff --git a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs b/src/Avalonia.Base/Media/IGlyphTypeface.cs
similarity index 52%
rename from src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs
rename to src/Avalonia.Base/Media/IGlyphTypeface.cs
index 415f34fb29..de2a2309ee 100644
--- a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs
+++ b/src/Avalonia.Base/Media/IGlyphTypeface.cs
@@ -1,55 +1,23 @@
using System;
using Avalonia.Metadata;
-namespace Avalonia.Platform
+namespace Avalonia.Media
{
[Unstable]
- public interface IGlyphTypefaceImpl : IDisposable
+ public interface IGlyphTypeface : IDisposable
{
///
- /// Gets the font design units per em.
+ /// Gets the number of glyphs held by this glyph typeface.
///
- short DesignEmHeight { get; }
+ int GlyphCount { get; }
///
- /// Gets the recommended distance above the baseline in design em size.
+ /// Gets the font metrics.
///
- int Ascent { get; }
-
- ///
- /// Gets the recommended distance under the baseline in design em size.
- ///
- int Descent { get; }
-
- ///
- /// Gets the recommended additional space between two lines of text in design em size.
- ///
- int LineGap { get; }
-
- ///
- /// Gets a value that indicates the distance of the underline from the baseline in design em size.
- ///
- int UnderlinePosition { get; }
-
- ///
- /// Gets a value that indicates the thickness of the underline in design em size.
- ///
- int UnderlineThickness { get; }
-
- ///
- /// Gets a value that indicates the distance of the strikethrough from the baseline in design em size.
- ///
- int StrikethroughPosition { get; }
-
- ///
- /// Gets a value that indicates the thickness of the underline in design em size.
- ///
- int StrikethroughThickness { get; }
-
- ///
- /// A value indicating whether all glyphs in the font have the same advancement.
- ///
- bool IsFixedPitch { get; }
+ ///
+ /// The font metrics.
+ ///
+ FontMetrics Metrics { get; }
///
/// Returns an glyph index for the specified codepoint.
@@ -63,6 +31,16 @@ namespace Avalonia.Platform
///
ushort GetGlyph(uint codepoint);
+ ///
+ /// Tries to get an glyph index for specified codepoint.
+ ///
+ /// The codepoint.
+ /// A glyph index.
+ ///
+ /// true if an glyph index was found, false otherwise.
+ ///
+ bool TryGetGlyph(uint codepoint, out ushort glyph);
+
///
/// Returns an array of glyph indices. Codepoints that are not represented by the font are returned as 0.
///
@@ -89,5 +67,13 @@ namespace Avalonia.Platform
/// An array of glyph advances.
///
int[] GetGlyphAdvances(ReadOnlySpan glyphs);
+
+ ///
+ /// Returns the contents of the table data for the specified tag.
+ ///
+ /// The table tag to get the data for.
+ /// The contents of the table data for the specified tag.
+ /// Returns true if the content exists, otherwise false.
+ bool TryGetTable(uint tag, out byte[] table);
}
}
diff --git a/src/Avalonia.Base/Media/TextDecoration.cs b/src/Avalonia.Base/Media/TextDecoration.cs
index 4c9764af96..0a7328125a 100644
--- a/src/Avalonia.Base/Media/TextDecoration.cs
+++ b/src/Avalonia.Base/Media/TextDecoration.cs
@@ -155,9 +155,9 @@ namespace Avalonia.Media
///
/// The drawing context.
/// The decorated run.
- /// The font metrics of the decorated run.
+ /// The font metrics of the decorated run.
/// The default brush that is used to draw the decoration.
- internal void Draw(DrawingContext drawingContext, GlyphRun glyphRun, FontMetrics fontMetrics, IBrush defaultBrush)
+ internal void Draw(DrawingContext drawingContext, GlyphRun glyphRun, TextMetrics textMetrics, IBrush defaultBrush)
{
var baselineOrigin = glyphRun.BaselineOrigin;
var thickness = StrokeThickness;
@@ -168,16 +168,16 @@ namespace Avalonia.Media
switch (Location)
{
case TextDecorationLocation.Underline:
- thickness = fontMetrics.UnderlineThickness;
+ thickness = textMetrics.UnderlineThickness;
break;
case TextDecorationLocation.Strikethrough:
- thickness = fontMetrics.StrikethroughThickness;
+ thickness = textMetrics.StrikethroughThickness;
break;
}
break;
case TextDecorationUnit.FontRenderingEmSize:
- thickness = fontMetrics.FontRenderingEmSize * thickness;
+ thickness = textMetrics.FontRenderingEmSize * thickness;
break;
}
@@ -189,17 +189,17 @@ namespace Avalonia.Media
origin += glyphRun.BaselineOrigin;
break;
case TextDecorationLocation.Strikethrough:
- origin += new Point(baselineOrigin.X, baselineOrigin.Y + fontMetrics.StrikethroughPosition);
+ origin += new Point(baselineOrigin.X, baselineOrigin.Y + textMetrics.StrikethroughPosition);
break;
case TextDecorationLocation.Underline:
- origin += new Point(baselineOrigin.X, baselineOrigin.Y + fontMetrics.UnderlinePosition);
+ origin += new Point(baselineOrigin.X, baselineOrigin.Y + textMetrics.UnderlinePosition);
break;
}
switch (StrokeOffsetUnit)
{
case TextDecorationUnit.FontRenderingEmSize:
- origin += new Point(0, StrokeOffset * fontMetrics.FontRenderingEmSize);
+ origin += new Point(0, StrokeOffset * textMetrics.FontRenderingEmSize);
break;
case TextDecorationUnit.Pixel:
origin += new Point(0, StrokeOffset);
diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs
index 47a6334e39..85924a3d32 100644
--- a/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs
@@ -8,13 +8,13 @@ namespace Avalonia.Media.TextFormatting
{
private static readonly IComparer s_clusterComparer = new CompareClusters();
- public ShapedBuffer(ReadOnlySlice text, int length, GlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel)
+ public ShapedBuffer(ReadOnlySlice text, int length, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel)
: this(text, new GlyphInfo[length], glyphTypeface, fontRenderingEmSize, bidiLevel)
{
}
- internal ShapedBuffer(ReadOnlySlice text, ArraySlice glyphInfos, GlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel)
+ internal ShapedBuffer(ReadOnlySlice text, ArraySlice glyphInfos, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel)
{
Text = text;
GlyphInfos = glyphInfos;
@@ -29,7 +29,7 @@ namespace Avalonia.Media.TextFormatting
public int Length => GlyphInfos.Length;
- public GlyphTypeface GlyphTypeface { get; }
+ public IGlyphTypeface GlyphTypeface { get; }
public double FontRenderingEmSize { get; }
diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs
index 53287a264d..21101f462c 100644
--- a/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs
@@ -1,5 +1,4 @@
using System;
-using System.Diagnostics;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Utilities;
@@ -18,7 +17,7 @@ namespace Avalonia.Media.TextFormatting
Text = shapedBuffer.Text;
Properties = properties;
TextSourceLength = Text.Length;
- FontMetrics = new FontMetrics(properties.Typeface, properties.FontRenderingEmSize);
+ TextMetrics = new TextMetrics(properties.Typeface, properties.FontRenderingEmSize);
}
public bool IsReversed { get; private set; }
@@ -36,9 +35,9 @@ namespace Avalonia.Media.TextFormatting
///
public override int TextSourceLength { get; }
- public FontMetrics FontMetrics { get; }
+ public TextMetrics TextMetrics { get; }
- public override double Baseline => -FontMetrics.Ascent;
+ public override double Baseline => -TextMetrics.Ascent;
public override Size Size => GlyphRun.Size;
@@ -89,7 +88,7 @@ namespace Avalonia.Media.TextFormatting
foreach (var textDecoration in Properties.TextDecorations)
{
- textDecoration.Draw(drawingContext, GlyphRun, FontMetrics, Properties.ForegroundBrush);
+ textDecoration.Draw(drawingContext, GlyphRun, TextMetrics, Properties.ForegroundBrush);
}
}
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
index aba8008fb9..96f88d1f44 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
@@ -1378,17 +1378,17 @@ namespace Avalonia.Media.TextFormatting
private TextLineMetrics CreateLineMetrics()
{
- var glyphTypeface = _paragraphProperties.DefaultTextRunProperties.Typeface.GlyphTypeface;
+ var fontMetrics = _paragraphProperties.DefaultTextRunProperties.Typeface.GlyphTypeface.Metrics;
var fontRenderingEmSize = _paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize;
- var scale = fontRenderingEmSize / glyphTypeface.DesignEmHeight;
+ var scale = fontRenderingEmSize / fontMetrics.DesignEmHeight;
var width = 0d;
var widthIncludingWhitespace = 0d;
var trailingWhitespaceLength = 0;
var newLineLength = 0;
- var ascent = glyphTypeface.Ascent * scale;
- var descent = glyphTypeface.Descent * scale;
- var lineGap = glyphTypeface.LineGap * scale;
+ var ascent = fontMetrics.Ascent * scale;
+ var descent = fontMetrics.Descent * scale;
+ var lineGap = fontMetrics.LineGap * scale;
var height = descent - ascent + lineGap;
@@ -1400,26 +1400,26 @@ namespace Avalonia.Media.TextFormatting
{
case ShapedTextCharacters textRun:
{
- var fontMetrics =
- new FontMetrics(textRun.Properties.Typeface, textRun.Properties.FontRenderingEmSize);
+ var textMetrics =
+ new TextMetrics(textRun.Properties.Typeface, textRun.Properties.FontRenderingEmSize);
if (fontRenderingEmSize < textRun.Properties.FontRenderingEmSize)
{
fontRenderingEmSize = textRun.Properties.FontRenderingEmSize;
- if (ascent > fontMetrics.Ascent)
+ if (ascent > textMetrics.Ascent)
{
- ascent = fontMetrics.Ascent;
+ ascent = textMetrics.Ascent;
}
- if (descent < fontMetrics.Descent)
+ if (descent < textMetrics.Descent)
{
- descent = fontMetrics.Descent;
+ descent = textMetrics.Descent;
}
- if (lineGap < fontMetrics.LineGap)
+ if (lineGap < textMetrics.LineGap)
{
- lineGap = fontMetrics.LineGap;
+ lineGap = textMetrics.LineGap;
}
if (descent - ascent + lineGap > height)
diff --git a/src/Avalonia.Base/Media/TextFormatting/FontMetrics.cs b/src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs
similarity index 69%
rename from src/Avalonia.Base/Media/TextFormatting/FontMetrics.cs
rename to src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs
index e01bba00a4..0382e66b5a 100644
--- a/src/Avalonia.Base/Media/TextFormatting/FontMetrics.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs
@@ -1,33 +1,33 @@
namespace Avalonia.Media.TextFormatting
{
///
- /// A metric that holds information about font specific measurements.
+ /// A metric that holds information about text specific measurements.
///
- public readonly struct FontMetrics
+ public readonly struct TextMetrics
{
- public FontMetrics(Typeface typeface, double fontRenderingEmSize)
+ public TextMetrics(Typeface typeface, double fontRenderingEmSize)
{
- var glyphTypeface = typeface.GlyphTypeface;
+ var fontMetrics = typeface.GlyphTypeface.Metrics;
- var scale = fontRenderingEmSize / glyphTypeface.DesignEmHeight;
+ var scale = fontRenderingEmSize / fontMetrics.DesignEmHeight;
FontRenderingEmSize = fontRenderingEmSize;
- Ascent = glyphTypeface.Ascent * scale;
+ Ascent = fontMetrics.Ascent * scale;
- Descent = glyphTypeface.Descent * scale;
+ Descent = fontMetrics.Descent * scale;
- LineGap = glyphTypeface.LineGap * scale;
+ LineGap = fontMetrics.LineGap * scale;
LineHeight = Descent - Ascent + LineGap;
- UnderlineThickness = glyphTypeface.UnderlineThickness * scale;
+ UnderlineThickness = fontMetrics.UnderlineThickness * scale;
- UnderlinePosition = glyphTypeface.UnderlinePosition * scale;
+ UnderlinePosition = fontMetrics.UnderlinePosition * scale;
- StrikethroughThickness = glyphTypeface.StrikethroughThickness * scale;
+ StrikethroughThickness = fontMetrics.StrikethroughThickness * scale;
- StrikethroughPosition = glyphTypeface.StrikethroughPosition * scale;
+ StrikethroughPosition = fontMetrics.StrikethroughPosition * scale;
}
///
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs
index 4e75bb921e..0d00bed51e 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs
@@ -8,7 +8,7 @@ namespace Avalonia.Media.TextFormatting
public readonly struct TextShaperOptions
{
public TextShaperOptions(
- GlyphTypeface typeface,
+ IGlyphTypeface typeface,
double fontRenderingEmSize = 12,
sbyte bidiLevel = 0,
CultureInfo? culture = null,
@@ -24,7 +24,7 @@ namespace Avalonia.Media.TextFormatting
///
/// Get the typeface.
///
- public GlyphTypeface Typeface { get; }
+ public IGlyphTypeface Typeface { get; }
///
/// Get the font rendering em size.
///
diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
index ce9cdde044..de40839853 100644
--- a/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
@@ -1,4 +1,5 @@
-using System.Runtime.CompilerServices;
+using System;
+using System.Runtime.CompilerServices;
using Avalonia.Utilities;
namespace Avalonia.Media.TextFormatting.Unicode
@@ -165,7 +166,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// The index to read at.
/// The count of character that were read.
///
- public static Codepoint ReadAt(ReadOnlySlice text, int index, out int count)
+ public static Codepoint ReadAt(ReadOnlySpan text, int index, out int count)
{
count = 1;
diff --git a/src/Avalonia.Base/Media/Typeface.cs b/src/Avalonia.Base/Media/Typeface.cs
index f0daa841d9..e6047bf96c 100644
--- a/src/Avalonia.Base/Media/Typeface.cs
+++ b/src/Avalonia.Base/Media/Typeface.cs
@@ -81,7 +81,7 @@ namespace Avalonia.Media
///
/// The glyph typeface.
///
- public GlyphTypeface GlyphTypeface => FontManager.Current.GetOrAddGlyphTypeface(this);
+ public IGlyphTypeface GlyphTypeface => FontManager.Current.GetOrAddGlyphTypeface(this);
public static bool operator !=(Typeface a, Typeface b)
{
diff --git a/src/Avalonia.Base/Media/UnicodeRange.cs b/src/Avalonia.Base/Media/UnicodeRange.cs
index e2338b9b26..344b85bae9 100644
--- a/src/Avalonia.Base/Media/UnicodeRange.cs
+++ b/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;
diff --git a/src/Avalonia.Base/Platform/IFontManagerImpl.cs b/src/Avalonia.Base/Platform/IFontManagerImpl.cs
index 932249bd52..cd6e64abaf 100644
--- a/src/Avalonia.Base/Platform/IFontManagerImpl.cs
+++ b/src/Avalonia.Base/Platform/IFontManagerImpl.cs
@@ -43,6 +43,6 @@ namespace Avalonia.Platform
/// 0
/// The created glyph typeface. Can be Null if it was not possible to create a glyph typeface.
///
- IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface);
+ IGlyphTypeface CreateGlyphTypeface(Typeface typeface);
}
}
diff --git a/src/Avalonia.Base/Platform/IGlyphRunBuffer.cs b/src/Avalonia.Base/Platform/IGlyphRunBuffer.cs
new file mode 100644
index 0000000000..c1fc7a5967
--- /dev/null
+++ b/src/Avalonia.Base/Platform/IGlyphRunBuffer.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Drawing;
+
+namespace Avalonia.Platform
+{
+ public interface IGlyphRunBuffer
+ {
+ Span GlyphIndices { get; }
+
+ IGlyphRunImpl Build();
+ }
+
+ public interface IHorizontalGlyphRunBuffer : IGlyphRunBuffer
+ {
+ Span GlyphPositions { get; }
+ }
+
+ public interface IPositionedGlyphRunBuffer : IGlyphRunBuffer
+ {
+ Span GlyphPositions { get; }
+ }
+}
diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
index e39a4e23df..9d0d7974b4 100644
--- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
+++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
@@ -171,11 +171,40 @@ namespace Avalonia.Platform
IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride);
///
- /// Creates a platform implementation of a glyph run.
+ /// Allocates a platform glyph run buffer.
///
- /// The glyph run.
- ///
- IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun);
+ /// The glyph typeface.
+ /// The font rendering em size.
+ /// The length.
+ /// An .
+ ///
+ /// This buffer only holds glyph indices.
+ ///
+ IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length);
+
+ ///
+ /// Allocates a horizontal platform glyph run buffer.
+ ///
+ /// The glyph typeface.
+ /// The font rendering em size.
+ /// The length.
+ /// An .
+ ///
+ /// This buffer holds glyph indices and glyph advances.
+ ///
+ IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length);
+
+ ///
+ /// Allocates a positioned platform glyph run buffer.
+ ///
+ /// The glyph typeface.
+ /// The font rendering em size.
+ /// The length.
+ /// An .
+ ///
+ /// This buffer holds glyph indices, glyph advances and glyph positions.
+ ///
+ IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length);
///
/// Gets a value indicating whether the platform directly supports rectangles with rounded corners.
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs b/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs
index b0b3982ed5..06fb526736 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs
@@ -25,7 +25,7 @@ internal class FpsCounter
// ASCII chars
private GlyphRun[] _runs = new GlyphRun[LastChar - FirstChar + 1];
- public FpsCounter(GlyphTypeface typeface)
+ public FpsCounter(IGlyphTypeface typeface)
{
for (var c = FirstChar; c <= LastChar; c++)
{
diff --git a/src/Avalonia.Base/Utilities/MathUtilities.cs b/src/Avalonia.Base/Utilities/MathUtilities.cs
index 3c48c3469e..feb1097b5f 100644
--- a/src/Avalonia.Base/Utilities/MathUtilities.cs
+++ b/src/Avalonia.Base/Utilities/MathUtilities.cs
@@ -11,7 +11,7 @@ namespace Avalonia.Utilities
static class MathUtilities
{
// smallest such that 1.0+DoubleEpsilon != 1.0
- internal static readonly double DoubleEpsilon = 2.2204460492503131e-016;
+ internal const double DoubleEpsilon = 2.2204460492503131e-016;
private const float FloatEpsilon = 1.192092896e-07F;
diff --git a/src/Avalonia.Base/Utilities/ReadOnlySlice.cs b/src/Avalonia.Base/Utilities/ReadOnlySlice.cs
index ad545b2923..583a3139b9 100644
--- a/src/Avalonia.Base/Utilities/ReadOnlySlice.cs
+++ b/src/Avalonia.Base/Utilities/ReadOnlySlice.cs
@@ -214,6 +214,8 @@ namespace Avalonia.Utilities
return new ReadOnlySlice(memory);
}
+ public static implicit operator ReadOnlySpan(ReadOnlySlice slice) => slice.Span;
+
internal class ReadOnlySliceDebugView
{
private readonly ReadOnlySlice _readOnlySlice;
diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs
index 130d7e0edd..2cc5b99b2e 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs
@@ -17,268 +17,603 @@ namespace Avalonia.Controls
///
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[,]
+ ///
+ /// Defines all colors in the .
+ ///
+ ///
+ /// This is done in an enum to ensure it is compiled into the assembly improving
+ /// startup performance.
+ ///
+ 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,
};
- ///
- /// Gets the index of the default shade of colors in this palette.
- ///
- public const int DefaultShadeIndex = 2;
-
- ///
- /// The index in the color palette of the 'Pomegranate' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int PomegranateIndex = 0;
-
- ///
- /// The index in the color palette of the 'Amethyst' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int AmethystIndex = 1;
-
- ///
- /// The index in the color palette of the 'BelizeHole' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int BelizeHoleIndex = 2;
-
- ///
- /// The index in the color palette of the 'Turquoise' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int TurquoiseIndex = 3;
-
- ///
- /// The index in the color palette of the 'Nephritis' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int NephritisIndex = 4;
-
- ///
- /// The index in the color palette of the 'Sunflower' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int SunflowerIndex = 5;
-
- ///
- /// The index in the color palette of the 'Carrot' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int CarrotIndex = 6;
-
- ///
- /// The index in the color palette of the 'Clouds' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int CloudsIndex = 7;
-
- ///
- /// The index in the color palette of the 'Concrete' color.
- /// This index can correspond to multiple color shades.
- ///
- 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();
///
- /// The index in the color palette of the 'WetAsphalt' color.
- /// This index can correspond to multiple color shades.
+ /// Initializes all color chart colors.
///
- 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;
+ }
///
public int ColorCount
{
- // Table is transposed compared to the reference chart
- get => colorChart.GetLength(0);
+ get => 20;
}
///
public int ShadeCount
{
- // Table is transposed compared to the reference chart
- get => colorChart.GetLength(1);
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFC0392B.
- ///
- public static Color Pomegranate
- {
- get => colorChart[PomegranateIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF9B59B6.
- ///
- public static Color Amethyst
- {
- get => colorChart[AmethystIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF2980B9.
- ///
- public static Color BelizeHole
- {
- get => colorChart[BelizeHoleIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF1ABC9C.
- ///
- public static Color Turquoise
- {
- get => colorChart[TurquoiseIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF27AE60.
- ///
- public static Color Nephritis
- {
- get => colorChart[NephritisIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFF1C40F.
- ///
- public static Color Sunflower
- {
- get => colorChart[SunflowerIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFE67E22.
- ///
- public static Color Carrot
- {
- get => colorChart[CarrotIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFECF0F1.
- ///
- public static Color Clouds
- {
- get => colorChart[CloudsIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF95A5A6.
- ///
- public static Color Concrete
- {
- get => colorChart[ConcreteIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF34495E.
- ///
- public static Color WetAsphalt
- {
- get => colorChart[WetAsphaltIndex, DefaultShadeIndex];
+ get => 10;
}
///
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)];
}
}
}
diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs
new file mode 100644
index 0000000000..2758124fae
--- /dev/null
+++ b/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
+{
+ ///
+ /// Implements half of the for improved usability.
+ ///
+ ///
+ public class FlatHalfColorPalette : IColorPalette
+ {
+ protected static Color[,]? _colorChart = null;
+ protected static object _colorChartMutex = new object();
+
+ ///
+ /// Initializes all color chart colors.
+ ///
+ 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;
+ }
+
+ ///
+ public int ColorCount
+ {
+ get => 10;
+ }
+
+ ///
+ public int ShadeCount
+ {
+ get => 5;
+ }
+
+ ///
+ 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)];
+ }
+ }
+}
diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs
index 5cf5662ede..8fa7ede77e 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs
@@ -21,295 +21,529 @@ namespace Avalonia.Controls
///
public class MaterialColorPalette : IColorPalette
{
+ ///
+ /// Defines all colors in the .
+ ///
+ ///
+ /// This is done in an enum to ensure it is compiled into the assembly improving
+ /// startup performance.
+ ///
+ 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();
///
/// Initializes all color chart colors.
///
- ///
- /// This is pulled out separately to lazy load for performance.
- /// If no material color palette is ever used, no colors will be created.
- ///
- 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
///
public int ColorCount
{
- get
- {
- if (_colorChart == null)
- {
- InitColorChart();
- }
-
- return _colorChartColorCount;
- }
+ get => 19;
}
///
public int ShadeCount
{
- get
- {
- if (_colorChart == null)
- {
- InitColorChart();
- }
-
- return _colorChartShadeCount;
- }
+ get => 10;
}
///
@@ -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)];
}
}
}
diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs
new file mode 100644
index 0000000000..01d44aa65d
--- /dev/null
+++ b/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
+{
+ ///
+ /// Implements half of the for improved usability.
+ ///
+ ///
+ public class MaterialHalfColorPalette : IColorPalette
+ {
+ protected static Color[,]? _colorChart = null;
+ protected static object _colorChartMutex = new object();
+
+ ///
+ /// Initializes all color chart colors.
+ ///
+ 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;
+ }
+
+ ///
+ public int ColorCount
+ {
+ get => 10;
+ }
+
+ ///
+ public int ShadeCount
+ {
+ get => 5;
+ }
+
+ ///
+ 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)];
+ }
+ }
+}
diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/SixteenColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/SixteenColorPalette.cs
index f3abfdfd7f..e8e82eac91 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorPalettes/SixteenColorPalette.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/SixteenColorPalette.cs
@@ -50,107 +50,6 @@ namespace Avalonia.Controls
}
};
- ///
- /// Gets the index of the default shade of colors in this palette.
- ///
- public const int DefaultShadeIndex = 0;
-
- ///
- /// The index in the color palette of the 'White' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int WhiteIndex = 0;
-
- ///
- /// The index in the color palette of the 'Silver' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int SilverIndex = 1;
-
- ///
- /// The index in the color palette of the 'Gray' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int GrayIndex = 2;
-
- ///
- /// The index in the color palette of the 'Black' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int BlackIndex = 3;
-
- ///
- /// The index in the color palette of the 'Red' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int RedIndex = 4;
-
- ///
- /// The index in the color palette of the 'Maroon' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int MaroonIndex = 5;
-
- ///
- /// The index in the color palette of the 'Yellow' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int YellowIndex = 6;
-
- ///
- /// The index in the color palette of the 'Olive' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int OliveIndex = 7;
-
- ///
- /// The index in the color palette of the 'Lime' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int LimeIndex = 8;
-
- ///
- /// The index in the color palette of the 'Green' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int GreenIndex = 9;
-
- ///
- /// The index in the color palette of the 'Aqua' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int AquaIndex = 10;
-
- ///
- /// The index in the color palette of the 'Teal' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int TealIndex = 11;
-
- ///
- /// The index in the color palette of the 'Blue' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int BlueIndex = 12;
-
- ///
- /// The index in the color palette of the 'Navy' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int NavyIndex = 13;
-
- ///
- /// The index in the color palette of the 'Fuchsia' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int FuchsiaIndex = 14;
-
- ///
- /// The index in the color palette of the 'Purple' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int PurpleIndex = 15;
-
///
public int ColorCount
{
@@ -163,134 +62,6 @@ namespace Avalonia.Controls
get => colorChart.GetLength(1);
}
- ///
- /// Gets the palette defined color that has an ARGB value of #FFFFFFFF.
- ///
- public static Color White
- {
- get => colorChart[WhiteIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFC0C0C0.
- ///
- public static Color Silver
- {
- get => colorChart[SilverIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF808080.
- ///
- public static Color Gray
- {
- get => colorChart[GrayIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF000000.
- ///
- public static Color Black
- {
- get => colorChart[BlackIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFFF0000.
- ///
- public static Color Red
- {
- get => colorChart[RedIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF800000.
- ///
- public static Color Maroon
- {
- get => colorChart[MaroonIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFFFFF00.
- ///
- public static Color Yellow
- {
- get => colorChart[YellowIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF808000.
- ///
- public static Color Olive
- {
- get => colorChart[OliveIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF00FF00.
- ///
- public static Color Lime
- {
- get => colorChart[LimeIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF008000.
- ///
- public static Color Green
- {
- get => colorChart[GreenIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF00FFFF.
- ///
- public static Color Aqua
- {
- get => colorChart[AquaIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF008080.
- ///
- public static Color Teal
- {
- get => colorChart[TealIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF0000FF.
- ///
- public static Color Blue
- {
- get => colorChart[BlueIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF000080.
- ///
- public static Color Navy
- {
- get => colorChart[NavyIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFFF00FF.
- ///
- public static Color Fuchsia
- {
- get => colorChart[FuchsiaIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF800080.
- ///
- public static Color Purple
- {
- get => colorChart[PurpleIndex, DefaultShadeIndex];
- }
-
///
public Color GetColor(int colorIndex, int shadeIndex)
{
diff --git a/src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs b/src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs
index 39369bcbdb..29f9f3c571 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs
@@ -1,4 +1,6 @@
-namespace Avalonia.Controls
+using Avalonia.Controls.Primitives;
+
+namespace Avalonia.Controls
{
///
/// Presents a color for user editing using a spectrum, palette and component sliders within a drop down.
@@ -11,8 +13,33 @@
///
public ColorPicker() : base()
{
- // Completely ignore property changes here
- // The ColorView in the control template is responsible to manage this
+ }
+
+ ///
+ 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;
}
}
diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs
index b662d20223..ec08e96d87 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs
+++ b/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;
+
///
/// Initializes a new instance of the class.
///
@@ -38,6 +41,18 @@ namespace Avalonia.Controls.Primitives
{
}
+ ///
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+ }
+
+ ///
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+ }
+
///
/// Updates the visual state of the control by applying latest PseudoClasses.
///
@@ -98,7 +113,7 @@ namespace Avalonia.Controls.Primitives
if (pixelWidth != 0 && pixelHeight != 0)
{
- var bitmap = await ColorPickerHelpers.CreateComponentBitmapAsync(
+ ArrayList 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 ||
diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs
index 00d84f5dd3..39b7b7f660 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs
@@ -93,6 +93,14 @@ namespace Avalonia.Controls.Primitives
nameof(Shape),
ColorSpectrumShape.Box);
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty ThirdComponentProperty =
+ AvaloniaProperty.Register(
+ nameof(ThirdComponent),
+ ColorComponent.Component3); // Value
+
///
/// Gets or sets the currently selected color in the RGB color model.
///
@@ -218,5 +226,21 @@ namespace Avalonia.Controls.Primitives
get => GetValue(ShapeProperty);
set => SetValue(ShapeProperty, value);
}
+
+ ///
+ /// Gets the third HSV color component that is NOT displayed by the spectrum.
+ /// This is automatically calculated from the property.
+ ///
+ ///
+ /// 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
+ /// type instead of the more accurate
+ /// to allow direct usage by the generalized color sliders.
+ ///
+ public ColorComponent ThirdComponent
+ {
+ get => GetValue(ThirdComponentProperty);
+ private set => SetValue(ThirdComponentProperty, value);
+ }
}
}
diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs
index bd44161a42..f0ed89fb3a 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs
+++ b/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
///
/// Initializes a new instance of the class.
///
- 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();
}
+ ///
+ /// Updates the selected and based on a point within the color spectrum.
+ ///
+ /// The point on the spectrum representing the color.
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);
}
+ ///
+ /// Updates the position of the selection ellipse on the spectrum which indicates the selected color.
+ ///
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 bgraMinPixelData = new List();
- List bgraMiddle1PixelData = new List();
- List bgraMiddle2PixelData = new List();
- List bgraMiddle3PixelData = new List();
- List bgraMiddle4PixelData = new List();
- List bgraMaxPixelData = new List();
- List newHsvValues = new List();
+ ArrayList bgraMinPixelData;
+ ArrayList bgraMiddle1PixelData;
+ ArrayList bgraMiddle2PixelData;
+ ArrayList bgraMiddle3PixelData;
+ ArrayList bgraMiddle4PixelData;
+ ArrayList bgraMaxPixelData;
+ List 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(pixelDataSize);
+ bgraMaxPixelData = new ArrayList(pixelDataSize);
+ newHsvValues = new List(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(pixelDataSize);
+ bgraMiddle2PixelData = new ArrayList(pixelDataSize);
+ bgraMiddle3PixelData = new ArrayList(pixelDataSize);
+ bgraMiddle4PixelData = new ArrayList(pixelDataSize);
+ }
+ else
+ {
+ bgraMiddle1PixelData = new ArrayList(0);
+ bgraMiddle2PixelData = new ArrayList(0);
+ bgraMiddle3PixelData = new ArrayList(0);
+ bgraMiddle4PixelData = new ArrayList(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 bgraMinPixelData,
- List bgraMiddle1PixelData,
- List bgraMiddle2PixelData,
- List bgraMiddle3PixelData,
- List bgraMiddle4PixelData,
- List bgraMaxPixelData,
+ ArrayList bgraMinPixelData,
+ ArrayList bgraMiddle1PixelData,
+ ArrayList bgraMiddle2PixelData,
+ ArrayList bgraMiddle3PixelData,
+ ArrayList bgraMiddle4PixelData,
+ ArrayList bgraMaxPixelData,
List newHsvValues)
{
double hMin = minHue;
@@ -1271,12 +1318,12 @@ namespace Avalonia.Controls.Primitives
double maxSaturation,
double minValue,
double maxValue,
- List bgraMinPixelData,
- List bgraMiddle1PixelData,
- List bgraMiddle2PixelData,
- List bgraMiddle3PixelData,
- List bgraMiddle4PixelData,
- List bgraMaxPixelData,
+ ArrayList bgraMinPixelData,
+ ArrayList bgraMiddle1PixelData,
+ ArrayList bgraMiddle2PixelData,
+ ArrayList bgraMiddle3PixelData,
+ ArrayList bgraMiddle4PixelData,
+ ArrayList bgraMaxPixelData,
List newHsvValues)
{
double hMin = minHue;
diff --git a/src/Avalonia.Controls.ColorPicker/Converters/ThirdComponentConverter.cs b/src/Avalonia.Controls.ColorPicker/Converters/ThirdComponentConverter.cs
deleted file mode 100644
index 11e33c74f0..0000000000
--- a/src/Avalonia.Controls.ColorPicker/Converters/ThirdComponentConverter.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using System;
-using System.Globalization;
-using Avalonia.Data.Converters;
-
-namespace Avalonia.Controls.Primitives.Converters
-{
- ///
- /// Gets the third corresponding with a given
- /// that represents the other two components.
- ///
- ///
- /// This is a highly-specialized converter for the color picker.
- ///
- public class ThirdComponentConverter : IValueConverter
- {
- ///
- 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;
- }
-
- ///
- public object? ConvertBack(
- object? value,
- Type targetType,
- object? parameter,
- CultureInfo culture)
- {
- return AvaloniaProperty.UnsetValue;
- }
- }
-}
diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs
new file mode 100644
index 0000000000..0b4c2f8579
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs
@@ -0,0 +1,71 @@
+namespace Avalonia.Controls.Primitives
+{
+ ///
+ /// A thin wrapper over an that allows some additional list-like functionality.
+ ///
+ ///
+ /// 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.
+ ///
+ /// The type of items in the array.
+ internal class ArrayList
+ {
+ private int _nextIndex = 0;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ArrayList(int capacity)
+ {
+ Capacity = capacity;
+ Array = new T[capacity];
+ }
+
+ ///
+ /// Provides access to the underlying array by index.
+ /// This exists for simplification and the property
+ /// may also be used.
+ ///
+ /// The index of the item to get or set.
+ /// The item at the given index.
+ public T this[int i]
+ {
+ get => Array[i];
+ set => Array[i] = value;
+ }
+
+ ///
+ /// Gets the underlying array.
+ ///
+ public T[] Array { get; private set; }
+
+ ///
+ /// Gets the fixed capacity/size of the array.
+ /// This must be set during construction.
+ ///
+ public int Capacity { get; private set; }
+
+ ///
+ /// Adds the given item to the array at the next available index.
+ /// WARNING: This must be used carefully and only once, in sequence.
+ ///
+ /// The item to add.
+ 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;
+ }
+ }
+}
diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs
index 381bc42aaa..819d745772 100644
--- a/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs
+++ b/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.
/// A new bitmap representing a gradient of color component values.
- public static async Task CreateComponentBitmapAsync(
+ public static async Task> CreateComponentBitmapAsync(
int width,
int height,
Orientation orientation,
@@ -49,14 +49,14 @@ namespace Avalonia.Controls.Primitives
{
if (width == 0 || height == 0)
{
- return Array.Empty();
+ return new ArrayList(0);
}
- var bitmap = await Task.Run(() =>
+ var bitmap = await Task.Run>(() =>
{
int pixelDataIndex = 0;
double componentStep;
- byte[] bgraPixelData;
+ ArrayList 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(width * height * 4);
bgraPixelDataHeight = height * 4;
bgraPixelDataWidth = width * 4;
@@ -604,7 +604,7 @@ namespace Avalonia.Controls.Primitives
/// The pixel height of the bitmap.
/// A new .
public static WriteableBitmap CreateBitmapFromPixelData(
- IList bgraPixelData,
+ ArrayList 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;
}
+
+ ///
+ /// Updates the given 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.
+ ///
+ /// The existing to update.
+ /// The bitmap (in raw BGRA pre-multiplied alpha pixels).
+ public static void UpdateBitmapFromPixelData(
+ WriteableBitmap bitmap,
+ ArrayList bgraPixelData)
+ {
+ using (var frameBuffer = bitmap.Lock())
+ {
+ Marshal.Copy(bgraPixelData.Array, 0, frameBuffer.Address, bgraPixelData.Array.Length);
+ }
+
+ return;
+ }
}
}
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
index 16bc2acdd1..b3c6d1a430 100644
--- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
@@ -8,7 +8,6 @@
x:CompileBindings="True">
-
@@ -241,23 +240,21 @@
-
+
-
+
-
@@ -215,23 +214,21 @@
-
+
-
+
WatermarkProperty =
+ TextBox.WatermarkProperty.AddOwner();
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly StyledProperty MinimumPrefixLengthProperty =
+ AvaloniaProperty.Register(
+ nameof(MinimumPrefixLength), 1,
+ validate: IsValidMinimumPrefixLength);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly StyledProperty MinimumPopulateDelayProperty =
+ AvaloniaProperty.Register(
+ nameof(MinimumPopulateDelay),
+ TimeSpan.Zero,
+ validate: IsValidMinimumPopulateDelay);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly StyledProperty MaxDropDownHeightProperty =
+ AvaloniaProperty.Register(
+ nameof(MaxDropDownHeight),
+ double.PositiveInfinity,
+ validate: IsValidMaxDropDownHeight);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly StyledProperty IsTextCompletionEnabledProperty =
+ AvaloniaProperty.Register(nameof(IsTextCompletionEnabled));
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly StyledProperty ItemTemplateProperty =
+ AvaloniaProperty.Register(nameof(ItemTemplate));
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty IsDropDownOpenProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(IsDropDownOpen),
+ o => o.IsDropDownOpen,
+ (o, v) => o.IsDropDownOpen = v);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier the property.
+ public static readonly DirectProperty SelectedItemProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(SelectedItem),
+ o => o.SelectedItem,
+ (o, v) => o.SelectedItem = v,
+ defaultBindingMode: BindingMode.TwoWay,
+ enableDataValidation: true);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty TextProperty =
+ TextBlock.TextProperty.AddOwnerWithDataValidation(
+ o => o.Text,
+ (o, v) => o.Text = v,
+ defaultBindingMode: BindingMode.TwoWay,
+ enableDataValidation: true);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty SearchTextProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(SearchText),
+ o => o.SearchText,
+ unsetValue: string.Empty);
+
+ ///
+ /// Gets the identifier for the property.
+ ///
+ public static readonly StyledProperty FilterModeProperty =
+ AvaloniaProperty.Register(
+ nameof(FilterMode),
+ defaultValue: AutoCompleteFilterMode.StartsWith,
+ validate: IsValidFilterMode);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty?> ItemFilterProperty =
+ AvaloniaProperty.RegisterDirect?>(
+ nameof(ItemFilter),
+ o => o.ItemFilter,
+ (o, v) => o.ItemFilter = v);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty?> TextFilterProperty =
+ AvaloniaProperty.RegisterDirect?>(
+ nameof(TextFilter),
+ o => o.TextFilter,
+ (o, v) => o.TextFilter = v,
+ unsetValue: AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith));
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty?> ItemSelectorProperty =
+ AvaloniaProperty.RegisterDirect?>(
+ nameof(ItemSelector),
+ o => o.ItemSelector,
+ (o, v) => o.ItemSelector = v);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty?> TextSelectorProperty =
+ AvaloniaProperty.RegisterDirect?>(
+ nameof(TextSelector),
+ o => o.TextSelector,
+ (o, v) => o.TextSelector = v);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty ItemsProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Items),
+ o => o.Items,
+ (o, v) => o.Items = v);
+
+ public static readonly DirectProperty>>?> AsyncPopulatorProperty =
+ AvaloniaProperty.RegisterDirect>>?>(
+ nameof(AsyncPopulator),
+ o => o.AsyncPopulator,
+ (o, v) => o.AsyncPopulator = v);
+
+ ///
+ /// Gets or sets the minimum number of characters required to be entered
+ /// in the text box before the displays possible matches.
+ ///
+ ///
+ /// The minimum number of characters to be entered in the text box
+ /// before the
+ /// displays possible matches. The default is 1.
+ ///
+ ///
+ /// 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.
+ ///
+ public int MinimumPrefixLength
+ {
+ get => GetValue(MinimumPrefixLengthProperty);
+ set => SetValue(MinimumPrefixLengthProperty, value);
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the first possible match
+ /// found during the filtering process will be displayed automatically
+ /// in the text box.
+ ///
+ ///
+ /// True if the first possible match found will be displayed
+ /// automatically in the text box; otherwise, false. The default is
+ /// false.
+ ///
+ public bool IsTextCompletionEnabled
+ {
+ get => GetValue(IsTextCompletionEnabledProperty);
+ set => SetValue(IsTextCompletionEnabledProperty, value);
+ }
+
+ ///
+ /// Gets or sets the used
+ /// to display each item in the drop-down portion of the control.
+ ///
+ /// The used to
+ /// display each item in the drop-down. The default is null.
+ ///
+ /// 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.
+ ///
+ public IDataTemplate ItemTemplate
+ {
+ get => GetValue(ItemTemplateProperty);
+ set => SetValue(ItemTemplateProperty, value);
+ }
+
+ ///
+ /// Gets or sets the minimum delay, after text is typed
+ /// in the text box before the
+ /// control
+ /// populates the list of possible matches in the drop-down.
+ ///
+ /// The minimum delay, after text is typed in
+ /// the text box, but before the
+ /// populates
+ /// the list of possible matches in the drop-down. The default is 0.
+ public TimeSpan MinimumPopulateDelay
+ {
+ get => GetValue(MinimumPopulateDelayProperty);
+ set => SetValue(MinimumPopulateDelayProperty, value);
+ }
+
+ ///
+ /// Gets or sets the maximum height of the drop-down portion of the
+ /// control.
+ ///
+ /// The maximum height of the drop-down portion of the
+ /// control.
+ /// The default is .
+ /// The specified value is less than 0.
+ public double MaxDropDownHeight
+ {
+ get => GetValue(MaxDropDownHeightProperty);
+ set => SetValue(MaxDropDownHeightProperty, value);
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the drop-down portion of
+ /// the control is open.
+ ///
+ ///
+ /// True if the drop-down is open; otherwise, false. The default is
+ /// false.
+ ///
+ public bool IsDropDownOpen
+ {
+ get => _isDropDownOpen;
+ set => SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value);
+ }
+
+ ///
+ /// Gets or sets the that
+ /// is used to get the values for display in the text portion of
+ /// the
+ /// control.
+ ///
+ /// The object used
+ /// when binding to a collection property.
+ [AssignBinding]
+ public IBinding? ValueMemberBinding
+ {
+ get => _valueBindingEvaluator?.ValueBinding;
+ set
+ {
+ if (ValueMemberBinding != value)
+ {
+ _valueBindingEvaluator = new BindingEvaluator(value);
+ OnValueMemberBindingChanged(value);
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the selected item in the drop-down.
+ ///
+ /// The selected item in the drop-down.
+ ///
+ /// 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.
+ ///
+ public object? SelectedItem
+ {
+ get => _selectedItem;
+ set => SetAndRaise(SelectedItemProperty, ref _selectedItem, value);
+ }
+
+ ///
+ /// Gets or sets the text in the text box portion of the
+ /// control.
+ ///
+ /// The text in the text box portion of the
+ /// control.
+ public string? Text
+ {
+ get => _text;
+ set => SetAndRaise(TextProperty, ref _text, value);
+ }
+
+ ///
+ /// Gets the text that is used to filter items in the
+ /// item collection.
+ ///
+ /// The text that is used to filter items in the
+ /// item collection.
+ ///
+ /// The SearchText value is typically the same as the
+ /// Text property, but is set after the TextChanged event occurs
+ /// and before the Populating event.
+ ///
+ public string? SearchText
+ {
+ get => _searchText;
+ private set
+ {
+ try
+ {
+ _allowWrite = true;
+ SetAndRaise(SearchTextProperty, ref _searchText, value);
+ }
+ finally
+ {
+ _allowWrite = false;
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets how the text in the text box is used to filter items
+ /// specified by the
+ /// property for display in the drop-down.
+ ///
+ /// One of the
+ /// values The default is .
+ /// The specified value is not a valid
+ /// .
+ ///
+ /// 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.
+ ///
+ public AutoCompleteFilterMode FilterMode
+ {
+ get => GetValue(FilterModeProperty);
+ set => SetValue(FilterModeProperty, value);
+ }
+
+ public string? Watermark
+ {
+ get => GetValue(WatermarkProperty);
+ set => SetValue(WatermarkProperty, value);
+ }
+
+ ///
+ /// Gets or sets the custom method that uses user-entered text to filter
+ /// the items specified by the
+ /// property for display in the drop-down.
+ ///
+ /// The custom method that uses the user-entered text to filter
+ /// the items specified by the
+ /// property. The default is null.
+ ///
+ /// The filter mode is automatically set to Custom if you set the
+ /// ItemFilter property.
+ ///
+ public AutoCompleteFilterPredicate
- 0,0,0,4
40
40
41
@@ -84,18 +83,8 @@
-
-
-
+
- 0,0,0,4
40
40
41
@@ -91,19 +90,9 @@
-
+ Margin="{TemplateBinding Padding}">
40
1
1
- 0,0,0,4
40
41
242
@@ -90,20 +89,8 @@
-
-
+ Margin="{TemplateBinding Padding}">
-
-
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
index ce931b9c14..debdece60f 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
@@ -5,6 +5,11 @@
true
Avalonia.Markup.Xaml.Loader
$(DefineConstants);XAMLX_INTERNAL
+ 10
+
+
+
+ false
diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs
index 1e42a46875..1b6fbcef5c 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs
+++ b/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);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
index d462a2210e..f28f7bc626 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
+++ b/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))
{
diff --git a/src/Skia/Avalonia.Skia/FontManagerImpl.cs b/src/Skia/Avalonia.Skia/FontManagerImpl.cs
index 125dd0e455..6b5e0b3db5 100644
--- a/src/Skia/Avalonia.Skia/FontManagerImpl.cs
+++ b/src/Skia/Avalonia.Skia/FontManagerImpl.cs
@@ -102,7 +102,7 @@ namespace Avalonia.Skia
return false;
}
- public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface)
+ public IGlyphTypeface CreateGlyphTypeface(Typeface typeface)
{
SKTypeface skTypeface = null;
diff --git a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
index dcb4eac7ca..7f3faf251f 100644
--- a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
+++ b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
@@ -1,14 +1,14 @@
using System;
using System.Runtime.InteropServices;
+using Avalonia.Media;
using Avalonia.Metadata;
-using Avalonia.Platform;
using HarfBuzzSharp;
using SkiaSharp;
namespace Avalonia.Skia
{
[Unstable]
- public class GlyphTypefaceImpl : IGlyphTypefaceImpl
+ public class GlyphTypefaceImpl : IGlyphTypeface
{
private bool _isDisposed;
@@ -25,35 +25,32 @@ namespace Avalonia.Skia
Font.SetFunctionsOpenType();
- DesignEmHeight = (short)Typeface.UnitsPerEm;
-
var metrics = Typeface.ToFont().Metrics;
const double defaultFontRenderingEmSize = 12.0;
- Ascent = (int)(metrics.Ascent / defaultFontRenderingEmSize * Typeface.UnitsPerEm);
-
- Descent = (int)(metrics.Descent / defaultFontRenderingEmSize * Typeface.UnitsPerEm);
-
- LineGap = (int)(metrics.Leading / defaultFontRenderingEmSize * Typeface.UnitsPerEm);
-
- UnderlinePosition = metrics.UnderlinePosition != null ?
+ Metrics = new FontMetrics
+ {
+ DesignEmHeight = (short)Typeface.UnitsPerEm,
+ Ascent = (int)(metrics.Ascent / defaultFontRenderingEmSize * Typeface.UnitsPerEm),
+ Descent = (int)(metrics.Descent / defaultFontRenderingEmSize * Typeface.UnitsPerEm),
+ LineGap = (int)(metrics.Leading / defaultFontRenderingEmSize * Typeface.UnitsPerEm),
+ UnderlinePosition = metrics.UnderlinePosition != null ?
(int)(metrics.UnderlinePosition / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
- 0;
-
- UnderlineThickness = metrics.UnderlineThickness != null ?
+ 0,
+ UnderlineThickness = metrics.UnderlineThickness != null ?
(int)(metrics.UnderlineThickness / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
- 0;
-
- StrikethroughPosition = metrics.StrikeoutPosition != null ?
+ 0,
+ StrikethroughPosition = metrics.StrikeoutPosition != null ?
(int)(metrics.StrikeoutPosition / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
- 0;
-
- StrikethroughThickness = metrics.StrikeoutThickness != null ?
+ 0,
+ StrikethroughThickness = metrics.StrikeoutThickness != null ?
(int)(metrics.StrikeoutThickness / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
- 0;
+ 0,
+ IsFixedPitch = Typeface.IsFixedPitch
+ };
- IsFixedPitch = Typeface.IsFixedPitch;
+ GlyphCount = Typeface.GlyphCount;
IsFakeBold = isFakeBold;
@@ -67,39 +64,16 @@ namespace Avalonia.Skia
public SKTypeface Typeface { get; }
public int ReplacementCodepoint { get; }
-
- ///
- public short DesignEmHeight { get; }
-
- ///
- public int Ascent { get; }
-
- ///
- public int Descent { get; }
- ///
- public int LineGap { get; }
+ public FontMetrics Metrics { get; }
- ///
- public int UnderlinePosition { get; }
+ public int GlyphCount { get; }
- ///
- public int UnderlineThickness { get; }
-
- ///
- public int StrikethroughPosition { get; }
-
- ///
- public int StrikethroughThickness { get; }
-
- ///
- public bool IsFixedPitch { get; }
-
public bool IsFakeBold { get; }
-
+
public bool IsFakeItalic { get; }
- ///
+ ///
public ushort GetGlyph(uint codepoint)
{
if (Font.TryGetGlyph(codepoint, out var glyph))
@@ -110,7 +84,14 @@ namespace Avalonia.Skia
return 0;
}
- ///
+ public bool TryGetGlyph(uint codepoint, out ushort glyph)
+ {
+ glyph = GetGlyph(codepoint);
+
+ return glyph != 0;
+ }
+
+ ///
public ushort[] GetGlyphs(ReadOnlySpan codepoints)
{
var glyphs = new ushort[codepoints.Length];
@@ -126,13 +107,13 @@ namespace Avalonia.Skia
return glyphs;
}
- ///
+ ///
public int GetGlyphAdvance(ushort glyph)
{
return Font.GetHorizontalGlyphAdvance(glyph);
}
- ///
+ ///
public int[] GetGlyphAdvances(ReadOnlySpan glyphs)
{
var glyphIndices = new uint[glyphs.Length];
@@ -180,5 +161,10 @@ namespace Avalonia.Skia
Dispose(true);
GC.SuppressFinalize(this);
}
+
+ public bool TryGetTable(uint tag, out byte[] table)
+ {
+ return Typeface.TryGetTableData(tag, out table);
+ }
}
}
diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
index 91fe4fc085..a9696efbd4 100644
--- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
+++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
-using System.IO;
+using System.IO;
using System.Linq;
using System.Threading;
@@ -12,6 +12,8 @@ using Avalonia.OpenGL.Imaging;
using Avalonia.Platform;
using Avalonia.Media.Imaging;
using SkiaSharp;
+using System.Runtime.InteropServices;
+using System.Drawing;
namespace Avalonia.Skia
{
@@ -33,13 +35,17 @@ namespace Avalonia.Skia
}
var gl = AvaloniaLocator.Current.GetService();
- if (gl != null)
+ if (gl != null)
_skiaGpu = new GlSkiaGpu(gl, maxResourceBytes);
-
- //TODO: SKFont crashes when disposed in finalizer so we keep it alive
- GC.SuppressFinalize(s_font);
}
+
+ public bool SupportsIndividualRoundRects => true;
+
+ public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
+
+ public PixelFormat DefaultPixelFormat { get; }
+
public IGeometryImpl CreateEllipseGeometry(Rect rect) => new EllipseGeometryImpl(rect);
public IGeometryImpl CreateLineGeometry(Point p1, Point p2) => new LineGeometryImpl(p1, p2);
@@ -64,7 +70,7 @@ namespace Avalonia.Skia
public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun)
{
- if (glyphRun.GlyphTypeface.PlatformImpl is not GlyphTypefaceImpl glyphTypeface)
+ if (glyphRun.GlyphTypeface is not GlyphTypefaceImpl glyphTypeface)
{
throw new InvalidOperationException("PlatformImpl can't be null.");
}
@@ -228,133 +234,95 @@ namespace Avalonia.Skia
return new WriteableBitmapImpl(size, dpi, format, alphaFormat);
}
- private static readonly SKFont s_font = new SKFont
- {
- Subpixel = true,
- Edging = SKFontEdging.SubpixelAntialias,
- Hinting = SKFontHinting.Full,
- LinearMetrics = true
- };
-
- private static readonly ThreadLocal s_textBlobBuilderThreadLocal = new ThreadLocal(() => new SKTextBlobBuilder());
-
- ///
- public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun)
+ public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi)
{
- var count = glyphRun.GlyphIndices.Count;
- var textBlobBuilder = s_textBlobBuilderThreadLocal.Value;
-
- var glyphTypeface = (GlyphTypefaceImpl)glyphRun.GlyphTypeface.PlatformImpl;
+ if (_skiaGpu is IOpenGlAwareSkiaGpu glAware)
+ return glAware.CreateOpenGlBitmap(size, dpi);
+ if (_skiaGpu == null)
+ throw new PlatformNotSupportedException("GPU acceleration is not available");
+ throw new PlatformNotSupportedException(
+ "Current GPU acceleration backend does not support OpenGL integration");
+ }
- var typeface = glyphTypeface.Typeface;
+ public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ => new SKGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length);
- s_font.Size = (float)glyphRun.FontRenderingEmSize;
- s_font.Typeface = typeface;
- s_font.Embolden = glyphTypeface.IsFakeBold;
- s_font.SkewX = glyphTypeface.IsFakeItalic ? -0.2f : 0;
+ public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ => new SKHorizontalGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length);
- SKTextBlob textBlob;
+ public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ => new SKPositionedGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length);
- var scale = (float)(glyphRun.FontRenderingEmSize / glyphTypeface.DesignEmHeight);
+ private abstract class SKGlyphRunBufferBase : IGlyphRunBuffer
+ {
+ protected readonly SKTextBlobBuilder _builder;
+ protected readonly SKFont _font;
- if (glyphRun.GlyphOffsets == null)
+ public SKGlyphRunBufferBase(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
{
- if (glyphTypeface.IsFixedPitch)
- {
- var buffer = textBlobBuilder.AllocateRun(s_font, glyphRun.GlyphIndices.Count, 0, 0);
-
- var glyphs = buffer.GetGlyphSpan();
+ _builder = new SKTextBlobBuilder();
- for (int i = 0; i < glyphs.Length; i++)
- {
- glyphs[i] = glyphRun.GlyphIndices[i];
- }
+ var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface;
- textBlob = textBlobBuilder.Build();
- }
- else
+ _font = new SKFont
{
- var buffer = textBlobBuilder.AllocateHorizontalRun(s_font, count, 0);
-
- var positions = buffer.GetPositionSpan();
-
- var width = 0d;
-
- for (var i = 0; i < count; i++)
- {
- positions[i] = (float)width;
-
- if (glyphRun.GlyphAdvances == null)
- {
- width += glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]) * scale;
- }
- else
- {
- width += glyphRun.GlyphAdvances[i];
- }
- }
-
- var glyphs = buffer.GetGlyphSpan();
+ Subpixel = true,
+ Edging = SKFontEdging.SubpixelAntialias,
+ Hinting = SKFontHinting.Full,
+ LinearMetrics = true,
+ Size = fontRenderingEmSize,
+ Typeface = glyphTypefaceImpl.Typeface,
+ Embolden = glyphTypefaceImpl.IsFakeBold,
+ SkewX = glyphTypefaceImpl.IsFakeItalic ? -0.2f : 0
+ };
+ }
- for (int i = 0; i < glyphs.Length; i++)
- {
- glyphs[i] = glyphRun.GlyphIndices[i];
- }
+ public abstract Span GlyphIndices { get; }
- textBlob = textBlobBuilder.Build();
- }
- }
- else
+ public IGlyphRunImpl Build()
{
- var buffer = textBlobBuilder.AllocatePositionedRun(s_font, count);
-
- var glyphPositions = buffer.GetPositionSpan();
+ return new GlyphRunImpl(_builder.Build());
+ }
+ }
- var currentX = 0.0;
+ private sealed class SKGlyphRunBuffer : SKGlyphRunBufferBase
+ {
+ private readonly SKRunBuffer _buffer;
- for (var i = 0; i < count; i++)
- {
- var glyphOffset = glyphRun.GlyphOffsets[i];
-
- glyphPositions[i] = new SKPoint((float)(currentX + glyphOffset.X), (float)glyphOffset.Y);
-
- if (glyphRun.GlyphAdvances == null)
- {
- currentX += glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]) * scale;
- }
- else
- {
- currentX += glyphRun.GlyphAdvances[i];
- }
- }
+ public SKGlyphRunBuffer(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length)
+ {
+ _buffer = _builder.AllocateRun(_font, length, 0, 0);
+ }
- var glyphs = buffer.GetGlyphSpan();
+ public override Span GlyphIndices => _buffer.GetGlyphSpan();
+ }
- for (int i = 0; i < glyphs.Length; i++)
- {
- glyphs[i] = glyphRun.GlyphIndices[i];
- }
+ private sealed class SKHorizontalGlyphRunBuffer : SKGlyphRunBufferBase, IHorizontalGlyphRunBuffer
+ {
+ private readonly SKHorizontalRunBuffer _buffer;
- textBlob = textBlobBuilder.Build();
+ public SKHorizontalGlyphRunBuffer(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length)
+ {
+ _buffer = _builder.AllocateHorizontalRun(_font, length, 0);
}
- return new GlyphRunImpl(textBlob);
+ public override Span GlyphIndices => _buffer.GetGlyphSpan();
+
+ public Span GlyphPositions => _buffer.GetPositionSpan();
}
- public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi)
+ private sealed class SKPositionedGlyphRunBuffer : SKGlyphRunBufferBase, IPositionedGlyphRunBuffer
{
- if (_skiaGpu is IOpenGlAwareSkiaGpu glAware)
- return glAware.CreateOpenGlBitmap(size, dpi);
- if (_skiaGpu == null)
- throw new PlatformNotSupportedException("GPU acceleration is not available");
- throw new PlatformNotSupportedException(
- "Current GPU acceleration backend does not support OpenGL integration");
- }
+ private readonly SKPositionedRunBuffer _buffer;
- public bool SupportsIndividualRoundRects => true;
+ public SKPositionedGlyphRunBuffer(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length)
+ {
+ _buffer = _builder.AllocatePositionedRun(_font, length);
+ }
- public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
+ public override Span GlyphIndices => _buffer.GetGlyphSpan();
- public PixelFormat DefaultPixelFormat { get; }
+ public Span GlyphPositions => MemoryMarshal.Cast(_buffer.GetPositionSpan());
+ }
}
}
diff --git a/src/Skia/Avalonia.Skia/TextShaperImpl.cs b/src/Skia/Avalonia.Skia/TextShaperImpl.cs
index 6da8a20f6a..b07deb1f4d 100644
--- a/src/Skia/Avalonia.Skia/TextShaperImpl.cs
+++ b/src/Skia/Avalonia.Skia/TextShaperImpl.cs
@@ -31,7 +31,7 @@ namespace Avalonia.Skia
buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture);
- var font = ((GlyphTypefaceImpl)typeface.PlatformImpl).Font;
+ var font = ((GlyphTypefaceImpl)typeface).Font;
font.Shape(buffer);
diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
index 81fa8c4bce..8103f89dad 100644
--- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
+++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
@@ -12,6 +12,8 @@ using SharpDX.DirectWrite;
using GlyphRun = Avalonia.Media.GlyphRun;
using TextAlignment = Avalonia.Media.TextAlignment;
using SharpDX.Mathematics.Interop;
+using System.Runtime.InteropServices;
+using System.Drawing;
namespace Avalonia
{
@@ -160,7 +162,7 @@ namespace Avalonia.Direct2D1
public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun)
{
- if (glyphRun.GlyphTypeface.PlatformImpl is not GlyphTypefaceImpl glyphTypeface)
+ if (glyphRun.GlyphTypeface is not GlyphTypefaceImpl glyphTypeface)
{
throw new InvalidOperationException("PlatformImpl can't be null.");
}
@@ -258,69 +260,66 @@ namespace Avalonia.Direct2D1
return new WicBitmapImpl(format, alphaFormat, data, size, dpi, stride);
}
- public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun)
+ private class DWGlyphRunBuffer : IGlyphRunBuffer
{
- var glyphTypeface = (GlyphTypefaceImpl)glyphRun.GlyphTypeface.PlatformImpl;
+ protected readonly SharpDX.DirectWrite.GlyphRun _dwRun;
- var glyphCount = glyphRun.GlyphIndices.Count;
-
- var run = new SharpDX.DirectWrite.GlyphRun
+ public DWGlyphRunBuffer(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
{
- FontFace = glyphTypeface.FontFace,
- FontSize = (float)glyphRun.FontRenderingEmSize
- };
-
- var indices = new short[glyphCount];
+ var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface;
- for (var i = 0; i < glyphCount; i++)
- {
- indices[i] = (short)glyphRun.GlyphIndices[i];
+ _dwRun = new SharpDX.DirectWrite.GlyphRun
+ {
+ FontFace = glyphTypefaceImpl.FontFace,
+ FontSize = fontRenderingEmSize,
+ Indices = new short[length]
+ };
}
- run.Indices = indices;
-
- run.Advances = new float[glyphCount];
+ public Span GlyphIndices => MemoryMarshal.Cast(_dwRun.Indices.AsSpan());
- var scale = (float)(glyphRun.FontRenderingEmSize / glyphTypeface.DesignEmHeight);
-
- if (glyphRun.GlyphAdvances == null)
+ public IGlyphRunImpl Build()
{
- for (var i = 0; i < glyphCount; i++)
- {
- var advance = glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]) * scale;
-
- run.Advances[i] = advance;
- }
+ return new GlyphRunImpl(_dwRun);
}
- else
- {
- for (var i = 0; i < glyphCount; i++)
- {
- var advance = (float)glyphRun.GlyphAdvances[i];
+ }
- run.Advances[i] = advance;
- }
+ private class DWHorizontalGlyphRunBuffer : DWGlyphRunBuffer, IHorizontalGlyphRunBuffer
+ {
+ public DWHorizontalGlyphRunBuffer(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ : base(glyphTypeface, fontRenderingEmSize, length)
+ {
+ _dwRun.Advances = new float[length];
}
- if (glyphRun.GlyphOffsets == null)
+ public Span GlyphPositions => _dwRun.Advances.AsSpan();
+ }
+
+ private class DWPositionedGlyphRunBuffer : DWGlyphRunBuffer, IPositionedGlyphRunBuffer
+ {
+ public DWPositionedGlyphRunBuffer(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ : base(glyphTypeface, fontRenderingEmSize, length)
{
- return new GlyphRunImpl(run);
+ _dwRun.Advances = new float[length];
+ _dwRun.Offsets = new GlyphOffset[length];
}
- run.Offsets = new GlyphOffset[glyphCount];
+ public Span GlyphPositions => MemoryMarshal.Cast(_dwRun.Offsets.AsSpan());
+ }
- for (var i = 0; i < glyphCount; i++)
- {
- var (x, y) = glyphRun.GlyphOffsets[i];
+ public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ return new DWGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length);
+ }
- run.Offsets[i] = new GlyphOffset
- {
- AdvanceOffset = (float)x,
- AscenderOffset = (float)y
- };
- }
+ public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ return new DWHorizontalGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length);
+ }
- return new GlyphRunImpl(run);
+ public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ return new DWPositionedGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length);
}
public bool SupportsIndividualRoundRects => false;
diff --git a/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs
index c996337520..b98ed3ffe6 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs
@@ -62,7 +62,7 @@ namespace Avalonia.Direct2D1.Media
return false;
}
- public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface)
+ public IGlyphTypeface CreateGlyphTypeface(Typeface typeface)
{
return new GlyphTypefaceImpl(typeface);
}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs
index 4154b44702..77d0e58d3d 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs
@@ -1,14 +1,15 @@
using System;
+using System.Drawing.Drawing2D;
using Avalonia.Media;
using Avalonia.Metadata;
-using Avalonia.Platform;
using HarfBuzzSharp;
using SharpDX.DirectWrite;
+using FontMetrics = Avalonia.Media.FontMetrics;
namespace Avalonia.Direct2D1.Media
{
[Unstable]
- public class GlyphTypefaceImpl : IGlyphTypefaceImpl
+ public class GlyphTypefaceImpl : IGlyphTypeface
{
private bool _isDisposed;
@@ -26,40 +27,28 @@ namespace Avalonia.Direct2D1.Media
Font.GetScale(out var xScale, out _);
- DesignEmHeight = (short)xScale;
-
if (!Font.TryGetHorizontalFontExtents(out var fontExtents))
{
Font.TryGetVerticalFontExtents(out fontExtents);
}
- Ascent = -fontExtents.Ascender;
-
- Descent = -fontExtents.Descender;
-
- LineGap = fontExtents.LineGap;
-
- if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineOffset, out var underlinePosition))
- {
- UnderlinePosition = underlinePosition;
- }
-
- if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineSize, out var underlineThickness))
- {
- UnderlineThickness = underlineThickness;
- }
+ Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineOffset, out var underlinePosition);
+ Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineSize, out var underlineThickness);
+ Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutOffset, out var strikethroughPosition);
+ Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutSize, out var strikethroughThickness);
- if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutOffset, out var strikethroughPosition))
+ Metrics = new FontMetrics
{
- StrikethroughPosition = strikethroughPosition;
- }
-
- if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutSize, out var strikethroughThickness))
- {
- StrikethroughThickness = strikethroughThickness;
- }
-
- IsFixedPitch = FontFace.IsMonospacedFont;
+ DesignEmHeight = (short)xScale,
+ Ascent = -fontExtents.Ascender,
+ Descent = -fontExtents.Descender,
+ LineGap = fontExtents.LineGap,
+ UnderlinePosition = underlinePosition,
+ UnderlineThickness = underlineThickness,
+ StrikethroughPosition = strikethroughPosition,
+ StrikethroughThickness = strikethroughThickness,
+ IsFixedPitch = FontFace.IsMonospacedFont
+ };
}
private Blob GetTable(Face face, Tag tag)
@@ -89,35 +78,11 @@ namespace Avalonia.Direct2D1.Media
public HarfBuzzSharp.Font Font { get; }
- ///
- public short DesignEmHeight { get; }
-
- ///
- public int Ascent { get; }
-
- ///
- public int Descent { get; }
-
- ///
- public int LineGap { get; }
+ public FontMetrics Metrics { get; }
- //ToDo: Read font table for these values
- ///
- public int UnderlinePosition { get; }
+ public int GlyphCount { get; set; }
- ///
- public int UnderlineThickness { get; }
-
- ///
- public int StrikethroughPosition { get; }
-
- ///
- public int StrikethroughThickness { get; }
-
- ///
- public bool IsFixedPitch { get; }
-
- ///
+ ///
public ushort GetGlyph(uint codepoint)
{
if (Font.TryGetGlyph(codepoint, out var glyph))
@@ -128,7 +93,14 @@ namespace Avalonia.Direct2D1.Media
return 0;
}
- ///
+ public bool TryGetGlyph(uint codepoint, out ushort glyph)
+ {
+ glyph = GetGlyph(codepoint);
+
+ return glyph != 0;
+ }
+
+ ///
public ushort[] GetGlyphs(ReadOnlySpan codepoints)
{
var glyphs = new ushort[codepoints.Length];
@@ -144,13 +116,13 @@ namespace Avalonia.Direct2D1.Media
return glyphs;
}
- ///
+ ///
public int GetGlyphAdvance(ushort glyph)
{
return Font.GetHorizontalGlyphAdvance(glyph);
}
- ///
+ ///
public int[] GetGlyphAdvances(ReadOnlySpan glyphs)
{
var glyphIndices = new uint[glyphs.Length];
@@ -187,6 +159,21 @@ namespace Avalonia.Direct2D1.Media
Dispose(true);
GC.SuppressFinalize(this);
}
+
+ public bool TryGetTable(uint tag, out byte[] table)
+ {
+ table = null;
+ var blob = Face.ReferenceTable(tag);
+
+ if (blob.Length > 0)
+ {
+ table = blob.AsSpan().ToArray();
+
+ return true;
+ }
+
+ return false;
+ }
}
}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs
index bda36e6c8c..064320f809 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs
@@ -31,7 +31,7 @@ namespace Avalonia.Direct2D1.Media
buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture);
- var font = ((GlyphTypefaceImpl)typeface.PlatformImpl).Font;
+ var font = ((GlyphTypefaceImpl)typeface).Font;
font.Shape(buffer);
diff --git a/src/iOS/Avalonia.iOS/TextInputResponder.cs b/src/iOS/Avalonia.iOS/TextInputResponder.cs
index feb8ef1bb7..d5723d78f7 100644
--- a/src/iOS/Avalonia.iOS/TextInputResponder.cs
+++ b/src/iOS/Avalonia.iOS/TextInputResponder.cs
@@ -405,7 +405,7 @@ partial class AvaloniaView
// TODO: Query from the input client
Logger.TryGet(LogEventLevel.Debug, ImeLog)?
.Log(null, "IUITextInput:GetSelectionRect");
- return new UITextSelectionRect[0];
+ return Array.Empty();
}
[Export("textStylingAtPosition:inDirection:")]
diff --git a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
index dadef1cb9c..0c0fe5b921 100644
--- a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
+++ b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
@@ -3,6 +3,10 @@
Exe
net461;netcoreapp2.0
+
+
+ false
+
diff --git a/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs
index cd0586233f..df16c1b34f 100644
--- a/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs
@@ -185,7 +185,7 @@ namespace Avalonia.Base.UnitTests.Media
var characters = new ReadOnlySlice(Enumerable.Repeat('a', count).ToArray(), start, count);
- return new GlyphRun(new GlyphTypeface(new MockGlyphTypeface()), 10, characters, glyphIndices, glyphAdvances,
+ return new GlyphRun(new MockGlyphTypeface(), 10, characters, glyphIndices, glyphAdvances,
glyphClusters: glyphClusters, biDiLevel: bidiLevel);
}
}
diff --git a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs
index 26a1ab88c7..5f8eb45f71 100644
--- a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs
+++ b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs
@@ -126,6 +126,21 @@ namespace Avalonia.Base.UnitTests.VisualTree
throw new NotImplementedException();
}
+ public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ throw new NotImplementedException();
+ }
+
class MockStreamGeometry : IStreamGeometryImpl
{
private MockStreamGeometryContext _impl = new MockStreamGeometryContext();
diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
index 0193f5d772..34f0dfef11 100644
--- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
+++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
@@ -112,12 +112,22 @@ namespace Avalonia.Benchmarks
return new MockFontManagerImpl();
}
- public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun)
+ public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun)
{
- return new NullGlyphRun();
+ return new MockStreamGeometryImpl();
}
- public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun)
+ public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
{
throw new NotImplementedException();
}
diff --git a/tests/Avalonia.Controls.UnitTests/RichTextBlockTests.cs b/tests/Avalonia.Controls.UnitTests/RichTextBlockTests.cs
index c74f13b808..05007e4f2e 100644
--- a/tests/Avalonia.Controls.UnitTests/RichTextBlockTests.cs
+++ b/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((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);
+ }
+ }
}
}
diff --git a/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs b/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs
index 7eeab2f7f2..2dd849bee1 100644
--- a/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs
+++ b/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs
@@ -109,7 +109,7 @@ namespace Avalonia.IntegrationTests.Appium
using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Owned, WindowStartupLocation.Manual))
{
- mainWindow.Click();
+ mainWindow.SendClick();
var secondaryWindowIndex = GetWindowOrder("SecondaryWindow");
Assert.Equal(1, secondaryWindowIndex);
}
diff --git a/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs b/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs
index 7d0cf05b0b..7998c95877 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs
@@ -64,7 +64,7 @@ namespace Avalonia.Skia.UnitTests.Media
return true;
}
- public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface)
+ public IGlyphTypeface CreateGlyphTypeface(Typeface typeface)
{
SKTypeface skTypeface;
diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs
index 960c409058..a315158e1b 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs
@@ -237,7 +237,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var glyph = typeface.GlyphTypeface.GetGlyph('a');
var advance = typeface.GlyphTypeface.GetGlyphAdvance(glyph) *
- (12.0 / typeface.GlyphTypeface.DesignEmHeight);
+ (12.0 / typeface.GlyphTypeface.Metrics.DesignEmHeight);
var paragraphWidth = advance * numberOfCharactersPerLine;
diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs
index e21986d3a2..d6da2c77c4 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs
@@ -578,9 +578,9 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
{
var glyphTypeface = Typeface.Default.GlyphTypeface;
- var emHeight = glyphTypeface.DesignEmHeight;
+ var emHeight = glyphTypeface.Metrics.DesignEmHeight;
- var lineHeight = (glyphTypeface.Descent - glyphTypeface.Ascent) * (12.0 / emHeight);
+ var lineHeight = glyphTypeface.Metrics.LineSpacing * (12.0 / emHeight);
var layout = new TextLayout(
text,
diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs
index 251c850fc8..33c18c5064 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs
@@ -12,7 +12,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
{
public class TextLineTests
{
- private static readonly string s_multiLineText = "012345678\r\r0123456789";
+ private const string s_multiLineText = "012345678\r\r0123456789";
[Fact]
public void Should_Get_First_CharacterHit()
diff --git a/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs b/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs
index 9f958bdcdd..2b1685f358 100644
--- a/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs
+++ b/tests/Avalonia.UnitTests/HarfBuzzFontManagerImpl.cs
@@ -58,7 +58,7 @@ namespace Avalonia.UnitTests
return false;
}
- public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface)
+ public IGlyphTypeface CreateGlyphTypeface(Typeface typeface)
{
var fontFamily = typeface.FontFamily;
diff --git a/tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs b/tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs
index 32e0434cd4..3bcbc2efbd 100644
--- a/tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs
+++ b/tests/Avalonia.UnitTests/HarfBuzzGlyphTypefaceImpl.cs
@@ -1,11 +1,11 @@
using System;
using System.IO;
-using Avalonia.Platform;
+using Avalonia.Media;
using HarfBuzzSharp;
namespace Avalonia.UnitTests
{
- public class HarfBuzzGlyphTypefaceImpl : IGlyphTypefaceImpl
+ public class HarfBuzzGlyphTypefaceImpl : IGlyphTypeface
{
private bool _isDisposed;
private Blob _blob;
@@ -21,70 +21,49 @@ namespace Avalonia.UnitTests
Font.SetFunctionsOpenType();
Font.GetScale(out var scale, out _);
-
- DesignEmHeight = (short)scale;
-
- var metrics = Font.OpenTypeMetrics;
const double defaultFontRenderingEmSize = 12.0;
- Ascent = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalAscender) / defaultFontRenderingEmSize * DesignEmHeight);
+ var metrics = Font.OpenTypeMetrics;
- Descent = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalDescender) / defaultFontRenderingEmSize * DesignEmHeight);
+ Metrics = new FontMetrics
+ {
+ DesignEmHeight = (short)scale,
+ Ascent = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalAscender) / defaultFontRenderingEmSize * scale),
+ Descent = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalDescender) / defaultFontRenderingEmSize * scale),
+ LineGap = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalLineGap) / defaultFontRenderingEmSize * scale),
- LineGap = (int)(metrics.GetXVariation(OpenTypeMetricsTag.HorizontalLineGap) / defaultFontRenderingEmSize * DesignEmHeight);
+ UnderlinePosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineOffset) / defaultFontRenderingEmSize * scale),
- UnderlinePosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineOffset) / defaultFontRenderingEmSize * DesignEmHeight);
+ UnderlineThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineSize) / defaultFontRenderingEmSize * scale),
- UnderlineThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.UnderlineSize) / defaultFontRenderingEmSize * DesignEmHeight);
+ StrikethroughPosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutOffset) / defaultFontRenderingEmSize * scale),
- StrikethroughPosition = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutOffset) / defaultFontRenderingEmSize * DesignEmHeight);
+ StrikethroughThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutSize) / defaultFontRenderingEmSize * scale),
- StrikethroughThickness = (int)(metrics.GetXVariation(OpenTypeMetricsTag.StrikeoutSize) / defaultFontRenderingEmSize * DesignEmHeight);
+ IsFixedPitch = GetGlyphAdvance(GetGlyph('a')) == GetGlyphAdvance(GetGlyph('b'))
+ };
- IsFixedPitch = GetGlyphAdvance(GetGlyph('a')) == GetGlyphAdvance(GetGlyph('b'));
+ GlyphCount = Face.GlyphCount;
IsFakeBold = isFakeBold;
IsFakeItalic = isFakeItalic;
}
+ public FontMetrics Metrics { get; }
+
public Face Face { get; }
public Font Font { get; }
- ///
- public short DesignEmHeight { get; }
-
- ///
- public int Ascent { get; }
-
- ///
- public int Descent { get; }
-
- ///
- public int LineGap { get; }
-
- ///
- public int UnderlinePosition { get; }
-
- ///
- public int UnderlineThickness { get; }
-
- ///
- public int StrikethroughPosition { get; }
-
- ///
- public int StrikethroughThickness { get; }
-
- ///
- public bool IsFixedPitch { get; }
+ public int GlyphCount { get; set; }
public bool IsFakeBold { get; }
public bool IsFakeItalic { get; }
- ///
+ ///
public ushort GetGlyph(uint codepoint)
{
if (Font.TryGetGlyph(codepoint, out var glyph))
@@ -95,7 +74,21 @@ namespace Avalonia.UnitTests
return 0;
}
- ///
+ public bool TryGetGlyph(uint codepoint,out ushort glyph)
+ {
+ glyph = 0;
+
+ if (Font.TryGetGlyph(codepoint, out var glyphId))
+ {
+ glyph = (ushort)glyphId;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
public ushort[] GetGlyphs(ReadOnlySpan codepoints)
{
var glyphs = new ushort[codepoints.Length];
@@ -111,13 +104,13 @@ namespace Avalonia.UnitTests
return glyphs;
}
- ///
+ ///
public int GetGlyphAdvance(ushort glyph)
{
return Font.GetHorizontalGlyphAdvance(glyph);
}
- ///
+ ///
public int[] GetGlyphAdvances(ReadOnlySpan glyphs)
{
var glyphIndices = new uint[glyphs.Length];
@@ -130,6 +123,21 @@ namespace Avalonia.UnitTests
return Font.GetHorizontalGlyphAdvances(glyphIndices);
}
+ public bool TryGetTable(uint tag, out byte[] table)
+ {
+ table = null;
+ var blob = Face.ReferenceTable(tag);
+
+ if (blob.Length > 0)
+ {
+ table = blob.AsSpan().ToArray();
+
+ return true;
+ }
+
+ return false;
+ }
+
private void Dispose(bool disposing)
{
if (_isDisposed)
diff --git a/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs b/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs
index 459ae3a549..7b7488bd5a 100644
--- a/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs
+++ b/tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs
@@ -30,7 +30,7 @@ namespace Avalonia.UnitTests
buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture);
- var font = ((HarfBuzzGlyphTypefaceImpl)typeface.PlatformImpl).Font;
+ var font = ((HarfBuzzGlyphTypefaceImpl)typeface).Font;
font.Shape(buffer);
diff --git a/tests/Avalonia.UnitTests/MockFontManagerImpl.cs b/tests/Avalonia.UnitTests/MockFontManagerImpl.cs
index e2678394df..e9b923a367 100644
--- a/tests/Avalonia.UnitTests/MockFontManagerImpl.cs
+++ b/tests/Avalonia.UnitTests/MockFontManagerImpl.cs
@@ -33,7 +33,7 @@ namespace Avalonia.UnitTests
return false;
}
- public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface)
+ public IGlyphTypeface CreateGlyphTypeface(Typeface typeface)
{
return new MockGlyphTypeface();
}
diff --git a/tests/Avalonia.UnitTests/MockGlyphTypeface.cs b/tests/Avalonia.UnitTests/MockGlyphTypeface.cs
index c9c59a6e32..a1c492a7f1 100644
--- a/tests/Avalonia.UnitTests/MockGlyphTypeface.cs
+++ b/tests/Avalonia.UnitTests/MockGlyphTypeface.cs
@@ -1,19 +1,19 @@
using System;
-using Avalonia.Platform;
+using Avalonia.Media;
namespace Avalonia.UnitTests
{
- public class MockGlyphTypeface : IGlyphTypefaceImpl
+ public class MockGlyphTypeface : IGlyphTypeface
{
- public short DesignEmHeight => 10;
- public int Ascent => 2;
- public int Descent => 10;
- public int LineGap { get; }
- public int UnderlinePosition { get; }
- public int UnderlineThickness { get; }
- public int StrikethroughPosition { get; }
- public int StrikethroughThickness { get; }
- public bool IsFixedPitch { get; }
+ public FontMetrics Metrics => new FontMetrics
+ {
+ DesignEmHeight = 10,
+ Ascent = 2,
+ Descent = 10,
+ IsFixedPitch = true
+ };
+
+ public int GlyphCount => 1337;
public ushort GetGlyph(uint codepoint)
{
@@ -30,6 +30,13 @@ namespace Avalonia.UnitTests
return 8;
}
+ public bool TryGetGlyph(uint codepoint, out ushort glyph)
+ {
+ glyph = 8;
+
+ return true;
+ }
+
public int[] GetGlyphAdvances(ReadOnlySpan glyphs)
{
var advances = new int[glyphs.Length];
@@ -43,5 +50,11 @@ namespace Avalonia.UnitTests
}
public void Dispose() { }
+
+ public bool TryGetTable(uint tag, out byte[] table)
+ {
+ table = null;
+ return false;
+ }
}
}
diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
index f6a127b573..586436ef7f 100644
--- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
+++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
@@ -152,6 +152,21 @@ namespace Avalonia.UnitTests
return Mock.Of();
}
+ public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ return Mock.Of();
+ }
+
+ public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ return Mock.Of();
+ }
+
+ public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ return Mock.Of();
+ }
+
public bool SupportsIndividualRoundRects { get; set; }
public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;