Browse Source

Merge branch 'master' into fixes/fluent-fix-checkbox-foreground

pull/4777/head
danwalmsley 6 years ago
committed by GitHub
parent
commit
f43ca2a181
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      samples/ControlCatalog/Pages/DataGridPage.xaml
  2. 2
      src/Avalonia.Animation/KeySplineTypeConverter.cs
  3. 4
      src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs
  4. 5
      src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt
  5. 24
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  6. 34
      src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs
  7. 39
      src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs
  8. 231
      src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs
  9. 21
      src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
  10. 22
      src/Avalonia.Controls.DataGrid/Utils/DataGridHelper.cs
  11. 9
      src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
  12. 4
      src/Avalonia.Controls/ComboBox.cs
  13. 6
      src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs
  14. 21
      src/Avalonia.Controls/Primitives/Popup.cs
  15. 4
      src/Avalonia.Themes.Default/ComboBox.xaml
  16. 2
      src/Avalonia.Themes.Default/MenuItem.xaml
  17. 3
      src/Avalonia.Themes.Fluent/ComboBox.xaml
  18. 2
      src/Avalonia.Themes.Fluent/ContextMenu.xaml
  19. 4
      src/Avalonia.Themes.Fluent/MenuItem.xaml
  20. 92
      src/Avalonia.Themes.Fluent/ScrollViewer.xaml
  21. 4
      tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs
  22. 2
      tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs
  23. 10
      tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs
  24. 45
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
  25. 50
      tests/Avalonia.Visuals.UnitTests/Media/GlyphRunTests.cs

17
samples/ControlCatalog/Pages/DataGridPage.xaml

@ -11,12 +11,17 @@
<Setter Property="Background" Value="{Binding Path=GDP, Mode=OneWay, Converter={StaticResource GDPConverter}}" /> <Setter Property="Background" Value="{Binding Path=GDP, Mode=OneWay, Converter={StaticResource GDPConverter}}" />
</Style> </Style>
</UserControl.Styles> </UserControl.Styles>
<Grid RowDefinitions="Auto,*"> <Grid RowDefinitions="Auto,Auto,*">
<StackPanel Orientation="Vertical" Spacing="4" Grid.Row="0"> <StackPanel Orientation="Vertical" Spacing="4" Grid.Row="0">
<TextBlock Classes="h1">DataGrid</TextBlock> <TextBlock Classes="h1">DataGrid</TextBlock>
<TextBlock Classes="h2">A control for displaying and interacting with a data source.</TextBlock> <TextBlock Classes="h2">A control for displaying and interacting with a data source.</TextBlock>
</StackPanel> </StackPanel>
<TabControl Grid.Row="1"> <StackPanel Grid.Row="1" Spacing="4" Orientation="Horizontal" IsVisible="{Binding #EditableTab.IsSelected}">
<TextBlock Text="FontSize:" VerticalAlignment="Center"/>
<Slider x:Name="FontSizeSlider" Minimum="5" Maximum="30" Value="14" Width="100" VerticalAlignment="Center" />
<CheckBox x:Name="IsThreeStateCheckBox" IsChecked="False" Content="IsThreeState"/>
</StackPanel>
<TabControl Grid.Row="2">
<TabItem Header="DataGrid"> <TabItem Header="DataGrid">
<DataGrid Name="dataGrid1" Margin="12" CanUserResizeColumns="True" CanUserReorderColumns="True" CanUserSortColumns="True" HeadersVisibility="All"> <DataGrid Name="dataGrid1" Margin="12" CanUserResizeColumns="True" CanUserReorderColumns="True" CanUserSortColumns="True" HeadersVisibility="All">
<DataGrid.Columns> <DataGrid.Columns>
@ -39,13 +44,13 @@
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
</TabItem> </TabItem>
<TabItem Header="Editable"> <TabItem x:Name="EditableTab" Header="Editable">
<Grid RowDefinitions="*,Auto"> <Grid RowDefinitions="*,Auto">
<DataGrid Name="dataGridEdit" Margin="12" Grid.Row="0"> <DataGrid Name="dataGridEdit" Margin="12" Grid.Row="0">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Header="First Name" Binding="{Binding FirstName}" Width="2*" /> <DataGridTextColumn Header="First Name" Binding="{Binding FirstName}" Width="2*" FontSize="{Binding #FontSizeSlider.Value, Mode=OneWay}" />
<DataGridTextColumn Header="Last" Binding="{Binding LastName}" Width="2*" /> <DataGridTextColumn Header="Last" Binding="{Binding LastName}" Width="2*" FontSize="{Binding #FontSizeSlider.Value, Mode=OneWay}" />
<DataGridCheckBoxColumn Header="Is Banned" Binding="{Binding IsBanned}" Width="*" /> <DataGridCheckBoxColumn Header="Is Banned" Binding="{Binding IsBanned}" Width="*" IsThreeState="{Binding #IsThreeStateCheckBox.IsChecked, Mode=OneWay}" />
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
<Button Grid.Row="1" Name="btnAdd" Margin="12,0,12,12" Content="Add" HorizontalAlignment="Right" /> <Button Grid.Row="1" Name="btnAdd" Margin="12,0,12,12" Content="Add" HorizontalAlignment="Right" />

