diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs
index 7e2bbc13bc..097815cc69 100644
--- a/nukebuild/Build.cs
+++ b/nukebuild/Build.cs
@@ -138,9 +138,19 @@ partial class Build : NukeBuild
.SetWorkingDirectory(webappDir)
.SetCommand("dist"));
});
-
- Target Compile => _ => _
+
+ Target CompileNative => _ => _
.DependsOn(Clean)
+ .OnlyWhenStatic(() => EnvironmentInfo.IsOsx)
+ .Executes(() =>
+ {
+ var project = $"{RootDirectory}/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/";
+ var args = $"-project {project} -configuration {Parameters.Configuration} CONFIGURATION_BUILD_DIR={RootDirectory}/Build/Products/Release";
+ ProcessTasks.StartProcess("xcodebuild", args).AssertZeroExitCode();
+ });
+
+ Target Compile => _ => _
+ .DependsOn(Clean, CompileNative)
.DependsOn(CompileHtmlPreviewer)
.Executes(async () =>
{
diff --git a/samples/ControlCatalog/Pages/DataGridPage.xaml b/samples/ControlCatalog/Pages/DataGridPage.xaml
index d045626c2c..cacc2204bd 100644
--- a/samples/ControlCatalog/Pages/DataGridPage.xaml
+++ b/samples/ControlCatalog/Pages/DataGridPage.xaml
@@ -11,12 +11,17 @@
-
+
DataGrid
A control for displaying and interacting with a data source.
-
+
+
+
+
+
+
@@ -39,13 +44,13 @@
-
+
-
-
-
+
+
+
diff --git a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
index 7f15845596..ddee880288 100644
--- a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
+++ b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
@@ -61,7 +61,6 @@ namespace RenderDemo.Pages
{
Foreground = Brushes.Black,
GlyphRun = new GlyphRun(_glyphTypeface, _fontSize, _glyphIndices),
- BaselineOrigin = new Point(0, -_glyphTypeface.Ascent * scale)
};
drawingGroup.Children.Add(glyphRunDrawing);
@@ -69,7 +68,7 @@ namespace RenderDemo.Pages
var geometryDrawing = new GeometryDrawing
{
Pen = new Pen(Brushes.Black),
- Geometry = new RectangleGeometry { Rect = glyphRunDrawing.GlyphRun.Bounds }
+ Geometry = new RectangleGeometry { Rect = new Rect(glyphRunDrawing.GlyphRun.Size) }
};
drawingGroup.Children.Add(geometryDrawing);
diff --git a/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs
index 3b61f6d898..f052283b22 100644
--- a/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs
+++ b/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs
@@ -63,12 +63,12 @@ namespace Avalonia.Data.Core.Plugins
{
if (errors.Count == 1)
{
- return new ValidationException(errors[0].ErrorMessage);
+ return new DataValidationException(errors[0].ErrorMessage);
}
else
{
return new AggregateException(
- errors.Select(x => new ValidationException(x.ErrorMessage)));
+ errors.Select(x => new DataValidationException(x.ErrorMessage)));
}
}
}
diff --git a/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt b/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt
new file mode 100644
index 0000000000..82472c505a
--- /dev/null
+++ b/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt
@@ -0,0 +1,5 @@
+Compat issues with assembly Avalonia.Controls.DataGrid:
+MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.DataGridTextColumn.FontFamilyProperty' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.String Avalonia.Controls.DataGridTextColumn.FontFamily.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void Avalonia.Controls.DataGridTextColumn.FontFamily.set(System.String)' does not exist in the implementation but it does exist in the contract.
+Total Issues: 3
diff --git a/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs
index f1bbea9949..e2a067ac61 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs
@@ -17,9 +17,7 @@ namespace Avalonia.Controls
///
public class DataGridCheckBoxColumn : DataGridBoundColumn
{
-
private bool _beganEditWithKeyboard;
- private bool _isThreeState;
private CheckBox _currentCheckBox;
private DataGrid _owningGrid;
@@ -31,6 +29,12 @@ namespace Avalonia.Controls
BindingTarget = CheckBox.IsCheckedProperty;
}
+ ///
+ /// Defines the property.
+ ///
+ public static StyledProperty IsThreeStateProperty =
+ CheckBox.IsThreeStateProperty.AddOwner();
+
///
/// Gets or sets a value that indicates whether the hosted controls allow three states or two.
///
@@ -39,17 +43,17 @@ namespace Avalonia.Controls
///
public bool IsThreeState
{
- get
- {
- return _isThreeState;
- }
- set
+ get => GetValue(IsThreeStateProperty);
+ set => SetValue(IsThreeStateProperty, value);
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == IsThreeStateProperty)
{
- if (_isThreeState != value)
- {
- _isThreeState = value;
- NotifyPropertyChanged(nameof(IsThreeState));
- }
+ NotifyPropertyChanged(change.Property.Name);
}
}
@@ -203,9 +207,9 @@ namespace Avalonia.Controls
{
throw new ArgumentNullException("element");
}
- if(element is CheckBox checkBox)
+ if (element is CheckBox checkBox)
{
- checkBox.IsThreeState = IsThreeState;
+ DataGridHelper.SyncColumnProperty(this, checkBox, IsThreeStateProperty);
}
else
{
@@ -229,7 +233,7 @@ namespace Avalonia.Controls
{
checkBox.HorizontalAlignment = HorizontalAlignment.Center;
checkBox.VerticalAlignment = VerticalAlignment.Center;
- checkBox.IsThreeState = IsThreeState;
+ DataGridHelper.SyncColumnProperty(this, checkBox, IsThreeStateProperty);
}
private bool EnsureOwningGrid()
diff --git a/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs
index d31204b9e6..1cf6ab68ac 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs
@@ -20,11 +20,6 @@ namespace Avalonia.Controls
{
private const string DATAGRID_TextColumnCellTextBlockMarginKey = "DataGridTextColumnCellTextBlockMargin";
- private double? _fontSize;
- private FontStyle? _fontStyle;
- private FontWeight? _fontWeight;
- private IBrush _foreground;
-
///
/// Initializes a new instance of the class.
///
@@ -36,18 +31,24 @@ namespace Avalonia.Controls
///
/// Identifies the FontFamily dependency property.
///
- public static readonly StyledProperty FontFamilyProperty =
- AvaloniaProperty.Register(nameof(FontFamily));
+ public static readonly AttachedProperty FontFamilyProperty =
+ TextBlock.FontFamilyProperty.AddOwner();
///
/// Gets or sets the font name.
///
- public string FontFamily
+ public FontFamily FontFamily
{
- get { return GetValue(FontFamilyProperty); }
- set { SetValue(FontFamilyProperty, value); }
+ get => GetValue(FontFamilyProperty);
+ set => SetValue(FontFamilyProperty, value);
}
+ ///
+ /// Identifies the FontSize dependency property.
+ ///
+ public static readonly AttachedProperty FontSizeProperty =
+ TextBlock.FontSizeProperty.AddOwner();
+
///
/// Gets or sets the font size.
///
@@ -55,74 +56,66 @@ namespace Avalonia.Controls
[DefaultValue(double.NaN)]
public double FontSize
{
- get
- {
- return _fontSize ?? Double.NaN;
- }
- set
- {
- if (_fontSize != value)
- {
- _fontSize = value;
- NotifyPropertyChanged(nameof(FontSize));
- }
- }
+ get => GetValue(FontSizeProperty);
+ set => SetValue(FontSizeProperty, value);
}
+ ///
+ /// Identifies the FontStyle dependency property.
+ ///
+ public static readonly AttachedProperty FontStyleProperty =
+ TextBlock.FontStyleProperty.AddOwner();
+
///
/// Gets or sets the font style.
///
public FontStyle FontStyle
{
- get
- {
- return _fontStyle ?? FontStyle.Normal;
- }
- set
- {
- if (_fontStyle != value)
- {
- _fontStyle = value;
- NotifyPropertyChanged(nameof(FontStyle));
- }
- }
+ get => GetValue(FontStyleProperty);
+ set => SetValue(FontStyleProperty, value);
}
+ ///
+ /// Identifies the FontWeight dependency property.
+ ///
+ public static readonly AttachedProperty FontWeightProperty =
+ TextBlock.FontWeightProperty.AddOwner();
+
///
/// Gets or sets the font weight or thickness.
///
public FontWeight FontWeight
{
- get
- {
- return _fontWeight ?? FontWeight.Normal;
- }
- set
- {
- if (_fontWeight != value)
- {
- _fontWeight = value;
- NotifyPropertyChanged(nameof(FontWeight));
- }
- }
+ get => GetValue(FontWeightProperty);
+ set => SetValue(FontWeightProperty, value);
}
+ ///
+ /// Identifies the Foreground dependency property.
+ ///
+ public static readonly AttachedProperty ForegroundProperty =
+ TextBlock.ForegroundProperty.AddOwner();
+
///
/// Gets or sets a brush that describes the foreground of the column cells.
///
public IBrush Foreground
{
- get
- {
- return _foreground;
- }
- set
+ get => GetValue(ForegroundProperty);
+ set => SetValue(ForegroundProperty, value);
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == FontFamilyProperty
+ || change.Property == FontSizeProperty
+ || change.Property == FontStyleProperty
+ || change.Property == FontWeightProperty
+ || change.Property == ForegroundProperty)
{
- if (_foreground != value)
- {
- _foreground = value;
- NotifyPropertyChanged(nameof(Foreground));
- }
+ NotifyPropertyChanged(change.Property.Name);
}
}
@@ -154,26 +147,7 @@ namespace Avalonia.Controls
Background = new SolidColorBrush(Colors.Transparent)
};
- if (IsSet(FontFamilyProperty))
- {
- textBox.FontFamily = FontFamily;
- }
- if (_fontSize.HasValue)
- {
- textBox.FontSize = _fontSize.Value;
- }
- if (_fontStyle.HasValue)
- {
- textBox.FontStyle = _fontStyle.Value;
- }
- if (_fontWeight.HasValue)
- {
- textBox.FontWeight = _fontWeight.Value;
- }
- if (_foreground != null)
- {
- textBox.Foreground = _foreground;
- }
+ SyncProperties(textBox);
return textBox;
}
@@ -192,26 +166,8 @@ namespace Avalonia.Controls
VerticalAlignment = VerticalAlignment.Center
};
- if (IsSet(FontFamilyProperty))
- {
- textBlockElement.FontFamily = FontFamily;
- }
- if (_fontSize.HasValue)
- {
- textBlockElement.FontSize = _fontSize.Value;
- }
- if (_fontStyle.HasValue)
- {
- textBlockElement.FontStyle = _fontStyle.Value;
- }
- if (_fontWeight.HasValue)
- {
- textBlockElement.FontWeight = _fontWeight.Value;
- }
- if (_foreground != null)
- {
- textBlockElement.Foreground = _foreground;
- }
+ SyncProperties(textBlockElement);
+
if (Binding != null)
{
textBlockElement.Bind(TextBlock.TextProperty, Binding);
@@ -261,99 +217,42 @@ namespace Avalonia.Controls
throw new ArgumentNullException("element");
}
- if(element is TextBox textBox)
+ if (element is AvaloniaObject content)
{
if (propertyName == nameof(FontFamily))
{
- textBox.FontFamily = FontFamily;
+ DataGridHelper.SyncColumnProperty(this, content, FontFamilyProperty);
}
else if (propertyName == nameof(FontSize))
{
- SetTextFontSize(textBox, TextBox.FontSizeProperty);
+ DataGridHelper.SyncColumnProperty(this, content, FontSizeProperty);
}
else if (propertyName == nameof(FontStyle))
{
- textBox.FontStyle = FontStyle;
+ DataGridHelper.SyncColumnProperty(this, content, FontStyleProperty);
}
else if (propertyName == nameof(FontWeight))
{
- textBox.FontWeight = FontWeight;
+ DataGridHelper.SyncColumnProperty(this, content, FontWeightProperty);
}
else if (propertyName == nameof(Foreground))
{
- textBox.Foreground = Foreground;
- }
- else
- {
- if (FontFamily != null)
- {
- textBox.FontFamily = FontFamily;
- }
- SetTextFontSize(textBox, TextBox.FontSizeProperty);
- textBox.FontStyle = FontStyle;
- textBox.FontWeight = FontWeight;
- if (Foreground != null)
- {
- textBox.Foreground = Foreground;
- }
- }
-
- }
- else if (element is TextBlock textBlock)
- {
- if (propertyName == nameof(FontFamily))
- {
- textBlock.FontFamily = FontFamily;
- }
- else if (propertyName == nameof(FontSize))
- {
- SetTextFontSize(textBlock, TextBlock.FontSizeProperty);
- }
- else if (propertyName == nameof(FontStyle))
- {
- textBlock.FontStyle = FontStyle;
- }
- else if (propertyName == nameof(FontWeight))
- {
- textBlock.FontWeight = FontWeight;
- }
- else if (propertyName == nameof(Foreground))
- {
- textBlock.Foreground = Foreground;
- }
- else
- {
- if (FontFamily != null)
- {
- textBlock.FontFamily = FontFamily;
- }
- SetTextFontSize(textBlock, TextBlock.FontSizeProperty);
- textBlock.FontStyle = FontStyle;
- textBlock.FontWeight = FontWeight;
- if (Foreground != null)
- {
- textBlock.Foreground = Foreground;
- }
+ DataGridHelper.SyncColumnProperty(this, content, ForegroundProperty);
}
}
else
{
- throw DataGridError.DataGrid.ValueIsNotAnInstanceOfEitherOr("element", typeof(TextBox), typeof(TextBlock));
+ throw DataGridError.DataGrid.ValueIsNotAnInstanceOf("element", typeof(AvaloniaObject));
}
}
- private void SetTextFontSize(AvaloniaObject textElement, AvaloniaProperty fontSizeProperty)
+ private void SyncProperties(AvaloniaObject content)
{
- double newFontSize = FontSize;
- if (double.IsNaN(newFontSize))
- {
- textElement.ClearValue(fontSizeProperty);
- }
- else
- {
- textElement.SetValue(fontSizeProperty, newFontSize);
- }
+ DataGridHelper.SyncColumnProperty(this, content, FontFamilyProperty);
+ DataGridHelper.SyncColumnProperty(this, content, FontSizeProperty);
+ DataGridHelper.SyncColumnProperty(this, content, FontStyleProperty);
+ DataGridHelper.SyncColumnProperty(this, content, FontWeightProperty);
+ DataGridHelper.SyncColumnProperty(this, content, ForegroundProperty);
}
-
}
}
diff --git a/src/Avalonia.Controls.DataGrid/Utils/DataGridHelper.cs b/src/Avalonia.Controls.DataGrid/Utils/DataGridHelper.cs
new file mode 100644
index 0000000000..2cf41082ca
--- /dev/null
+++ b/src/Avalonia.Controls.DataGrid/Utils/DataGridHelper.cs
@@ -0,0 +1,22 @@
+namespace Avalonia.Controls
+{
+ internal static class DataGridHelper
+ {
+ internal static void SyncColumnProperty(AvaloniaObject column, AvaloniaObject content, AvaloniaProperty property)
+ {
+ SyncColumnProperty(column, content, property, property);
+ }
+
+ internal static void SyncColumnProperty(AvaloniaObject column, AvaloniaObject content, AvaloniaProperty contentProperty, AvaloniaProperty columnProperty)
+ {
+ if (!column.IsSet(columnProperty))
+ {
+ content.ClearValue(contentProperty);
+ }
+ else
+ {
+ content.SetValue(contentProperty, column.GetValue(columnProperty));
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
index 046b55d49a..7c259f0a09 100644
--- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
+++ b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
@@ -420,7 +420,7 @@ namespace Avalonia.Controls
_calendar.DayButtonMouseUp -= Calendar_DayButtonMouseUp;
_calendar.DisplayDateChanged -= Calendar_DisplayDateChanged;
_calendar.SelectedDatesChanged -= Calendar_SelectedDatesChanged;
- _calendar.PointerPressed -= Calendar_PointerPressed;
+ _calendar.PointerReleased -= Calendar_PointerReleased;
_calendar.KeyDown -= Calendar_KeyDown;
}
_calendar = e.NameScope.Find(ElementCalendar);
@@ -435,7 +435,7 @@ namespace Avalonia.Controls
_calendar.DayButtonMouseUp += Calendar_DayButtonMouseUp;
_calendar.DisplayDateChanged += Calendar_DisplayDateChanged;
_calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
- _calendar.PointerPressed += Calendar_PointerPressed;
+ _calendar.PointerReleased += Calendar_PointerReleased;
_calendar.KeyDown += Calendar_KeyDown;
//_calendar.SizeChanged += new SizeChangedEventHandler(Calendar_SizeChanged);
//_calendar.IsTabStop = true;
@@ -831,9 +831,10 @@ namespace Avalonia.Controls
}
}
}
- private void Calendar_PointerPressed(object sender, PointerPressedEventArgs e)
+ private void Calendar_PointerReleased(object sender, PointerReleasedEventArgs e)
{
- if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
+
+ if (e.InitialPressMouseButton == MouseButton.Left)
{
e.Handled = true;
}
diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs
index 27313b0b4c..02a9daee75 100644
--- a/src/Avalonia.Controls/ComboBox.cs
+++ b/src/Avalonia.Controls/ComboBox.cs
@@ -257,7 +257,7 @@ namespace Avalonia.Controls
}
///
- protected override void OnPointerPressed(PointerPressedEventArgs e)
+ protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
if (!e.Handled)
{
@@ -276,7 +276,7 @@ namespace Avalonia.Controls
}
}
- base.OnPointerPressed(e);
+ base.OnPointerReleased(e);
}
///
diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs
index 3a098593dc..d1fbb25cb1 100644
--- a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs
+++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs
@@ -47,11 +47,7 @@ namespace Avalonia.Controls.Generators
{
var container = item as T;
- if (item == null)
- {
- return null;
- }
- else if (container != null)
+ if (container != null)
{
return container;
}
diff --git a/src/Avalonia.Controls/Primitives/AccessText.cs b/src/Avalonia.Controls/Primitives/AccessText.cs
index 89f672deaa..7a5e6ce426 100644
--- a/src/Avalonia.Controls/Primitives/AccessText.cs
+++ b/src/Avalonia.Controls/Primitives/AccessText.cs
@@ -126,7 +126,7 @@ namespace Avalonia.Controls.Primitives
if (shapedTextCharacters.GlyphRun.Characters.End < textPosition)
{
- currentX += shapedTextCharacters.GlyphRun.Bounds.Width;
+ currentX += shapedTextCharacters.Size.Width;
continue;
}
@@ -143,7 +143,7 @@ namespace Avalonia.Controls.Primitives
width = 0.0;
}
- return new Rect(currentX, currentY, width, shapedTextCharacters.GlyphRun.Bounds.Height);
+ return new Rect(currentX, currentY, width, shapedTextCharacters.Size.Height);
}
}
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index a676892384..1e5e80d144 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -128,6 +128,7 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty TopmostProperty =
AvaloniaProperty.Register(nameof(Topmost));
+ private bool _isOpenRequested = false;
private bool _isOpen;
private bool _ignoreIsOpenChanged;
private PopupOpenState? _openState;
@@ -361,17 +362,19 @@ namespace Avalonia.Controls.Primitives
if (placementTarget == null)
{
- throw new InvalidOperationException("Popup has no logical parent and PlacementTarget is null");
+ _isOpenRequested = true;
+ return;
}
var topLevel = placementTarget.VisualRoot as TopLevel;
if (topLevel == null)
{
- throw new InvalidOperationException(
- "Attempted to open a popup not attached to a TopLevel");
+ _isOpenRequested = true;
+ return;
}
+ _isOpenRequested = false;
var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver);
var handlerCleanup = new CompositeDisposable(5);
@@ -492,6 +495,17 @@ namespace Avalonia.Controls.Primitives
return new Size();
}
+
+ ///
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+ if (_isOpenRequested)
+ {
+ Open();
+ }
+ }
+
///
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
@@ -552,6 +566,7 @@ namespace Avalonia.Controls.Primitives
private void CloseCore()
{
+ _isOpenRequested = false;
if (_openState is null)
{
using (BeginIgnoringIsOpen())
diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs
index d8477840af..d61519e697 100644
--- a/src/Avalonia.Controls/TextBlock.cs
+++ b/src/Avalonia.Controls/TextBlock.cs
@@ -434,7 +434,10 @@ namespace Avalonia.Controls
var padding = Padding;
- TextLayout.Draw(context, new Point(padding.Left + offsetX, padding.Top));
+ using (context.PushPostTransform(Matrix.CreateTranslation(padding.Left + offsetX, padding.Top)))
+ {
+ TextLayout.Draw(context);
+ }
}
///
diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
index 3ae6c8c30e..4f6af0a41b 100644
--- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
+++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
@@ -385,7 +385,7 @@ namespace Avalonia.Headless
}
- public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin)
+ public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{
}
diff --git a/src/Avalonia.Native/Avalonia.Native.csproj b/src/Avalonia.Native/Avalonia.Native.csproj
index f084411c2f..49bd578290 100644
--- a/src/Avalonia.Native/Avalonia.Native.csproj
+++ b/src/Avalonia.Native/Avalonia.Native.csproj
@@ -1,7 +1,8 @@
- false
+ $([MSBuild]::IsOSPlatform(OSX))
+ $(PackAvaloniaNative)
true
netstandard2.0
/usr/bin/castxml
@@ -10,8 +11,9 @@
false
-
+
+ libAvaloniaNative.dylib
runtimes/osx/native/libAvaloniaNative.dylib
true
PreserveNewest
@@ -26,4 +28,4 @@
-
+
\ No newline at end of file
diff --git a/src/Avalonia.Themes.Default/ComboBox.xaml b/src/Avalonia.Themes.Default/ComboBox.xaml
index cced76e850..67151731a8 100644
--- a/src/Avalonia.Themes.Default/ComboBox.xaml
+++ b/src/Avalonia.Themes.Default/ComboBox.xaml
@@ -26,6 +26,7 @@
+
-
+
-
+
+
+
+
+
+
diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt
index 5aa497861d..148916932f 100644
--- a/src/Avalonia.Visuals/ApiCompatBaseline.txt
+++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt
@@ -1,15 +1,33 @@
Compat issues with assembly Avalonia.Visuals:
+MembersMustExist : Member 'public void Avalonia.Media.DrawingContext.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.GetOrAddTypeface(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.MatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.GlyphRun.Bounds.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Media.GlyphRunDrawing.BaselineOriginProperty' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public Avalonia.Point Avalonia.Media.GlyphRunDrawing.BaselineOrigin.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void Avalonia.Media.GlyphRunDrawing.BaselineOrigin.set(Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
CannotSealType : Type 'Avalonia.Media.Typeface' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
TypeCannotChangeClassification : Type 'Avalonia.Media.Typeface' is a 'struct' in the implementation but is a 'class' in the contract.
CannotMakeMemberNonVirtual : Member 'public System.Boolean Avalonia.Media.Typeface.Equals(System.Object)' is non-virtual in the implementation but is virtual in the contract.
CannotMakeMemberNonVirtual : Member 'public System.Int32 Avalonia.Media.Typeface.GetHashCode()' is non-virtual in the implementation but is virtual in the contract.
TypesMustExist : Type 'Avalonia.Media.Fonts.FontKey' does not exist in the implementation but it does exist in the contract.
+CannotAddAbstractMembers : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.DrawableTextRun.Size' is abstract in the implementation but is missing in the contract.
+MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.TextFormatting.DrawableTextRun.Bounds.get()' does not exist in the implementation but it does exist in the contract.
+CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' is abstract in the implementation but is missing in the contract.
+MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
+CannotAddAbstractMembers : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.DrawableTextRun.Size.get()' is abstract in the implementation but is missing in the contract.
+MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.TextFormatting.ShapedTextCharacters.Bounds.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextCharacters.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLayout.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak' is abstract in the implementation but is missing in the contract.
+CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext)' is abstract in the implementation but is missing in the contract.
+MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.LineBreak.get()' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak.get()' is abstract in the implementation but is missing in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun)' is present in the implementation but not in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' is present in the contract but not in the implementation.
+MembersMustExist : Member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract.
-Total Issues: 13
+Total Issues: 31
diff --git a/src/Avalonia.Visuals/Media/DrawingContext.cs b/src/Avalonia.Visuals/Media/DrawingContext.cs
index ba7191d7a6..ae4c927ae2 100644
--- a/src/Avalonia.Visuals/Media/DrawingContext.cs
+++ b/src/Avalonia.Visuals/Media/DrawingContext.cs
@@ -206,14 +206,13 @@ namespace Avalonia.Media
///
/// The foreground brush.
/// The glyph run.
- /// The baseline origin of the glyph run.
- public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin)
+ public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{
Contract.Requires(glyphRun != null);
if (foreground != null)
{
- PlatformImpl.DrawGlyphRun(foreground, glyphRun, baselineOrigin);
+ PlatformImpl.DrawGlyphRun(foreground, glyphRun);
}
}
diff --git a/src/Avalonia.Visuals/Media/GlyphRun.cs b/src/Avalonia.Visuals/Media/GlyphRun.cs
index da3a1f721c..14ab083b4f 100644
--- a/src/Avalonia.Visuals/Media/GlyphRun.cs
+++ b/src/Avalonia.Visuals/Media/GlyphRun.cs
@@ -16,8 +16,9 @@ namespace Avalonia.Media
private IGlyphRunImpl _glyphRunImpl;
private GlyphTypeface _glyphTypeface;
private double _fontRenderingEmSize;
- private Rect? _bounds;
+ private Size? _size;
private int _biDiLevel;
+ private Point? _baselineOrigin;
private ReadOnlySlice _glyphIndices;
private ReadOnlySlice _glyphAdvances;
@@ -89,6 +90,20 @@ namespace Avalonia.Media
set => Set(ref _fontRenderingEmSize, value);
}
+ ///
+ /// Gets or sets the baseline origin of the.
+ ///
+ public Point BaselineOrigin
+ {
+ get
+ {
+ _baselineOrigin ??= CalculateBaselineOrigin();
+
+ return _baselineOrigin.Value;
+ }
+ set => Set(ref _baselineOrigin, value);
+ }
+
///
/// Gets or sets an array of values that represent the glyph indices in the rendering physical font.
///
@@ -156,16 +171,13 @@ namespace Avalonia.Media
///
/// Gets or sets the conservative bounding box of the .
///
- public Rect Bounds
+ public Size Size
{
get
{
- if (_bounds == null)
- {
- _bounds = CalculateBounds();
- }
+ _size ??= CalculateSize();
- return _bounds.Value;
+ return _size.Value;
}
}
@@ -200,7 +212,7 @@ namespace Avalonia.Media
if (characterHit.FirstCharacterIndex + characterHit.TrailingLength > Characters.End)
{
- return Bounds.Width;
+ return Size.Width;
}
var glyphIndex = FindGlyphIndex(characterHit.FirstCharacterIndex);
@@ -257,7 +269,7 @@ namespace Avalonia.Media
}
//After
- if (distance > Bounds.Size.Width)
+ if (distance > Size.Width)
{
isInside = false;
@@ -529,12 +541,21 @@ namespace Avalonia.Media
}
///
- /// Calculates the bounds of the .
+ /// Calculates the default baseline origin of the .
+ ///
+ /// The baseline origin.
+ private Point CalculateBaselineOrigin()
+ {
+ return new Point(0, -GlyphTypeface.Ascent * Scale);
+ }
+
+ ///
+ /// Calculates the size of the .
///
///
/// The calculated bounds.
///
- private Rect CalculateBounds()
+ private Size CalculateSize()
{
var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale;
@@ -555,7 +576,7 @@ namespace Avalonia.Media
}
}
- return new Rect(0, GlyphTypeface.Ascent * Scale, width, height);
+ return new Size(width, height);
}
private void Set(ref T field, T value)
@@ -590,11 +611,15 @@ namespace Avalonia.Media
throw new InvalidOperationException();
}
+ _baselineOrigin = new Point(0, -GlyphTypeface.Ascent * Scale);
+
var platformRenderInterface = AvaloniaLocator.Current.GetService();
_glyphRunImpl = platformRenderInterface.CreateGlyphRun(this, out var width);
var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale;
+
+ _size = new Size(width, height);
}
void IDisposable.Dispose()
diff --git a/src/Avalonia.Visuals/Media/GlyphRunDrawing.cs b/src/Avalonia.Visuals/Media/GlyphRunDrawing.cs
index d0ea113a6f..7e0d5c3c81 100644
--- a/src/Avalonia.Visuals/Media/GlyphRunDrawing.cs
+++ b/src/Avalonia.Visuals/Media/GlyphRunDrawing.cs
@@ -8,9 +8,6 @@
public static readonly StyledProperty GlyphRunProperty =
AvaloniaProperty.Register(nameof(GlyphRun));
- public static readonly StyledProperty BaselineOriginProperty =
- AvaloniaProperty.Register(nameof(BaselineOrigin));
-
public IBrush Foreground
{
get => GetValue(ForegroundProperty);
@@ -23,12 +20,6 @@
set => SetValue(GlyphRunProperty, value);
}
- public Point BaselineOrigin
- {
- get => GetValue(BaselineOriginProperty);
- set => SetValue(BaselineOriginProperty, value);
- }
-
public override void Draw(DrawingContext context)
{
if (GlyphRun == null)
@@ -36,12 +27,12 @@
return;
}
- context.DrawGlyphRun(Foreground, GlyphRun, BaselineOrigin);
+ context.DrawGlyphRun(Foreground, GlyphRun);
}
public override Rect GetBounds()
{
- return GlyphRun?.Bounds ?? default;
+ return GlyphRun != null ? new Rect(GlyphRun.Size) : Rect.Empty;
}
}
}
diff --git a/src/Avalonia.Visuals/Media/TextDecoration.cs b/src/Avalonia.Visuals/Media/TextDecoration.cs
index 681fc5d499..d9b3f664ce 100644
--- a/src/Avalonia.Visuals/Media/TextDecoration.cs
+++ b/src/Avalonia.Visuals/Media/TextDecoration.cs
@@ -155,8 +155,7 @@ namespace Avalonia.Media
///
/// The drawing context.
/// The shaped characters that are decorated.
- /// The origin.
- internal void Draw(DrawingContext drawingContext, ShapedTextCharacters shapedTextCharacters, Point origin)
+ internal void Draw(DrawingContext drawingContext, ShapedTextCharacters shapedTextCharacters)
{
var fontRenderingEmSize = shapedTextCharacters.Properties.FontRenderingEmSize;
var fontMetrics = shapedTextCharacters.FontMetrics;
@@ -181,16 +180,20 @@ namespace Avalonia.Media
break;
}
+ var origin = new Point();
+
switch (Location)
{
- case TextDecorationLocation.Overline:
- origin += new Point(0, fontMetrics.Ascent);
+ case TextDecorationLocation.Baseline:
+ origin += shapedTextCharacters.GlyphRun.BaselineOrigin;
break;
case TextDecorationLocation.Strikethrough:
- origin += new Point(0, -fontMetrics.StrikethroughPosition);
+ origin += new Point(shapedTextCharacters.GlyphRun.BaselineOrigin.X,
+ shapedTextCharacters.GlyphRun.BaselineOrigin.Y - fontMetrics.StrikethroughPosition);
break;
case TextDecorationLocation.Underline:
- origin += new Point(0, -fontMetrics.UnderlinePosition);
+ origin += new Point(shapedTextCharacters.GlyphRun.BaselineOrigin.X,
+ shapedTextCharacters.GlyphRun.BaselineOrigin.Y - fontMetrics.UnderlinePosition);
break;
}
@@ -207,7 +210,7 @@ namespace Avalonia.Media
var pen = new Pen(Stroke ?? shapedTextCharacters.Properties.ForegroundBrush, thickness,
new DashStyle(StrokeDashArray, StrokeDashOffset), StrokeLineCap);
- drawingContext.DrawLine(pen, origin, origin + new Point(shapedTextCharacters.Bounds.Width, 0));
+ drawingContext.DrawLine(pen, origin, origin + new Point(shapedTextCharacters.Size.Width, 0));
}
}
}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs b/src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs
index 56790cc0db..338c92f6b1 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs
@@ -6,15 +6,14 @@
public abstract class DrawableTextRun : TextRun
{
///
- /// Gets the bounds.
+ /// Gets the size.
///
- public abstract Rect Bounds { get; }
+ public abstract Size Size { get; }
///
/// Draws the at the given origin.
///
/// The drawing context.
- /// The origin.
- public abstract void Draw(DrawingContext drawingContext, Point origin);
+ public abstract void Draw(DrawingContext drawingContext);
}
}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs b/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs
index 9e67a03f45..09ecc0a026 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs
@@ -26,7 +26,7 @@ namespace Avalonia.Media.TextFormatting
public override int TextSourceLength { get; }
///
- public override Rect Bounds => GlyphRun.Bounds;
+ public override Size Size => GlyphRun.Size;
///
/// Gets the font metrics.
@@ -45,7 +45,7 @@ namespace Avalonia.Media.TextFormatting
public GlyphRun GlyphRun { get; }
///
- public override void Draw(DrawingContext drawingContext, Point origin)
+ public override void Draw(DrawingContext drawingContext)
{
if (GlyphRun.GlyphIndices.Length == 0)
{
@@ -64,11 +64,10 @@ namespace Avalonia.Media.TextFormatting
if (Properties.BackgroundBrush != null)
{
- drawingContext.DrawRectangle(Properties.BackgroundBrush, null,
- new Rect(origin.X, origin.Y + FontMetrics.Ascent, Bounds.Width, Bounds.Height));
+ drawingContext.DrawRectangle(Properties.BackgroundBrush, null, new Rect(Size));
}
- drawingContext.DrawGlyphRun(Properties.ForegroundBrush, GlyphRun, origin);
+ drawingContext.DrawGlyphRun(Properties.ForegroundBrush, GlyphRun);
if (Properties.TextDecorations == null)
{
@@ -77,7 +76,7 @@ namespace Avalonia.Media.TextFormatting
foreach (var textDecoration in Properties.TextDecorations)
{
- textDecoration.Draw(drawingContext, this, origin);
+ textDecoration.Draw(drawingContext, this);
}
}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
index b116249fd4..3e85f0f6f0 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
@@ -52,7 +52,7 @@ namespace Avalonia.Media.TextFormatting
{
var glyphRun = textCharacters.GlyphRun;
- if (glyphRun.Bounds.Width < availableWidth)
+ if (glyphRun.Size.Width < availableWidth)
{
return glyphRun.Characters.Length;
}
@@ -348,7 +348,7 @@ namespace Avalonia.Media.TextFormatting
{
var currentRun = textRuns[runIndex];
- if (currentWidth + currentRun.GlyphRun.Bounds.Width > availableWidth)
+ if (currentWidth + currentRun.Size.Width > availableWidth)
{
var measuredLength = MeasureCharacters(currentRun, paragraphWidth - currentWidth);
@@ -421,7 +421,7 @@ namespace Avalonia.Media.TextFormatting
return new TextLineImpl(splitResult.First, textLineMetrics, lineBreak);
}
- currentWidth += currentRun.GlyphRun.Bounds.Width;
+ currentWidth += currentRun.Size.Width;
currentLength += currentRun.GlyphRun.Characters.Length;
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
index df1ecb4067..daa8807bf6 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
@@ -115,22 +115,24 @@ namespace Avalonia.Media.TextFormatting
/// Draws the text layout.
///
/// The drawing context.
- /// The origin.
- public void Draw(DrawingContext context, Point origin)
+ public void Draw(DrawingContext context)
{
if (!TextLines.Any())
{
return;
}
- var currentY = origin.Y;
+ var currentY = 0.0;
foreach (var textLine in TextLines)
{
var offsetX = TextLine.GetParagraphOffsetX(textLine.LineMetrics.Size.Width, Size.Width,
_paragraphProperties.TextAlignment);
- textLine.Draw(context, new Point(origin.X + offsetX, currentY));
+ using (context.PushPostTransform(Matrix.CreateTranslation(offsetX, currentY)))
+ {
+ textLine.Draw(context);
+ }
currentY += textLine.LineMetrics.Size.Height;
}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs
index c052fb8948..8a1efa0611 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs
@@ -51,8 +51,7 @@ namespace Avalonia.Media.TextFormatting
/// Draws the at the given origin.
///
/// The drawing context.
- /// The origin.
- public abstract void Draw(DrawingContext drawingContext, Point origin);
+ public abstract void Draw(DrawingContext drawingContext);
///
/// Create a collapsed line based on collapsed text properties.
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
index 51092cddda..f5e87d097b 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
@@ -33,17 +33,18 @@ namespace Avalonia.Media.TextFormatting
public override bool HasCollapsed { get; }
///
- public override void Draw(DrawingContext drawingContext, Point origin)
+ public override void Draw(DrawingContext drawingContext)
{
- var currentX = origin.X;
+ var currentX = 0.0;
foreach (var textRun in _textRuns)
{
- var baselineOrigin = new Point(currentX, origin.Y + LineMetrics.TextBaseline);
-
- textRun.Draw(drawingContext, baselineOrigin);
+ using (drawingContext.PushPostTransform(Matrix.CreateTranslation(currentX, 0)))
+ {
+ textRun.Draw(drawingContext);
+ }
- currentX += textRun.Bounds.Width;
+ currentX += textRun.Size.Width;
}
}
@@ -64,13 +65,13 @@ namespace Avalonia.Media.TextFormatting
var shapedSymbol = CreateShapedSymbol(collapsingProperties.Symbol);
- var availableWidth = collapsingProperties.Width - shapedSymbol.Bounds.Width;
+ var availableWidth = collapsingProperties.Width - shapedSymbol.Size.Width;
while (runIndex < _textRuns.Count)
{
var currentRun = _textRuns[runIndex];
- currentWidth += currentRun.GlyphRun.Bounds.Width;
+ currentWidth += currentRun.Size.Width;
if (currentWidth > availableWidth)
{
@@ -125,7 +126,7 @@ namespace Avalonia.Media.TextFormatting
return new TextLineImpl(shapedTextCharacters, textLineMetrics, TextLineBreak, true);
}
- availableWidth -= currentRun.GlyphRun.Bounds.Width;
+ availableWidth -= currentRun.Size.Width;
collapsedLength += currentRun.GlyphRun.Characters.Length;
@@ -133,7 +134,7 @@ namespace Avalonia.Media.TextFormatting
}
textLineMetrics =
- new TextLineMetrics(LineMetrics.Size.WithWidth(LineMetrics.Size.Width + shapedSymbol.Bounds.Width),
+ new TextLineMetrics(LineMetrics.Size.WithWidth(LineMetrics.Size.Width + shapedSymbol.Size.Width),
LineMetrics.TextBaseline, TextRange, LineMetrics.HasOverflowed);
return new TextLineImpl(new List(_textRuns) { shapedSymbol }, textLineMetrics, null,
@@ -156,12 +157,12 @@ namespace Avalonia.Media.TextFormatting
{
characterHit = run.GlyphRun.GetCharacterHitFromDistance(distance, out _);
- if (distance <= run.Bounds.Width)
+ if (distance <= run.Size.Width)
{
break;
}
- distance -= run.Bounds.Width;
+ distance -= run.Size.Width;
}
return characterHit;
@@ -229,7 +230,7 @@ namespace Avalonia.Media.TextFormatting
{
if (codepointIndex > textRun.Text.End)
{
- currentDistance += textRun.Bounds.Width;
+ currentDistance += textRun.Size.Width;
continue;
}
@@ -405,7 +406,7 @@ namespace Avalonia.Media.TextFormatting
for (var i = 0; i < shapedTextCharacters.Count; i++)
{
- shapedWidth += shapedTextCharacters[i].Bounds.Width;
+ shapedWidth += shapedTextCharacters[i].Size.Width;
}
return shapedWidth;
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs
index 6875cc1c04..c4d7527659 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs
@@ -67,7 +67,7 @@ namespace Avalonia.Media.TextFormatting
var fontMetrics =
new FontMetrics(shapedRun.Properties.Typeface, shapedRun.Properties.FontRenderingEmSize);
- lineWidth += shapedRun.Bounds.Width;
+ lineWidth += shapedRun.Size.Width;
if (ascent > fontMetrics.Ascent)
{
diff --git a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
index c87946b3ea..019614ae80 100644
--- a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
+++ b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
@@ -84,8 +84,7 @@ namespace Avalonia.Platform
///
/// The foreground.
/// The glyph run.
- /// The baseline origin of the glyph run.
- void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin);
+ void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun);
///
/// Creates a new that can be used as a render layer
diff --git a/src/Avalonia.Visuals/Point.cs b/src/Avalonia.Visuals/Point.cs
index 4cce2c925b..7324f5fbd0 100644
--- a/src/Avalonia.Visuals/Point.cs
+++ b/src/Avalonia.Visuals/Point.cs
@@ -267,5 +267,24 @@ namespace Avalonia
{
return new Point(_x, y);
}
+
+ ///
+ /// Deconstructs the point into its X and Y coordinates.
+ ///
+ /// The X coordinate.
+ /// The Y coordinate.
+ public void Deconstruct(out double x, out double y)
+ {
+ x = this._x;
+ y = this._y;
+ }
+
+ ///
+ /// Gets a value indicating whether the X and Y coordinates are zero.
+ ///
+ public bool IsDefault
+ {
+ get { return (_x == 0) && (_y == 0); }
+ }
}
}
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
index 4a364998fd..cb6b1f59d4 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
@@ -204,13 +204,13 @@ namespace Avalonia.Rendering.SceneGraph
}
///
- public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin)
+ public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{
var next = NextDrawAs();
if (next == null || !next.Item.Equals(Transform, foreground, glyphRun))
{
- Add(new GlyphRunNode(Transform, foreground, glyphRun, baselineOrigin, CreateChildScene(foreground)));
+ Add(new GlyphRunNode(Transform, foreground, glyphRun, CreateChildScene(foreground)));
}
else
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs
index bdf05c4f86..a6dba1bd32 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using Avalonia.Media;
+using Avalonia.Media.Immutable;
using Avalonia.Platform;
using Avalonia.VisualTree;
@@ -17,20 +18,17 @@ namespace Avalonia.Rendering.SceneGraph
/// The transform.
/// The foreground brush.
/// The glyph run to draw.
- /// The baseline origin of the glyph run.
/// Child scenes for drawing visual brushes.
public GlyphRunNode(
Matrix transform,
IBrush foreground,
GlyphRun glyphRun,
- Point baselineOrigin,
IDictionary childScenes = null)
- : base(glyphRun.Bounds.Translate(baselineOrigin), transform)
+ : base(new Rect(glyphRun.Size), transform)
{
Transform = transform;
Foreground = foreground?.ToImmutable();
GlyphRun = glyphRun;
- BaselineOrigin = baselineOrigin;
ChildScenes = childScenes;
}
@@ -49,11 +47,6 @@ namespace Avalonia.Rendering.SceneGraph
///
public GlyphRun GlyphRun { get; }
- ///
- /// Gets the baseline origin.
- ///
- public Point BaselineOrigin { get; set; }
-
///
public override IDictionary ChildScenes { get; }
@@ -61,7 +54,7 @@ namespace Avalonia.Rendering.SceneGraph
public override void Render(IDrawingContextImpl context)
{
context.Transform = Transform;
- context.DrawGlyphRun(Foreground, GlyphRun, BaselineOrigin);
+ context.DrawGlyphRun(Foreground, GlyphRun);
}
///
diff --git a/src/Avalonia.Visuals/Size.cs b/src/Avalonia.Visuals/Size.cs
index f87b336b50..d87d2c5fc2 100644
--- a/src/Avalonia.Visuals/Size.cs
+++ b/src/Avalonia.Visuals/Size.cs
@@ -276,5 +276,24 @@ namespace Avalonia
{
return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", _width, _height);
}
+
+ ///
+ /// Deconstructs the size into its Width and Height values.
+ ///
+ /// The width.
+ /// The height.
+ public void Deconstruct(out double width, out double height)
+ {
+ width = this._width;
+ height = this._height;
+ }
+
+ ///
+ /// Gets a value indicating whether the Width and Height values are zero.
+ ///
+ public bool IsDefault
+ {
+ get { return (_width == 0) && (_height == 0); }
+ }
}
}
diff --git a/src/Avalonia.Visuals/Thickness.cs b/src/Avalonia.Visuals/Thickness.cs
index b03e91bf34..6d69c4d9a9 100644
--- a/src/Avalonia.Visuals/Thickness.cs
+++ b/src/Avalonia.Visuals/Thickness.cs
@@ -272,5 +272,28 @@ namespace Avalonia
{
return $"{_left},{_top},{_right},{_bottom}";
}
+
+ ///
+ /// Deconstructor the thickness into its left, top, right and bottom thickness values.
+ ///
+ /// The thickness on the left.
+ /// The thickness on the top.
+ /// The thickness on the right.
+ /// The thickness on the bottom.
+ public void Deconstruct(out double left, out double top, out double right, out double bottom)
+ {
+ left = this._left;
+ top = this._top;
+ right = this._right;
+ bottom = this._bottom;
+ }
+
+ ///
+ /// Gets a value indicating whether the left, top, right and bottom thickness values are zero.
+ ///
+ public bool IsDefault
+ {
+ get { return (_left == 0) && (_top == 0) && (_right == 0) && (_bottom == 0); }
+ }
}
}
diff --git a/src/Avalonia.Visuals/Vector.cs b/src/Avalonia.Visuals/Vector.cs
index 6059dc3971..2fcf804f14 100644
--- a/src/Avalonia.Visuals/Vector.cs
+++ b/src/Avalonia.Visuals/Vector.cs
@@ -333,5 +333,24 @@ namespace Avalonia
///
public static Vector UnitY
=> new Vector(0, 1);
+
+ ///
+ /// Deconstructs the vector into its X and Y components.
+ ///
+ /// The X component.
+ /// The Y component.
+ public void Deconstruct(out double x, out double y)
+ {
+ x = this._x;
+ y = this._y;
+ }
+
+ ///
+ /// Gets a value indicating whether the X and Y components are zero.
+ ///
+ public bool IsDefault
+ {
+ get { return (_x == 0) && (_y == 0); }
+ }
}
}
diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
index a155fd863b..98528a128a 100644
--- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
+++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
@@ -401,16 +401,16 @@ namespace Avalonia.Skia
}
///
- public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin)
+ public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{
- using (var paintWrapper = CreatePaint(_fillPaint, foreground, glyphRun.Bounds.Size))
+ using (var paintWrapper = CreatePaint(_fillPaint, foreground, glyphRun.Size))
{
var glyphRunImpl = (GlyphRunImpl)glyphRun.GlyphRunImpl;
ConfigureTextRendering(paintWrapper);
- Canvas.DrawText(glyphRunImpl.TextBlob, (float)baselineOrigin.X,
- (float)baselineOrigin.Y, paintWrapper.Paint);
+ Canvas.DrawText(glyphRunImpl.TextBlob, (float)glyphRun.BaselineOrigin.X,
+ (float)glyphRun.BaselineOrigin.Y, paintWrapper.Paint);
}
}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
index e0de40525f..258a51db5a 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
@@ -324,13 +324,14 @@ namespace Avalonia.Direct2D1.Media
/// The foreground.
/// The glyph run.
///
- public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin)
+ public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{
- using (var brush = CreateBrush(foreground, glyphRun.Bounds.Size))
+ using (var brush = CreateBrush(foreground, glyphRun.Size))
{
var glyphRunImpl = (GlyphRunImpl)glyphRun.GlyphRunImpl;
- _renderTarget.DrawGlyphRun(baselineOrigin.ToSharpDX(), glyphRunImpl.GlyphRun, brush.PlatformBrush, MeasuringMode.Natural);
+ _renderTarget.DrawGlyphRun(glyphRun.BaselineOrigin.ToSharpDX(), glyphRunImpl.GlyphRun,
+ brush.PlatformBrush, MeasuringMode.Natural);
}
}
diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs b/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs
index 5c8a941294..1d9524a191 100644
--- a/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs
@@ -59,12 +59,12 @@ namespace Avalonia.Markup.UnitTests.Data.Plugins
{
new BindingNotification(5),
new BindingNotification(
- new ValidationException(errmsg),
+ new DataValidationException(errmsg),
BindingErrorType.DataValidationError,
3),
new BindingNotification(7),
new BindingNotification(
- new ValidationException(errmsg),
+ new DataValidationException(errmsg),
BindingErrorType.DataValidationError,
11),
}, result);
diff --git a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs
index 3ae91c8bba..783215fb5d 100644
--- a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs
@@ -2,7 +2,6 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
-using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.UnitTests;
@@ -27,6 +26,7 @@ namespace Avalonia.Controls.UnitTests
Assert.True(target.IsDropDownOpen);
_helper.Down(target);
+ _helper.Up(target);
Assert.False(target.IsDropDownOpen);
}
diff --git a/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs b/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs
index ff3ad30a88..c5c7737f0d 100644
--- a/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs
@@ -144,6 +144,16 @@ namespace Avalonia.Controls.UnitTests.Generators
Assert.Equal("bar", container.Content);
}
+ [Fact]
+ public void Materialize_Should_Create_Containers_When_Item_Is_Null()
+ {
+ var owner = new Decorator();
+ var target = new ItemContainerGenerator(owner, ListBoxItem.ContentProperty, null);
+ var container = (ListBoxItem)target.Materialize(0, null).ContainerControl;
+
+ Assert.True(container != null, "The containers is not materialized.");
+ }
+
private IList Materialize(
IItemContainerGenerator generator,
int index,
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
index 53a8db2176..f032186bcd 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
@@ -21,7 +21,50 @@ namespace Avalonia.Controls.UnitTests.Primitives
public class PopupTests
{
protected bool UsePopupHost;
-
+
+ [Fact]
+ public void Popup_Open_Without_Target_Should_Attach_Itself_Later()
+ {
+ using (CreateServices())
+ {
+ int openedEvent = 0;
+ var target = new Popup();
+ target.Opened += (s, a) => openedEvent++;
+ target.IsOpen = true;
+
+ var window = PreparedWindow(target);
+ window.Show();
+ Assert.Equal(1, openedEvent);
+ }
+ }
+
+ [Fact]
+ public void Popup_Without_TopLevel_Shouldnt_Call_Open()
+ {
+ int openedEvent = 0;
+ var target = new Popup();
+ target.Opened += (s, a) => openedEvent++;
+ target.IsOpen = true;
+
+ Assert.Equal(0, openedEvent);
+ }
+
+ [Fact]
+ public void Opening_Popup_Shouldnt_Throw_When_Not_In_Visual_Tree()
+ {
+ var target = new Popup();
+ target.IsOpen = true;
+ }
+
+ [Fact]
+ public void Opening_Popup_Shouldnt_Throw_When_In_Tree_Without_TopLevel()
+ {
+ Control c = new Control();
+ var target = new Popup();
+ ((ISetLogicalParent)target).SetParent(c);
+ target.IsOpen = true;
+ }
+
[Fact]
public void Setting_Child_Should_Set_Child_Controls_LogicalParent()
{
diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs
index bf41381b52..f3e1c37705 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs
@@ -369,7 +369,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var glyphRun = shapedRun.GlyphRun;
- var width = glyphRun.Bounds.Width;
+ var width = glyphRun.Size.Width;
var characterHit = glyphRun.GetCharacterHitFromDistance(width, out _);
diff --git a/tests/Avalonia.Visuals.UnitTests/Media/GlyphRunTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/GlyphRunTests.cs
index 219c7ece46..58feb4714a 100644
--- a/tests/Avalonia.Visuals.UnitTests/Media/GlyphRunTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Media/GlyphRunTests.cs
@@ -119,6 +119,56 @@ namespace Avalonia.Visuals.UnitTests.Media
}
}
+ [InlineData(new double[] { 10, 10, 10 }, new ushort[] { 0, 0, 0 }, 0)]
+ [InlineData(new double[] { 10, 10, 10 }, new ushort[] { 0, 0, 0 }, 1)]
+ [InlineData(new double[] { 10, 10, 10, 10 }, new ushort[] { 0, 0, 0, 3 }, 0)]
+ [InlineData(new double[] { 10, 10, 10, 10 }, new ushort[] { 3, 0, 0, 0 }, 1)]
+ [InlineData(new double[] { 10, 10, 10, 10, 10 }, new ushort[] { 0, 1, 1, 1, 4 }, 0)]
+ [InlineData(new double[] { 10, 10, 10, 10, 10 }, new ushort[] { 4, 1, 1, 1, 0 }, 1)]
+ [Theory]
+ public void Should_Find_Glyph_Index(double[] advances, ushort[] clusters, int bidiLevel)
+ {
+ using (var glyphRun = CreateGlyphRun(advances, clusters, bidiLevel))
+ {
+ if (glyphRun.IsLeftToRight)
+ {
+ for (var i = 0; i < clusters.Length; i++)
+ {
+ var cluster = clusters[i];
+
+ var found = glyphRun.FindGlyphIndex(cluster);
+
+ var expected = i;
+
+ while (expected - 1 >= 0 && clusters[expected - 1] == cluster)
+ {
+ expected--;
+ }
+
+ Assert.Equal(expected, found);
+ }
+ }
+ else
+ {
+ for (var i = clusters.Length - 1; i > 0; i--)
+ {
+ var cluster = clusters[i];
+
+ var found = glyphRun.FindGlyphIndex(cluster);
+
+ var expected = i;
+
+ while (expected + 1 < clusters.Length && clusters[expected + 1] == cluster)
+ {
+ expected++;
+ }
+
+ Assert.Equal(expected, found);
+ }
+ }
+ }
+ }
+
private static GlyphRun CreateGlyphRun(double[] glyphAdvances, ushort[] glyphClusters, int bidiLevel = 0)
{
var count = glyphAdvances.Length;