diff --git a/.editorconfig b/.editorconfig
index 337760636b..9014c8938f 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -141,6 +141,8 @@ dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomme
dotnet_diagnostic.CA1802.severity = warning
# CA1825: Avoid zero-length array allocations
dotnet_diagnostic.CA1825.severity = warning
+# CA1821: Remove empty finalizers
+dotnet_diagnostic.CA1821.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/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/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/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.DataGrid/Collections/DataGridCollectionView.cs b/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs
index 906ec661ae..30517f4b00 100644
--- a/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs
+++ b/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs
@@ -3412,23 +3412,30 @@ namespace Avalonia.Collections
RefreshOrDefer();
return;
}
-
- object addedItem = args.NewItems?[0];
- object removedItem = args.OldItems?[0];
-
+
// fire notifications for removes
- if (args.Action == NotifyCollectionChangedAction.Remove ||
- args.Action == NotifyCollectionChangedAction.Replace)
+ if (args.OldItems != null &&
+ (args.Action == NotifyCollectionChangedAction.Remove ||
+ args.Action == NotifyCollectionChangedAction.Replace))
{
- ProcessRemoveEvent(removedItem, args.Action == NotifyCollectionChangedAction.Replace);
+ foreach (var removedItem in args.OldItems)
+ {
+ ProcessRemoveEvent(removedItem, args.Action == NotifyCollectionChangedAction.Replace);
+ }
}
// fire notifications for adds
- if ((args.Action == NotifyCollectionChangedAction.Add ||
- args.Action == NotifyCollectionChangedAction.Replace) &&
- (Filter == null || PassesFilter(addedItem)))
+ if (args.NewItems != null &&
+ (args.Action == NotifyCollectionChangedAction.Add ||
+ args.Action == NotifyCollectionChangedAction.Replace))
{
- ProcessAddEvent(addedItem, args.NewStartingIndex);
+ for (var i = 0; i < args.NewItems.Count; i++)
+ {
+ if (Filter == null || PassesFilter(args.NewItems[i]))
+ {
+ ProcessAddEvent(args.NewItems[i], args.NewStartingIndex + i);
+ }
+ }
}
if (args.Action != NotifyCollectionChangedAction.Replace)
{
diff --git a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
index fb27feb521..bb05cd1b1f 100644
--- a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
@@ -40,18 +40,6 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterDirect(nameof(DayVisible),
x => x.DayVisible, (x, v) => x.DayVisible = v);
- ///
- /// Defines the Property
- ///
- public static readonly StyledProperty
- 0,0,0,4
40
40
41
@@ -84,18 +83,8 @@
-
-
-
+