2
src/Avalonia.Animation/KeySplineTypeConverter.cs

@ -19,7 +19,7 @@ namespace Avalonia.Animation
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{ {
return KeySpline.Parse((string)value, culture); return KeySpline.Parse((string)value, CultureInfo.InvariantCulture);
} }
} }
} }

4
src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs

@ -63,12 +63,12 @@ namespace Avalonia.Data.Core.Plugins
{ {
if (errors.Count == 1) if (errors.Count == 1)
{ {
return new ValidationException(errors[0].ErrorMessage); return new DataValidationException(errors[0].ErrorMessage);
} }
else else
{ {
return new AggregateException( return new AggregateException(
errors.Select(x => new ValidationException(x.ErrorMessage))); errors.Select(x => new DataValidationException(x.ErrorMessage)));
} }
} }
} }

5
src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt

@ -0,0 +1,5 @@
Compat issues with assembly Avalonia.Controls.DataGrid:
MembersMustExist : Member 'public Avalonia.StyledProperty<System.String> Avalonia.StyledProperty<System.String> 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

24
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -31,7 +31,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Displays data in a customizable grid. /// Displays data in a customizable grid.
/// </summary> /// </summary>
[PseudoClasses(":invalid")] [PseudoClasses(":invalid", ":empty-rows", ":empty-columns")]
public partial class DataGrid : TemplatedControl public partial class DataGrid : TemplatedControl
{ {
private const string DATAGRID_elementRowsPresenterName = "PART_RowsPresenter"; private const string DATAGRID_elementRowsPresenterName = "PART_RowsPresenter";
@ -711,6 +711,7 @@ namespace Avalonia.Controls
DisplayData = new DataGridDisplayData(this); DisplayData = new DataGridDisplayData(this);
ColumnsInternal = CreateColumnsInstance(); ColumnsInternal = CreateColumnsInstance();
ColumnsInternal.CollectionChanged += ColumnsInternal_CollectionChanged;
RowHeightEstimate = DATAGRID_defaultRowHeight; RowHeightEstimate = DATAGRID_defaultRowHeight;
RowDetailsHeightEstimate = 0; RowDetailsHeightEstimate = 0;
@ -727,6 +728,8 @@ namespace Avalonia.Controls
CurrentCellCoordinates = new DataGridCellCoordinates(-1, -1); CurrentCellCoordinates = new DataGridCellCoordinates(-1, -1);
RowGroupHeaderHeightEstimate = DATAGRID_defaultRowHeight; RowGroupHeaderHeightEstimate = DATAGRID_defaultRowHeight;
UpdatePseudoClasses();
} }
private void SetValueNoCallback<T>(AvaloniaProperty<T> property, T value, BindingPriority priority = BindingPriority.LocalValue) private void SetValueNoCallback<T>(AvaloniaProperty<T> property, T value, BindingPriority priority = BindingPriority.LocalValue)
@ -851,9 +854,27 @@ namespace Avalonia.Controls
// can be set when the DataGrid is not part of the visual tree // can be set when the DataGrid is not part of the visual tree
_measured = false; _measured = false;
InvalidateMeasure(); InvalidateMeasure();
UpdatePseudoClasses();
} }
} }
private void ColumnsInternal_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add
|| e.Action == NotifyCollectionChangedAction.Remove
|| e.Action == NotifyCollectionChangedAction.Reset)
{
UpdatePseudoClasses();
}
}
internal void UpdatePseudoClasses()
{
PseudoClasses.Set(":empty-columns", !ColumnsInternal.GetVisibleColumns().Any());
PseudoClasses.Set(":empty-rows", !DataConnection.Any());
}
private void OnSelectedIndexChanged(AvaloniaPropertyChangedEventArgs e) private void OnSelectedIndexChanged(AvaloniaPropertyChangedEventArgs e)
{ {
if (!_areHandlersSuspended) if (!_areHandlersSuspended)
@ -1348,7 +1369,6 @@ namespace Avalonia.Controls
internal DataGridColumnCollection ColumnsInternal internal DataGridColumnCollection ColumnsInternal
{ {
get; get;
private set;
} }
internal int AnchorSlot internal int AnchorSlot

34
src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs

@ -17,9 +17,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public class DataGridCheckBoxColumn : DataGridBoundColumn public class DataGridCheckBoxColumn : DataGridBoundColumn
{ {
private bool _beganEditWithKeyboard; private bool _beganEditWithKeyboard;
private bool _isThreeState;
private CheckBox _currentCheckBox; private CheckBox _currentCheckBox;
private DataGrid _owningGrid; private DataGrid _owningGrid;
@ -31,6 +29,12 @@ namespace Avalonia.Controls
BindingTarget = CheckBox.IsCheckedProperty; BindingTarget = CheckBox.IsCheckedProperty;
} }
/// <summary>
/// Defines the <see cref="IsThreeState"/> property.
/// </summary>
public static StyledProperty<bool> IsThreeStateProperty =
CheckBox.IsThreeStateProperty.AddOwner<DataGridCheckBoxColumn>();
/// <summary> /// <summary>
/// Gets or sets a value that indicates whether the hosted <see cref="T:System.Windows.Controls.CheckBox" /> controls allow three states or two. /// Gets or sets a value that indicates whether the hosted <see cref="T:System.Windows.Controls.CheckBox" /> controls allow three states or two.
/// </summary> /// </summary>
@ -39,17 +43,17 @@ namespace Avalonia.Controls
/// </returns> /// </returns>
public bool IsThreeState public bool IsThreeState
{ {
get get => GetValue(IsThreeStateProperty);
{ set => SetValue(IsThreeStateProperty, value);
return _isThreeState; }
}
set protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
base.OnPropertyChanged(change);
if (change.Property == IsThreeStateProperty)
{ {
if (_isThreeState != value) NotifyPropertyChanged(change.Property.Name);
{
_isThreeState = value;
NotifyPropertyChanged(nameof(IsThreeState));
}
} }
} }
@ -203,9 +207,9 @@ namespace Avalonia.Controls
{ {
throw new ArgumentNullException("element"); throw new ArgumentNullException("element");
} }
if(element is CheckBox checkBox) if (element is CheckBox checkBox)
{ {
checkBox.IsThreeState = IsThreeState; DataGridHelper.SyncColumnProperty(this, checkBox, IsThreeStateProperty);
} }
else else
{ {
@ -229,7 +233,7 @@ namespace Avalonia.Controls
{ {
checkBox.HorizontalAlignment = HorizontalAlignment.Center; checkBox.HorizontalAlignment = HorizontalAlignment.Center;
checkBox.VerticalAlignment = VerticalAlignment.Center; checkBox.VerticalAlignment = VerticalAlignment.Center;
checkBox.IsThreeState = IsThreeState; DataGridHelper.SyncColumnProperty(this, checkBox, IsThreeStateProperty);
} }
private bool EnsureOwningGrid() private bool EnsureOwningGrid()

39
src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs

@ -77,24 +77,7 @@ namespace Avalonia.Controls
private set; private set;
} }
public int Count public int Count => GetCount(true);
{
get
{
IList list = List;
if (list != null)
{
return list.Count;
}
if(DataSource is DataGridCollectionView cv)
{
return cv.Count;
}
return DataSource?.Cast<object>().Count() ?? 0;
}
}
public bool DataIsPrimitive public bool DataIsPrimitive
{ {
@ -210,6 +193,24 @@ namespace Avalonia.Controls
} }
} }
internal bool Any()
{
return GetCount(false) > 0;
}
/// <param name="allowSlow">When "allowSlow" is false, method will not use Linq.Count() method and will return 0 or 1 instead.</param>
private int GetCount(bool allowSlow)
{
return DataSource switch
{
ICollection collection => collection.Count,
DataGridCollectionView cv => cv.Count,
IEnumerable enumerable when allowSlow => enumerable.Cast<object>().Count(),
IEnumerable enumerable when !allowSlow => enumerable.Cast<object>().Any() ? 1 : 0,
_ => 0
};
}
/// <summary> /// <summary>
/// Puts the entity into editing mode if possible /// Puts the entity into editing mode if possible
/// </summary> /// </summary>
@ -675,6 +676,8 @@ namespace Avalonia.Controls
} }
break; break;
} }
_owner.UpdatePseudoClasses();
} }
private void UpdateDataProperties() private void UpdateDataProperties()

231
src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs

@ -20,11 +20,6 @@ namespace Avalonia.Controls
{ {
private const string DATAGRID_TextColumnCellTextBlockMarginKey = "DataGridTextColumnCellTextBlockMargin"; private const string DATAGRID_TextColumnCellTextBlockMarginKey = "DataGridTextColumnCellTextBlockMargin";
private double? _fontSize;
private FontStyle? _fontStyle;
private FontWeight? _fontWeight;
private IBrush _foreground;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridTextColumn" /> class. /// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridTextColumn" /> class.
/// </summary> /// </summary>
@ -36,18 +31,24 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Identifies the FontFamily dependency property. /// Identifies the FontFamily dependency property.
/// </summary> /// </summary>
public static readonly StyledProperty<string> FontFamilyProperty = public static readonly AttachedProperty<FontFamily> FontFamilyProperty =
AvaloniaProperty.Register<DataGridTextColumn, string>(nameof(FontFamily)); TextBlock.FontFamilyProperty.AddOwner<DataGridTextColumn>();
/// <summary> /// <summary>
/// Gets or sets the font name. /// Gets or sets the font name.
/// </summary> /// </summary>
public string FontFamily public FontFamily FontFamily
{ {
get { return GetValue(FontFamilyProperty); } get => GetValue(FontFamilyProperty);
set { SetValue(FontFamilyProperty, value); } set => SetValue(FontFamilyProperty, value);
} }
/// <summary>
/// Identifies the FontSize dependency property.
/// </summary>
public static readonly AttachedProperty<double> FontSizeProperty =
TextBlock.FontSizeProperty.AddOwner<DataGridTextColumn>();
/// <summary> /// <summary>
/// Gets or sets the font size. /// Gets or sets the font size.
/// </summary> /// </summary>
@ -55,74 +56,66 @@ namespace Avalonia.Controls
[DefaultValue(double.NaN)] [DefaultValue(double.NaN)]
public double FontSize public double FontSize
{ {
get get => GetValue(FontSizeProperty);
{ set => SetValue(FontSizeProperty, value);
return _fontSize ?? Double.NaN;
}
set
{
if (_fontSize != value)
{
_fontSize = value;
NotifyPropertyChanged(nameof(FontSize));
}
}
} }
/// <summary>
/// Identifies the FontStyle dependency property.
/// </summary>
public static readonly AttachedProperty<FontStyle> FontStyleProperty =
TextBlock.FontStyleProperty.AddOwner<DataGridTextColumn>();
/// <summary> /// <summary>
/// Gets or sets the font style. /// Gets or sets the font style.
/// </summary> /// </summary>
public FontStyle FontStyle public FontStyle FontStyle
{ {
get get => GetValue(FontStyleProperty);
{ set => SetValue(FontStyleProperty, value);
return _fontStyle ?? FontStyle.Normal;
}
set
{
if (_fontStyle != value)
{
_fontStyle = value;
NotifyPropertyChanged(nameof(FontStyle));
}
}
} }
/// <summary>
/// Identifies the FontWeight dependency property.
/// </summary>
public static readonly AttachedProperty<FontWeight> FontWeightProperty =
TextBlock.FontWeightProperty.AddOwner<DataGridTextColumn>();
/// <summary> /// <summary>
/// Gets or sets the font weight or thickness. /// Gets or sets the font weight or thickness.
/// </summary> /// </summary>
public FontWeight FontWeight public FontWeight FontWeight
{ {
get get => GetValue(FontWeightProperty);
{ set => SetValue(FontWeightProperty, value);
return _fontWeight ?? FontWeight.Normal;
}
set
{
if (_fontWeight != value)
{
_fontWeight = value;
NotifyPropertyChanged(nameof(FontWeight));
}
}
} }
/// <summary>
/// Identifies the Foreground dependency property.
/// </summary>
public static readonly AttachedProperty<IBrush> ForegroundProperty =
TextBlock.ForegroundProperty.AddOwner<DataGridTextColumn>();
/// <summary> /// <summary>
/// Gets or sets a brush that describes the foreground of the column cells. /// Gets or sets a brush that describes the foreground of the column cells.
/// </summary> /// </summary>
public IBrush Foreground public IBrush Foreground
{ {
get get => GetValue(ForegroundProperty);
{ set => SetValue(ForegroundProperty, value);
return _foreground; }
}
set protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
base.OnPropertyChanged(change);
if (change.Property == FontFamilyProperty
|| change.Property == FontSizeProperty
|| change.Property == FontStyleProperty
|| change.Property == FontWeightProperty
|| change.Property == ForegroundProperty)
{ {
if (_foreground != value) NotifyPropertyChanged(change.Property.Name);
{
_foreground = value;
NotifyPropertyChanged(nameof(Foreground));
}
} }
} }
@ -154,26 +147,7 @@ namespace Avalonia.Controls
Background = new SolidColorBrush(Colors.Transparent) Background = new SolidColorBrush(Colors.Transparent)
}; };
if (IsSet(FontFamilyProperty)) SyncProperties(textBox);
{
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;
}
return textBox; return textBox;
} }
@ -192,26 +166,8 @@ namespace Avalonia.Controls
VerticalAlignment = VerticalAlignment.Center VerticalAlignment = VerticalAlignment.Center
}; };
if (IsSet(FontFamilyProperty)) SyncProperties(textBlockElement);
{
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;
}
if (Binding != null) if (Binding != null)
{ {
textBlockElement.Bind(TextBlock.TextProperty, Binding); textBlockElement.Bind(TextBlock.TextProperty, Binding);
@ -261,99 +217,42 @@ namespace Avalonia.Controls
throw new ArgumentNullException("element"); throw new ArgumentNullException("element");
} }
if(element is TextBox textBox) if (element is AvaloniaObject content)
{ {
if (propertyName == nameof(FontFamily)) if (propertyName == nameof(FontFamily))
{ {
textBox.FontFamily = FontFamily; DataGridHelper.SyncColumnProperty(this, content, FontFamilyProperty);
} }
else if (propertyName == nameof(FontSize)) else if (propertyName == nameof(FontSize))
{ {
SetTextFontSize(textBox, TextBox.FontSizeProperty); DataGridHelper.SyncColumnProperty(this, content, FontSizeProperty);
} }
else if (propertyName == nameof(FontStyle)) else if (propertyName == nameof(FontStyle))
{ {
textBox.FontStyle = FontStyle; DataGridHelper.SyncColumnProperty(this, content, FontStyleProperty);
} }
else if (propertyName == nameof(FontWeight)) else if (propertyName == nameof(FontWeight))
{ {
textBox.FontWeight = FontWeight; DataGridHelper.SyncColumnProperty(this, content, FontWeightProperty);
} }
else if (propertyName == nameof(Foreground)) else if (propertyName == nameof(Foreground))
{ {
textBox.Foreground = Foreground; DataGridHelper.SyncColumnProperty(this, content, ForegroundProperty);
}
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;
}
} }
} }
else 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; DataGridHelper.SyncColumnProperty(this, content, FontFamilyProperty);
if (double.IsNaN(newFontSize)) DataGridHelper.SyncColumnProperty(this, content, FontSizeProperty);
{ DataGridHelper.SyncColumnProperty(this, content, FontStyleProperty);
textElement.ClearValue(fontSizeProperty); DataGridHelper.SyncColumnProperty(this, content, FontWeightProperty);
} DataGridHelper.SyncColumnProperty(this, content, ForegroundProperty);
else
{
textElement.SetValue(fontSizeProperty, newFontSize);
}
} }
} }
} }

21
src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml

@ -588,18 +588,11 @@
<!--<DataGridColumnHeader Name="PART_TopRightCornerHeader" <!--<DataGridColumnHeader Name="PART_TopRightCornerHeader"
Grid.Column="2" Grid.Column="2"
Template="{StaticResource TopRightHeaderTemplate}" />--> Template="{StaticResource TopRightHeaderTemplate}" />-->
<!--<Rectangle Name="PART_ColumnHeadersAndRowsSeparator" <Rectangle Name="PART_ColumnHeadersAndRowsSeparator"
Grid.ColumnSpan="3" Grid.ColumnSpan="3"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
StrokeThickness="1"
Height="1" Height="1"
Fill="{DynamicResource DataGridGridLinesBrush}" />--> Fill="{DynamicResource DataGridGridLinesBrush}" />
<Border Name="PART_ColumnHeadersAndRowsSeparator"
Grid.ColumnSpan="3"
Height="2"
VerticalAlignment="Bottom"
BorderThickness="0,0,0,1"
BorderBrush="{DynamicResource DataGridGridLinesBrush}" />
<DataGridRowsPresenter Name="PART_RowsPresenter" <DataGridRowsPresenter Name="PART_RowsPresenter"
Grid.Row="1" Grid.Row="1"
@ -642,4 +635,14 @@
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>
</Style> </Style>
<Style Selector="DataGrid:empty-columns /template/ DataGridColumnHeader#PART_TopLeftCornerHeader">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="DataGrid:empty-columns /template/ DataGridColumnHeadersPresenter#PART_ColumnHeadersPresenter">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="DataGrid:empty-columns /template/ Rectangle#PART_ColumnHeadersAndRowsSeparator">
<Setter Property="IsVisible" Value="False" />
</Style>
</Styles> </Styles>

22
src/Avalonia.Controls.DataGrid/Utils/DataGridHelper.cs

@ -0,0 +1,22 @@
namespace Avalonia.Controls
{
internal static class DataGridHelper
{
internal static void SyncColumnProperty<T>(AvaloniaObject column, AvaloniaObject content, AvaloniaProperty<T> property)
{
SyncColumnProperty(column, content, property, property);
}
internal static void SyncColumnProperty<T>(AvaloniaObject column, AvaloniaObject content, AvaloniaProperty<T> contentProperty, AvaloniaProperty<T> columnProperty)
{
if (!column.IsSet(columnProperty))
{
content.ClearValue(contentProperty);
}
else
{
content.SetValue(contentProperty, column.GetValue(columnProperty));
}
}
}
}

9
src/Avalonia.Controls/Calendar/CalendarDatePicker.cs

@ -420,7 +420,7 @@ namespace Avalonia.Controls
_calendar.DayButtonMouseUp -= Calendar_DayButtonMouseUp; _calendar.DayButtonMouseUp -= Calendar_DayButtonMouseUp;
_calendar.DisplayDateChanged -= Calendar_DisplayDateChanged; _calendar.DisplayDateChanged -= Calendar_DisplayDateChanged;
_calendar.SelectedDatesChanged -= Calendar_SelectedDatesChanged; _calendar.SelectedDatesChanged -= Calendar_SelectedDatesChanged;
_calendar.PointerPressed -= Calendar_PointerPressed; _calendar.PointerReleased -= Calendar_PointerReleased;
_calendar.KeyDown -= Calendar_KeyDown; _calendar.KeyDown -= Calendar_KeyDown;
} }
_calendar = e.NameScope.Find<Calendar>(ElementCalendar); _calendar = e.NameScope.Find<Calendar>(ElementCalendar);
@ -435,7 +435,7 @@ namespace Avalonia.Controls
_calendar.DayButtonMouseUp += Calendar_DayButtonMouseUp; _calendar.DayButtonMouseUp += Calendar_DayButtonMouseUp;
_calendar.DisplayDateChanged += Calendar_DisplayDateChanged; _calendar.DisplayDateChanged += Calendar_DisplayDateChanged;
_calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged; _calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
_calendar.PointerPressed += Calendar_PointerPressed; _calendar.PointerReleased += Calendar_PointerReleased;
_calendar.KeyDown += Calendar_KeyDown; _calendar.KeyDown += Calendar_KeyDown;
//_calendar.SizeChanged += new SizeChangedEventHandler(Calendar_SizeChanged); //_calendar.SizeChanged += new SizeChangedEventHandler(Calendar_SizeChanged);
//_calendar.IsTabStop = true; //_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; e.Handled = true;
} }

4
src/Avalonia.Controls/ComboBox.cs

@ -257,7 +257,7 @@ namespace Avalonia.Controls
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnPointerPressed(PointerPressedEventArgs e) protected override void OnPointerReleased(PointerReleasedEventArgs e)
{ {
if (!e.Handled) if (!e.Handled)
{ {
@ -276,7 +276,7 @@ namespace Avalonia.Controls
} }
} }
base.OnPointerPressed(e); base.OnPointerReleased(e);
} }
/// <inheritdoc/> /// <inheritdoc/>

6
src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs

@ -47,11 +47,7 @@ namespace Avalonia.Controls.Generators
{ {
var container = item as T; var container = item as T;
if (item == null) if (container != null)
{
return null;
}
else if (container != null)
{ {
return container; return container;
} }

21
src/Avalonia.Controls/Primitives/Popup.cs

@ -128,6 +128,7 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<bool> TopmostProperty = public static readonly StyledProperty<bool> TopmostProperty =
AvaloniaProperty.Register<Popup, bool>(nameof(Topmost)); AvaloniaProperty.Register<Popup, bool>(nameof(Topmost));
private bool _isOpenRequested = false;
private bool _isOpen; private bool _isOpen;
private bool _ignoreIsOpenChanged; private bool _ignoreIsOpenChanged;
private PopupOpenState? _openState; private PopupOpenState? _openState;
@ -361,17 +362,19 @@ namespace Avalonia.Controls.Primitives
if (placementTarget == null) if (placementTarget == null)
{ {
throw new InvalidOperationException("Popup has no logical parent and PlacementTarget is null"); _isOpenRequested = true;
return;
} }
var topLevel = placementTarget.VisualRoot as TopLevel; var topLevel = placementTarget.VisualRoot as TopLevel;
if (topLevel == null) if (topLevel == null)
{ {
throw new InvalidOperationException( _isOpenRequested = true;
"Attempted to open a popup not attached to a TopLevel"); return;
} }
_isOpenRequested = false;
var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver); var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver);
var handlerCleanup = new CompositeDisposable(5); var handlerCleanup = new CompositeDisposable(5);
@ -492,6 +495,17 @@ namespace Avalonia.Controls.Primitives
return new Size(); return new Size();
} }
/// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
if (_isOpenRequested)
{
Open();
}
}
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{ {
@ -552,6 +566,7 @@ namespace Avalonia.Controls.Primitives
private void CloseCore() private void CloseCore()
{ {
_isOpenRequested = false;
if (_openState is null) if (_openState is null)
{ {
using (BeginIgnoringIsOpen()) using (BeginIgnoringIsOpen())

4
src/Avalonia.Themes.Default/ComboBox.xaml

@ -26,6 +26,7 @@
<Setter Property="Padding" Value="4" /> <Setter Property="Padding" Value="4" />
<Setter Property="MinHeight" Value="20" /> <Setter Property="MinHeight" Value="20" />
<Setter Property="PlaceholderForeground" Value="{DynamicResource ThemeForegroundBrush}" /> <Setter Property="PlaceholderForeground" Value="{DynamicResource ThemeForegroundBrush}" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<Border Name="border" <Border Name="border"
@ -69,7 +70,8 @@
IsLightDismissEnabled="True"> IsLightDismissEnabled="True">
<Border BorderBrush="{DynamicResource ThemeBorderMidBrush}" <Border BorderBrush="{DynamicResource ThemeBorderMidBrush}"
BorderThickness="1"> BorderThickness="1">
<ScrollViewer> <ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}">
<ItemsPresenter Name="PART_ItemsPresenter" <ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}" Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}" ItemsPanel="{TemplateBinding ItemsPanel}"

2
src/Avalonia.Themes.Default/MenuItem.xaml

@ -64,7 +64,7 @@
<Border Background="{TemplateBinding Background}" <Border Background="{TemplateBinding Background}"
BorderBrush="{DynamicResource ThemeBorderMidBrush}" BorderBrush="{DynamicResource ThemeBorderMidBrush}"
BorderThickness="{TemplateBinding BorderThickness}"> BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer> <ScrollViewer Classes="menuscroller">
<ItemsPresenter Name="PART_ItemsPresenter" <ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}" Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}" ItemsPanel="{TemplateBinding ItemsPanel}"

3
src/Avalonia.Themes.Fluent/ComboBox.xaml

@ -133,7 +133,8 @@
Padding="{DynamicResource ComboBoxDropdownBorderPadding}" Padding="{DynamicResource ComboBoxDropdownBorderPadding}"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
CornerRadius="{DynamicResource OverlayCornerRadius}"> CornerRadius="{DynamicResource OverlayCornerRadius}">
<ScrollViewer> <ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}">
<ItemsPresenter Name="PART_ItemsPresenter" <ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}" Items="{TemplateBinding Items}"
Margin="{DynamicResource ComboBoxDropdownContentMargin}" Margin="{DynamicResource ComboBoxDropdownContentMargin}"

2
src/Avalonia.Themes.Fluent/ContextMenu.xaml

@ -49,7 +49,7 @@
MinHeight="{TemplateBinding MinHeight}" MinHeight="{TemplateBinding MinHeight}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
CornerRadius="{DynamicResource OverlayCornerRadius}"> CornerRadius="{DynamicResource OverlayCornerRadius}">
<ScrollViewer> <ScrollViewer Classes="menuscroller">
<ItemsPresenter Name="PART_ItemsPresenter" <ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}" Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}" ItemsPanel="{TemplateBinding ItemsPanel}"

4
src/Avalonia.Themes.Fluent/MenuItem.xaml

@ -123,7 +123,7 @@
MinHeight="{DynamicResource MenuFlyoutThemeMinHeight}" MinHeight="{DynamicResource MenuFlyoutThemeMinHeight}"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
CornerRadius="{DynamicResource OverlayCornerRadius}"> CornerRadius="{DynamicResource OverlayCornerRadius}">
<ScrollViewer> <ScrollViewer Classes="menuscroller">
<ItemsPresenter Name="PART_ItemsPresenter" <ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}" Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}" ItemsPanel="{TemplateBinding ItemsPanel}"
@ -171,7 +171,7 @@
MinHeight="{DynamicResource MenuFlyoutThemeMinHeight}" MinHeight="{DynamicResource MenuFlyoutThemeMinHeight}"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
CornerRadius="{DynamicResource OverlayCornerRadius}"> CornerRadius="{DynamicResource OverlayCornerRadius}">
<ScrollViewer> <ScrollViewer Classes="menuscroller">
<ItemsPresenter Name="PART_ItemsPresenter" <ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}" Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}" ItemsPanel="{TemplateBinding ItemsPanel}"

92
src/Avalonia.Themes.Fluent/ScrollViewer.xaml

@ -1,5 +1,21 @@
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls">
<Design.PreviewWith>
<Grid ColumnDefinitions="*,20,*"
Height="300"
Width="200"
Margin="20">
<ScrollViewer>
<Border Height="1000" Background="#ccc"/>
</ScrollViewer>
<ScrollViewer Grid.Column="2" Classes="menuscroller">
<Border Height="1000" Background="#ccc"/>
</ScrollViewer>
</Grid>
</Design.PreviewWith>
<Style Selector="ScrollViewer"> <Style Selector="ScrollViewer">
<Setter Property="Background" <Setter Property="Background"
Value="Transparent" /> Value="Transparent" />
@ -66,4 +82,78 @@
<Setter Property="Opacity" Value="1" /> <Setter Property="Opacity" Value="1" />
</Style> </Style>
<Style Selector="ScrollViewer.menuscroller">
<Setter Property="Template">
<ControlTemplate>
<DockPanel>
<RepeatButton DockPanel.Dock="Top"
BorderThickness="0"
Background="Transparent"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
RenderTransform="{x:Null}"
Command="{Binding LineUp, RelativeSource={RelativeSource TemplatedParent}}">
<RepeatButton.IsVisible>
<MultiBinding Converter="{x:Static converters:MenuScrollingVisibilityConverter.Instance}"
ConverterParameter="0">
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="VerticalScrollBarVisibility"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Offset.Y"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Extent.Height"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Viewport.Height"/>
</MultiBinding>
</RepeatButton.IsVisible>
<Viewbox Width="{DynamicResource ScrollBarButtonArrowIconFontSize}"
Height="{DynamicResource ScrollBarButtonArrowIconFontSize}">
<Path VerticalAlignment="Center"
HorizontalAlignment="Center"
Data="M 19.091797 14.970703 L 10 5.888672 L 0.908203 14.970703 L 0.029297 14.091797 L 10 4.111328 L 19.970703 14.091797 Z"
Width="20"
Height="20" />
</Viewbox>
</RepeatButton>
<RepeatButton DockPanel.Dock="Bottom"
BorderThickness="0"
Background="Transparent"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
RenderTransform="{x:Null}"
Command="{Binding LineDown, RelativeSource={RelativeSource TemplatedParent}}">
<RepeatButton.IsVisible>
<MultiBinding Converter="{x:Static converters:MenuScrollingVisibilityConverter.Instance}"
ConverterParameter="100">
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="VerticalScrollBarVisibility"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Offset.Y"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Extent.Height"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Viewport.Height"/>
</MultiBinding>
</RepeatButton.IsVisible>
<Viewbox Width="{DynamicResource ScrollBarButtonArrowIconFontSize}"
Height="{DynamicResource ScrollBarButtonArrowIconFontSize}">
<Path VerticalAlignment="Center"
HorizontalAlignment="Center"
Data="M 18.935547 4.560547 L 19.814453 5.439453 L 10 15.253906 L 0.185547 5.439453 L 1.064453 4.560547 L 10 13.496094 Z"
Width="20"
Height="20" />
</Viewbox>
</RepeatButton>
<ScrollContentPresenter Name="PART_ContentPresenter"
CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}"
CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}"
Content="{TemplateBinding Content}"
Extent="{TemplateBinding Extent, Mode=TwoWay}"
Margin="{TemplateBinding Padding}"
Offset="{TemplateBinding Offset, Mode=TwoWay}"
Viewport="{TemplateBinding Viewport, Mode=TwoWay}"/>
</DockPanel>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="ScrollViewer.menuscroller /template/ RepeatButton > Viewbox > Path">
<Setter Property="Fill" Value="{DynamicResource ScrollBarButtonArrowForeground}" />
</Style>
<Style Selector="ScrollViewer.menuscroller /template/ RepeatButton:pointerover > Viewbox > Path">
<Setter Property="Fill" Value="{DynamicResource ScrollBarButtonArrowForegroundPointerOver}" />
</Style>
</Styles> </Styles>

4
tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs

@ -59,12 +59,12 @@ namespace Avalonia.Markup.UnitTests.Data.Plugins
{ {
new BindingNotification(5), new BindingNotification(5),
new BindingNotification( new BindingNotification(
new ValidationException(errmsg), new DataValidationException(errmsg),
BindingErrorType.DataValidationError, BindingErrorType.DataValidationError,
3), 3),
new BindingNotification(7), new BindingNotification(7),
new BindingNotification( new BindingNotification(
new ValidationException(errmsg), new DataValidationException(errmsg),
BindingErrorType.DataValidationError, BindingErrorType.DataValidationError,
11), 11),
}, result); }, result);

2
tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs

@ -2,7 +2,6 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.UnitTests; using Avalonia.UnitTests;
@ -27,6 +26,7 @@ namespace Avalonia.Controls.UnitTests
Assert.True(target.IsDropDownOpen); Assert.True(target.IsDropDownOpen);
_helper.Down(target); _helper.Down(target);
_helper.Up(target);
Assert.False(target.IsDropDownOpen); Assert.False(target.IsDropDownOpen);
} }

10
tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs

@ -144,6 +144,16 @@ namespace Avalonia.Controls.UnitTests.Generators
Assert.Equal("bar", container.Content); Assert.Equal("bar", container.Content);
} }
[Fact]
public void Materialize_Should_Create_Containers_When_Item_Is_Null()
{
var owner = new Decorator();
var target = new ItemContainerGenerator<ListBoxItem>(owner, ListBoxItem.ContentProperty, null);
var container = (ListBoxItem)target.Materialize(0, null).ContainerControl;
Assert.True(container != null, "The containers is not materialized.");
}
private IList<ItemContainerInfo> Materialize( private IList<ItemContainerInfo> Materialize(
IItemContainerGenerator generator, IItemContainerGenerator generator,
int index, int index,

45
tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

@ -21,7 +21,50 @@ namespace Avalonia.Controls.UnitTests.Primitives
public class PopupTests public class PopupTests
{ {
protected bool UsePopupHost; 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] [Fact]
public void Setting_Child_Should_Set_Child_Controls_LogicalParent() public void Setting_Child_Should_Set_Child_Controls_LogicalParent()
{ {

50
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) private static GlyphRun CreateGlyphRun(double[] glyphAdvances, ushort[] glyphClusters, int bidiLevel = 0)
{ {
var count = glyphAdvances.Length; var count = glyphAdvances.Length;

Loading…
Cancel
Save