Browse Source

Merge branch 'master' into issues/skiadrawingcontextctor

pull/4771/head
Max Katz 6 years ago
committed by GitHub
parent
commit
b3513d584f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      samples/ControlCatalog/App.xaml.cs
  2. 2
      samples/ControlCatalog/MainView.xaml
  3. 4
      samples/ControlCatalog/MainView.xaml.cs
  4. 17
      samples/ControlCatalog/Pages/DataGridPage.xaml
  5. 4
      samples/ControlCatalog/Pages/TextBlockPage.xaml
  6. 4
      samples/RenderDemo/App.xaml.cs
  7. 2
      src/Avalonia.Animation/KeySplineTypeConverter.cs
  8. 17
      src/Avalonia.Base/AvaloniaLocator.cs
  9. 12
      src/Avalonia.Base/AvaloniaObject.cs
  10. 4
      src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs
  11. 44
      src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs
  12. 5
      src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt
  13. 24
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  14. 34
      src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs
  15. 39
      src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs
  16. 231
      src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs
  17. 21
      src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
  18. 22
      src/Avalonia.Controls.DataGrid/Utils/DataGridHelper.cs
  19. 9
      src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
  20. 79
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  21. 4
      src/Avalonia.Controls/ComboBox.cs
  22. 6
      src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs
  23. 4
      src/Avalonia.Controls/MenuItem.cs
  24. 18
      src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
  25. 16
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  26. 3
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  27. 43
      src/Avalonia.Controls/Primitives/Popup.cs
  28. 15
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  29. 12
      src/Avalonia.Controls/TreeView.cs
  30. 11
      src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
  31. 3
      src/Avalonia.OpenGL/Egl/EglContext.cs
  32. 22
      src/Avalonia.OpenGL/Egl/EglDisplay.cs
  33. 13
      src/Avalonia.OpenGL/GlInterface.cs
  34. 4
      src/Avalonia.Themes.Default/ComboBox.xaml
  35. 2
      src/Avalonia.Themes.Default/MenuItem.xaml
  36. 7
      src/Avalonia.Themes.Default/PathIcon.xaml
  37. 8
      src/Avalonia.Themes.Default/ToggleSwitch.xaml
  38. 2
      src/Avalonia.Themes.Fluent/Accents/Base.xaml
  39. 2
      src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml
  40. BIN
      src/Avalonia.Themes.Fluent/Assets/Inter-Bold.ttf
  41. BIN
      src/Avalonia.Themes.Fluent/Assets/Inter-ExtraLight.ttf
  42. BIN
      src/Avalonia.Themes.Fluent/Assets/Inter-Light.ttf
  43. BIN
      src/Avalonia.Themes.Fluent/Assets/Inter-Medium.ttf
  44. BIN
      src/Avalonia.Themes.Fluent/Assets/Inter-Regular.ttf
  45. BIN
      src/Avalonia.Themes.Fluent/Assets/Inter-SemiBold.ttf
  46. BIN
      src/Avalonia.Themes.Fluent/Assets/Inter-Thin.ttf
  47. BIN
      src/Avalonia.Themes.Fluent/Assets/Roboto-Black.ttf
  48. BIN
      src/Avalonia.Themes.Fluent/Assets/Roboto-Bold.ttf
  49. BIN
      src/Avalonia.Themes.Fluent/Assets/Roboto-Light.ttf
  50. BIN
      src/Avalonia.Themes.Fluent/Assets/Roboto-Medium.ttf
  51. BIN
      src/Avalonia.Themes.Fluent/Assets/Roboto-Regular.ttf
  52. BIN
      src/Avalonia.Themes.Fluent/Assets/Roboto-Thin.ttf
  53. 33
      src/Avalonia.Themes.Fluent/CheckBox.xaml
  54. 3
      src/Avalonia.Themes.Fluent/ComboBox.xaml
  55. 2
      src/Avalonia.Themes.Fluent/ContextMenu.xaml
  56. 8
      src/Avalonia.Themes.Fluent/DataValidationErrors.xaml
  57. 85
      src/Avalonia.Themes.Fluent/ListBoxItem.xaml
  58. 4
      src/Avalonia.Themes.Fluent/MenuItem.xaml
  59. 7
      src/Avalonia.Themes.Fluent/PathIcon.xaml
  60. 92
      src/Avalonia.Themes.Fluent/ScrollViewer.xaml
  61. 10
      src/Avalonia.Themes.Fluent/ToggleSwitch.xaml
  62. 61
      src/Avalonia.Visuals/Animation/CompositePageTransition.cs
  63. 29
      src/Avalonia.Visuals/Animation/CrossFade.cs
  64. 44
      src/Avalonia.Visuals/Animation/PageSlide.cs
  65. 6
      src/Avalonia.Visuals/ApiCompatBaseline.txt
  66. 17
      src/Avalonia.Visuals/Media/GlyphRun.cs
  67. 2
      src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs
  68. 67
      src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
  69. 44
      src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
  70. 16
      src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
  71. 19
      src/Avalonia.Visuals/Point.cs
  72. 8
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  73. 2
      src/Avalonia.Visuals/Rendering/RenderLayer.cs
  74. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  75. 19
      src/Avalonia.Visuals/Size.cs
  76. 23
      src/Avalonia.Visuals/Thickness.cs
  77. 19
      src/Avalonia.Visuals/Vector.cs
  78. 12
      src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
  79. 57
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  80. 14
      src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
  81. 134
      src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs
  82. 32
      src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
  83. 7
      src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
  84. 2
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  85. 72
      src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
  86. 2
      src/Windows/Avalonia.Direct2D1/ExternalRenderTarget.cs
  87. 2
      src/Windows/Avalonia.Direct2D1/ILayerFactory.cs
  88. 4
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  89. 11
      src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
  90. 5
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs
  91. 2
      src/Windows/Avalonia.Direct2D1/RenderTarget.cs
  92. 2
      src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs
  93. 92
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  94. 62
      src/Windows/Avalonia.Win32/OpenGl/WglConsts.cs
  95. 85
      src/Windows/Avalonia.Win32/OpenGl/WglContext.cs
  96. 183
      src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs
  97. 85
      src/Windows/Avalonia.Win32/OpenGl/WglGlPlatformSurface.cs
  98. 39
      src/Windows/Avalonia.Win32/OpenGl/WglPlatformOpenGlInterface.cs
  99. 39
      src/Windows/Avalonia.Win32/OpenGl/WglRestoreContext.cs
  100. 18
      src/Windows/Avalonia.Win32/Win32GlManager.cs

2
samples/ControlCatalog/App.xaml.cs

@ -81,7 +81,7 @@ namespace ControlCatalog
public override void Initialize()
{
Styles.Insert(0, FluentDark);
Styles.Insert(0, FluentLight);
AvaloniaXamlLoader.Load(this);
}

2
samples/ControlCatalog/MainView.xaml

@ -77,8 +77,8 @@
<ComboBoxItem>Full Decorations</ComboBoxItem>
</ComboBox>
<ComboBox x:Name="Themes" SelectedIndex="0">
<ComboBoxItem>Fluent - Dark</ComboBoxItem>
<ComboBoxItem>Fluent - Light</ComboBoxItem>
<ComboBoxItem>Fluent - Dark</ComboBoxItem>
<ComboBoxItem>Simple - Light</ComboBoxItem>
<ComboBoxItem>Simple - Dark</ComboBoxItem>
</ComboBox>

4
samples/ControlCatalog/MainView.xaml.cs

@ -38,10 +38,10 @@ namespace ControlCatalog
switch (themes.SelectedIndex)
{
case 0:
Application.Current.Styles[0] = App.FluentDark;
Application.Current.Styles[0] = App.FluentLight;
break;
case 1:
Application.Current.Styles[0] = App.FluentLight;
Application.Current.Styles[0] = App.FluentDark;
break;
case 2:
Application.Current.Styles[0] = App.DefaultLight;

17
samples/ControlCatalog/Pages/DataGridPage.xaml

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

4
samples/ControlCatalog/Pages/TextBlockPage.xaml

@ -18,8 +18,8 @@
</StackPanel.Styles>
<Border>
<StackPanel Width="200" Spacing="8">
<TextBlock TextTrimming="CharacterEllipsis" Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit."/>
<TextBlock TextTrimming="WordEllipsis" Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit."/>
<TextBlock Margin="0 0 10 0" TextTrimming="CharacterEllipsis" Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit."/>
<TextBlock Margin="0 0 10 0" TextTrimming="WordEllipsis" Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit."/>
<TextBlock Text="Left aligned text" TextAlignment="Left" />
<TextBlock Text="Center aligned text" TextAlignment="Center" />
<TextBlock Text="Right aligned text" TextAlignment="Right" />

4
samples/RenderDemo/App.xaml.cs

@ -18,6 +18,10 @@ namespace RenderDemo
// App configuration, used by the entry point and previewer
static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.With(new Win32PlatformOptions
{
OverlayPopups = true,
})
.UsePlatformDetect()
.UseReactiveUI()
.LogToDebug();

2
src/Avalonia.Animation/KeySplineTypeConverter.cs

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

17
src/Avalonia.Base/AvaloniaLocator.cs

@ -54,6 +54,23 @@ namespace Avalonia
return _locator;
}
public AvaloniaLocator ToLazy<TImlp>(Func<TImlp> func) where TImlp : TService
{
var constructed = false;
TImlp instance = default;
_locator._registry[typeof (TService)] = () =>
{
if (!constructed)
{
instance = func();
constructed = true;
}
return instance;
};
return _locator;
}
public AvaloniaLocator ToSingleton<TImpl>() where TImpl : class, TService, new()
{
TImpl instance = null;

12
src/Avalonia.Base/AvaloniaObject.cs

@ -113,16 +113,8 @@ namespace Avalonia
/// <param name="binding">The binding information.</param>
public IBinding this[IndexerDescriptor binding]
{
get
{
return new IndexerBinding(this, binding.Property, binding.Mode);
}
set
{
var sourceBinding = value as IBinding;
this.Bind(binding.Property, sourceBinding);
}
get { return new IndexerBinding(this, binding.Property, binding.Mode); }
set { this.Bind(binding.Property, value); }
}
public bool CheckAccess() => Dispatcher.UIThread.CheckAccess();

4
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)));
}
}
}

44
src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using Avalonia.Utilities;
@ -11,8 +12,11 @@ namespace Avalonia.Data.Core.Plugins
/// </summary>
public class InpcPropertyAccessorPlugin : IPropertyAccessorPlugin
{
private readonly Dictionary<(Type, string), PropertyInfo> _propertyLookup =
new Dictionary<(Type, string), PropertyInfo>();
/// <inheritdoc/>
public bool Match(object obj, string propertyName) => GetPropertyWithName(obj.GetType(), propertyName) != null;
public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj.GetType(), propertyName) != null;
/// <summary>
/// Starts monitoring the value of a property on an object.
@ -30,7 +34,7 @@ namespace Avalonia.Data.Core.Plugins
reference.TryGetTarget(out object instance);
var p = GetPropertyWithName(instance.GetType(), propertyName);
var p = GetFirstPropertyWithName(instance.GetType(), propertyName);
if (p != null)
{
@ -44,12 +48,40 @@ namespace Avalonia.Data.Core.Plugins
}
}
private static PropertyInfo GetPropertyWithName(Type type, string propertyName)
private PropertyInfo GetFirstPropertyWithName(Type type, string propertyName)
{
const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public |
BindingFlags.Static | BindingFlags.Instance;
var key = (type, propertyName);
if (!_propertyLookup.TryGetValue(key, out PropertyInfo propertyInfo))
{
propertyInfo = TryFindAndCacheProperty(type, propertyName);
}
return propertyInfo;
}
private PropertyInfo TryFindAndCacheProperty(Type type, string propertyName)
{
PropertyInfo found = null;
const BindingFlags bindingFlags =
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
var properties = type.GetProperties(bindingFlags);
foreach (PropertyInfo propertyInfo in properties)
{
if (propertyInfo.Name == propertyName)
{
found = propertyInfo;
break;
}
}
_propertyLookup.Add((type, propertyName), found);
return type.GetProperty(propertyName, bindingFlags);
return found;
}
private class Accessor : PropertyAccessorBase, IWeakSubscriber<PropertyChangedEventArgs>

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>
/// Displays data in a customizable grid.
/// </summary>
[PseudoClasses(":invalid")]
[PseudoClasses(":invalid", ":empty-rows", ":empty-columns")]
public partial class DataGrid : TemplatedControl
{
private const string DATAGRID_elementRowsPresenterName = "PART_RowsPresenter";
@ -711,6 +711,7 @@ namespace Avalonia.Controls
DisplayData = new DataGridDisplayData(this);
ColumnsInternal = CreateColumnsInstance();
ColumnsInternal.CollectionChanged += ColumnsInternal_CollectionChanged;
RowHeightEstimate = DATAGRID_defaultRowHeight;
RowDetailsHeightEstimate = 0;
@ -727,6 +728,8 @@ namespace Avalonia.Controls
CurrentCellCoordinates = new DataGridCellCoordinates(-1, -1);
RowGroupHeaderHeightEstimate = DATAGRID_defaultRowHeight;
UpdatePseudoClasses();
}
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
_measured = false;
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)
{
if (!_areHandlersSuspended)
@ -1348,7 +1369,6 @@ namespace Avalonia.Controls
internal DataGridColumnCollection ColumnsInternal
{
get;
private set;
}
internal int AnchorSlot

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

@ -17,9 +17,7 @@ namespace Avalonia.Controls
/// </summary>
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;
}
/// <summary>
/// Defines the <see cref="IsThreeState"/> property.
/// </summary>
public static StyledProperty<bool> IsThreeStateProperty =
CheckBox.IsThreeStateProperty.AddOwner<DataGridCheckBoxColumn>();
/// <summary>
/// Gets or sets a value that indicates whether the hosted <see cref="T:System.Windows.Controls.CheckBox" /> controls allow three states or two.
/// </summary>
@ -39,17 +43,17 @@ namespace Avalonia.Controls
/// </returns>
public bool IsThreeState
{
get
{
return _isThreeState;
}
set
get => GetValue(IsThreeStateProperty);
set => SetValue(IsThreeStateProperty, value);
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> 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()

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

@ -77,24 +77,7 @@ namespace Avalonia.Controls
private set;
}
public int Count
{
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 int Count => GetCount(true);
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>
/// Puts the entity into editing mode if possible
/// </summary>
@ -675,6 +676,8 @@ namespace Avalonia.Controls
}
break;
}
_owner.UpdatePseudoClasses();
}
private void UpdateDataProperties()

231
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;
/// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridTextColumn" /> class.
/// </summary>
@ -36,18 +31,24 @@ namespace Avalonia.Controls
/// <summary>
/// Identifies the FontFamily dependency property.
/// </summary>
public static readonly StyledProperty<string> FontFamilyProperty =
AvaloniaProperty.Register<DataGridTextColumn, string>(nameof(FontFamily));
public static readonly AttachedProperty<FontFamily> FontFamilyProperty =
TextBlock.FontFamilyProperty.AddOwner<DataGridTextColumn>();
/// <summary>
/// Gets or sets the font name.
/// </summary>
public string FontFamily
public FontFamily FontFamily
{
get { return GetValue(FontFamilyProperty); }
set { SetValue(FontFamilyProperty, value); }
get => GetValue(FontFamilyProperty);
set => SetValue(FontFamilyProperty, value);
}
/// <summary>
/// Identifies the FontSize dependency property.
/// </summary>
public static readonly AttachedProperty<double> FontSizeProperty =
TextBlock.FontSizeProperty.AddOwner<DataGridTextColumn>();
/// <summary>
/// Gets or sets the font size.
/// </summary>
@ -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);
}
/// <summary>
/// Identifies the FontStyle dependency property.
/// </summary>
public static readonly AttachedProperty<FontStyle> FontStyleProperty =
TextBlock.FontStyleProperty.AddOwner<DataGridTextColumn>();
/// <summary>
/// Gets or sets the font style.
/// </summary>
public FontStyle FontStyle
{
get
{
return _fontStyle ?? FontStyle.Normal;
}
set
{
if (_fontStyle != value)
{
_fontStyle = value;
NotifyPropertyChanged(nameof(FontStyle));
}
}
get => GetValue(FontStyleProperty);
set => SetValue(FontStyleProperty, value);
}
/// <summary>
/// Identifies the FontWeight dependency property.
/// </summary>
public static readonly AttachedProperty<FontWeight> FontWeightProperty =
TextBlock.FontWeightProperty.AddOwner<DataGridTextColumn>();
/// <summary>
/// Gets or sets the font weight or thickness.
/// </summary>
public FontWeight FontWeight
{
get
{
return _fontWeight ?? FontWeight.Normal;
}
set
{
if (_fontWeight != value)
{
_fontWeight = value;
NotifyPropertyChanged(nameof(FontWeight));
}
}
get => GetValue(FontWeightProperty);
set => SetValue(FontWeightProperty, value);
}
/// <summary>
/// Identifies the Foreground dependency property.
/// </summary>
public static readonly AttachedProperty<IBrush> ForegroundProperty =
TextBlock.ForegroundProperty.AddOwner<DataGridTextColumn>();
/// <summary>
/// Gets or sets a brush that describes the foreground of the column cells.
/// </summary>
public IBrush Foreground
{
get
{
return _foreground;
}
set
get => GetValue(ForegroundProperty);
set => SetValue(ForegroundProperty, value);
}
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)
{
_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);
}
}
}

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

@ -588,18 +588,11 @@
<!--<DataGridColumnHeader Name="PART_TopRightCornerHeader"
Grid.Column="2"
Template="{StaticResource TopRightHeaderTemplate}" />-->
<!--<Rectangle Name="PART_ColumnHeadersAndRowsSeparator"
<Rectangle Name="PART_ColumnHeadersAndRowsSeparator"
Grid.ColumnSpan="3"
VerticalAlignment="Bottom"
StrokeThickness="1"
Height="1"
Fill="{DynamicResource DataGridGridLinesBrush}" />-->
<Border Name="PART_ColumnHeadersAndRowsSeparator"
Grid.ColumnSpan="3"
Height="2"
VerticalAlignment="Bottom"
BorderThickness="0,0,0,1"
BorderBrush="{DynamicResource DataGridGridLinesBrush}" />
Fill="{DynamicResource DataGridGridLinesBrush}" />
<DataGridRowsPresenter Name="PART_RowsPresenter"
Grid.Row="1"
@ -642,4 +635,14 @@
</ControlTemplate>
</Setter>
</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>

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.DisplayDateChanged -= Calendar_DisplayDateChanged;
_calendar.SelectedDatesChanged -= Calendar_SelectedDatesChanged;
_calendar.PointerPressed -= Calendar_PointerPressed;
_calendar.PointerReleased -= Calendar_PointerReleased;
_calendar.KeyDown -= Calendar_KeyDown;
}
_calendar = e.NameScope.Find<Calendar>(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;
}

79
src/Avalonia.Controls/Calendar/CalendarItem.cs

@ -36,11 +36,7 @@ namespace Avalonia.Controls.Primitives
private Button _headerButton;
private Button _nextButton;
private Button _previousButton;
private Grid _monthView;
private Grid _yearView;
private ITemplate<IControl> _dayTitleTemplate;
private CalendarButton _lastCalendarButton;
private CalendarDayButton _lastCalendarDayButton;
private DateTime _currentMonth;
private bool _isMouseLeftButtonDown = false;
@ -160,38 +156,12 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// Gets the Grid that hosts the content when in month mode.
/// </summary>
internal Grid MonthView
{
get { return _monthView; }
private set
{
if (_monthView != null)
_monthView.PointerLeave -= MonthView_MouseLeave;
_monthView = value;
if (_monthView != null)
_monthView.PointerLeave += MonthView_MouseLeave;
}
}
internal Grid MonthView { get; set; }
/// <summary>
/// Gets the Grid that hosts the content when in year or decade mode.
/// </summary>
internal Grid YearView
{
get { return _yearView; }
private set
{
if (_yearView != null)
_yearView.PointerLeave -= YearView_MouseLeave;
_yearView = value;
if (_yearView != null)
_yearView.PointerLeave += YearView_MouseLeave;
}
}
internal Grid YearView { get; set; }
private void PopulateGrids()
{
if (MonthView != null)
@ -226,7 +196,6 @@ namespace Avalonia.Controls.Primitives
cell.CalendarDayButtonMouseDown += Cell_MouseLeftButtonDown;
cell.CalendarDayButtonMouseUp += Cell_MouseLeftButtonUp;
cell.PointerEnter += Cell_MouseEnter;
cell.PointerLeave += Cell_MouseLeave;
cell.Click += Cell_Click;
children.Add(cell);
}
@ -256,7 +225,6 @@ namespace Avalonia.Controls.Primitives
month.CalendarLeftMouseButtonDown += Month_CalendarButtonMouseDown;
month.CalendarLeftMouseButtonUp += Month_CalendarButtonMouseUp;
month.PointerEnter += Month_MouseEnter;
month.PointerLeave += Month_MouseLeave;
children.Add(month);
}
}
@ -937,17 +905,7 @@ namespace Avalonia.Controls.Primitives
}
}
}
internal void Cell_MouseLeave(object sender, PointerEventArgs e)
{
if (_isMouseLeftButtonDown)
{
CalendarDayButton b = (CalendarDayButton)sender;
// The button is in Pressed state. Change the state to normal.
if (e.Pointer.Captured == b)
e.Pointer.Capture(null);
_lastCalendarDayButton = b;
}
}
internal void Cell_MouseLeftButtonDown(object sender, PointerPressedEventArgs e)
{
if (Owner != null)
@ -1207,35 +1165,6 @@ namespace Avalonia.Controls.Primitives
}
}
private void Month_MouseLeave(object sender, PointerEventArgs e)
{
if (_isMouseLeftButtonDownYearView)
{
CalendarButton b = (CalendarButton)sender;
// The button is in Pressed state. Change the state to normal.
if (e.Pointer.Captured == b)
e.Pointer.Capture(null);
//b.ReleaseMouseCapture();
_lastCalendarButton = b;
}
}
private void MonthView_MouseLeave(object sender, PointerEventArgs e)
{
if (_lastCalendarDayButton != null)
{
e.Pointer.Capture(_lastCalendarDayButton);
}
}
private void YearView_MouseLeave(object sender, PointerEventArgs e)
{
if (_lastCalendarButton != null)
{
e.Pointer.Capture(_lastCalendarButton);
}
}
internal void UpdateDisabled(bool isEnabled)
{
PseudoClasses.Set(":calendardisabled", !isEnabled);

4
src/Avalonia.Controls/ComboBox.cs

@ -257,7 +257,7 @@ namespace Avalonia.Controls
}
/// <inheritdoc/>
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);
}
/// <inheritdoc/>

6
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;
}

4
src/Avalonia.Controls/MenuItem.cs

@ -101,7 +101,7 @@ namespace Avalonia.Controls
private ICommand? _command;
private bool _commandCanExecute = true;
private Popup _popup;
private Popup? _popup;
/// <summary>
/// Initializes static members of the <see cref="MenuItem"/> class.
@ -145,7 +145,7 @@ namespace Avalonia.Controls
{
var parent = x as Control;
return parent?.GetObservable(DefinitionBase.PrivateSharedSizeScopeProperty) ??
Observable.Return<DefinitionBase.SharedSizeScope>(null);
Observable.Return<DefinitionBase.SharedSizeScope?>(null);
});
this.Bind(DefinitionBase.PrivateSharedSizeScopeProperty, parentSharedSizeScope);

18
src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs

@ -148,6 +148,7 @@ namespace Avalonia.Controls.Platform
{
case Key.Up:
case Key.Down:
{
if (item?.IsTopLevel == true)
{
if (item.HasSubMenu && !item.IsSubMenuOpen)
@ -161,8 +162,10 @@ namespace Avalonia.Controls.Platform
goto default;
}
break;
}
case Key.Left:
{
if (item?.Parent is IMenuItem parent && !parent.IsTopLevel && parent.IsSubMenuOpen)
{
parent.Close();
@ -174,8 +177,10 @@ namespace Avalonia.Controls.Platform
goto default;
}
break;
}
case Key.Right:
{
if (item != null && !item.IsTopLevel && item.HasSubMenu)
{
Open(item, true);
@ -186,8 +191,10 @@ namespace Avalonia.Controls.Platform
goto default;
}
break;
}
case Key.Enter:
{
if (item != null)
{
if (!item.HasSubMenu)
@ -202,12 +209,14 @@ namespace Avalonia.Controls.Platform
e.Handled = true;
}
break;
}
case Key.Escape:
if (item?.Parent != null)
{
if (item?.Parent is IMenuElement parent)
{
item.Parent.Close();
item.Parent.Focus();
parent.Close();
parent.Focus();
}
else
{
@ -216,8 +225,10 @@ namespace Avalonia.Controls.Platform
e.Handled = true;
break;
}
default:
{
var direction = e.Key.ToNavigationDirection();
if (direction.HasValue)
@ -246,6 +257,7 @@ namespace Avalonia.Controls.Platform
}
break;
}
}
if (!e.Handled && item?.Parent is IMenuItem parentItem)

16
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@ -1,9 +1,9 @@
using System;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Controls.Utils;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
@ -14,6 +14,7 @@ namespace Avalonia.Controls.Presenters
/// <summary>
/// Presents a single item of data inside a <see cref="TemplatedControl"/> template.
/// </summary>
[PseudoClasses(":empty")]
public class ContentPresenter : Control, IContentPresenter
{
/// <summary>
@ -102,6 +103,11 @@ namespace Avalonia.Controls.Presenters
TemplatedParentProperty.Changed.AddClassHandler<ContentPresenter>((x, e) => x.TemplatedParentChanged(e));
}
public ContentPresenter()
{
UpdatePseudoClasses();
}
/// <summary>
/// Gets or sets a brush with which to paint the background.
/// </summary>
@ -424,9 +430,15 @@ namespace Avalonia.Controls.Presenters
_recyclingDataTemplate = null;
}
UpdatePseudoClasses();
InvalidateMeasure();
}
private void UpdatePseudoClasses()
{
PseudoClasses.Set(":empty", Content is null);
}
private double GetLayoutScale()
{
var result = (VisualRoot as ILayoutRoot)?.LayoutScaling ?? 1.0;

3
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -77,7 +77,8 @@ namespace Avalonia.Controls.Presenters
static TextPresenter()
{
AffectsRender<TextPresenter>(SelectionBrushProperty);
AffectsRender<TextPresenter>(SelectionBrushProperty, TextBlock.ForegroundProperty,
SelectionForegroundBrushProperty, CaretBrushProperty);
AffectsMeasure<TextPresenter>(TextProperty, PasswordCharProperty, RevealPasswordProperty,
TextAlignmentProperty, TextWrappingProperty, TextBlock.FontSizeProperty,
TextBlock.FontStyleProperty, TextBlock.FontWeightProperty, TextBlock.FontFamilyProperty);

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

@ -128,6 +128,7 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<bool> TopmostProperty =
AvaloniaProperty.Register<Popup, bool>(nameof(Topmost));
private bool _isOpenRequested = false;
private bool _isOpen;
private bool _ignoreIsOpenChanged;
private PopupOpenState? _openState;
@ -357,21 +358,23 @@ namespace Avalonia.Controls.Primitives
return;
}
var placementTarget = PlacementTarget ?? this.GetLogicalAncestors().OfType<IVisual>().FirstOrDefault();
var placementTarget = PlacementTarget ?? this.FindLogicalAncestorOfType<IControl>();
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();
}
/// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
if (_isOpenRequested)
{
Open();
}
}
/// <inheritdoc/>
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())
@ -571,6 +586,26 @@ namespace Avalonia.Controls.Primitives
}
Closed?.Invoke(this, EventArgs.Empty);
var focusCheck = FocusManager.Instance?.Current;
// Focus is set to null as part of popup closing, so we only want to
// set focus to PlacementTarget if this is the case
if (focusCheck == null)
{
if (PlacementTarget != null)
{
FocusManager.Instance?.Focus(PlacementTarget);
}
else
{
var anc = this.FindLogicalAncestorOfType<IControl>();
if (anc != null)
{
FocusManager.Instance?.Focus(anc);
}
}
}
}
private void ListenForNonClientClick(RawInputEventArgs e)

15
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -848,21 +848,6 @@ namespace Avalonia.Controls.Primitives
}
}
/// <summary>
/// Sets an item container's 'selected' class or <see cref="ISelectable.IsSelected"/>.
/// </summary>
/// <param name="index">The index of the item.</param>
/// <param name="selected">Whether the item should be selected or deselected.</param>
private void MarkItemSelected(int index, bool selected)
{
var container = ItemContainerGenerator?.ContainerFromIndex(index);
if (container != null)
{
MarkContainerSelected(container, selected);
}
}
private void UpdateContainerSelection()
{
if (Presenter?.Panel is IPanel panel)

12
src/Avalonia.Controls/TreeView.cs

@ -117,10 +117,8 @@ namespace Avalonia.Controls
if (value != null)
{
if (selectedItems.Count != 1 || selectedItems[0] != value)
{
_syncingSelectedItems = true;
SelectSingleItem(value);
_syncingSelectedItems = false;
{
SelectSingleItem(value);
}
}
else if (SelectedItems.Count > 0)
@ -219,8 +217,12 @@ namespace Avalonia.Controls
private void SelectSingleItem(object item)
{
SelectedItems.Clear();
_syncingSelectedItems = true;
SelectedItems.Clear();
SelectedItems.Add(item);
_syncingSelectedItems = false;
SetAndRaise(SelectedItemProperty, ref _selectedItem, item);
}
/// <summary>

11
src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs

@ -238,7 +238,7 @@ namespace Avalonia.Headless
}
}
class HeadlessBitmapStub : IBitmapImpl, IRenderTargetBitmapImpl, IWriteableBitmapImpl
class HeadlessBitmapStub : IBitmapImpl, IDrawingContextLayerImpl, IWriteableBitmapImpl
{
public Size Size { get; }
@ -267,6 +267,13 @@ namespace Avalonia.Headless
return new HeadlessDrawingContextStub();
}
public void Blit(IDrawingContextImpl context)
{
}
public bool CanBlit => false;
public Vector Dpi { get; }
public PixelSize PixelSize { get; }
public int Version { get; set; }
@ -307,7 +314,7 @@ namespace Avalonia.Headless
}
public IRenderTargetBitmapImpl CreateLayer(Size size)
public IDrawingContextLayerImpl CreateLayer(Size size)
{
return new HeadlessBitmapStub(size, new Vector(96, 96));
}

3
src/Avalonia.OpenGL/Egl/EglContext.cs

@ -73,7 +73,8 @@ namespace Avalonia.OpenGL.Egl
var old = new RestoreContext(_egl, _disp.Handle, _lock);
var surf = surface ?? OffscreenSurface;
_egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
if (!_egl.MakeCurrent(_disp.Handle, surf.DangerousGetHandle(), surf.DangerousGetHandle(), Context))
if (!_egl.MakeCurrent(_disp.Handle, surf?.DangerousGetHandle() ?? IntPtr.Zero,
surf?.DangerousGetHandle() ?? IntPtr.Zero, Context))
throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl);
success = true;
return old;

22
src/Avalonia.OpenGL/Egl/EglDisplay.cs

@ -158,15 +158,21 @@ namespace Avalonia.OpenGL.Egl
var ctx = _egl.CreateContext(_display, _config, shareCtx?.Context ?? IntPtr.Zero, _contextAttributes);
if (ctx == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglCreateContext", _egl);
var surf = _egl.CreatePBufferSurface(_display, _config, new[]
var extensions = _egl.QueryString(Handle, EGL_EXTENSIONS);
IntPtr surf = IntPtr.Zero;
if (extensions?.Contains("EGL_KHR_surfaceless_context") != true)
{
EGL_WIDTH, 1,
EGL_HEIGHT, 1,
EGL_NONE
});
if (surf == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglCreatePBufferSurface", _egl);
var rv = new EglContext(this, _egl, shareCtx, ctx, context => new EglSurface(this, context, surf),
surf = _egl.CreatePBufferSurface(_display, _config,
new[] { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE });
if (surf == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglCreatePBufferSurface", _egl);
}
var rv = new EglContext(this, _egl, shareCtx, ctx,
context =>
surf == IntPtr.Zero ? null : new EglSurface(this, context, surf),
_version, _sampleCount, _stencilSize);
return rv;
}

13
src/Avalonia.OpenGL/GlInterface.cs

@ -117,6 +117,19 @@ namespace Avalonia.OpenGL
public delegate int GlCheckFramebufferStatus(int target);
[GlEntryPoint("glCheckFramebufferStatus")]
public GlCheckFramebufferStatus CheckFramebufferStatus { get; }
public delegate void GlBlitFramebuffer(int srcX0,
int srcY0,
int srcX1,
int srcY1,
int dstX0,
int dstY0,
int dstX1,
int dstY1,
int mask,
int filter);
[GlMinVersionEntryPoint("glBlitFramebuffer", 3, 0)]
public GlBlitFramebuffer BlitFramebuffer { get; }
public delegate void GlGenRenderbuffers(int count, int[] res);
[GlEntryPoint("glGenRenderbuffers")]

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

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

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

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

7
src/Avalonia.Themes.Default/PathIcon.xaml

@ -2,14 +2,15 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="PathIcon">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
<Setter Property="Height" Value="16" />
<Setter Property="Width" Value="16" />
<Setter Property="Height" Value="{DynamicResource IconElementThemeHeight}" />
<Setter Property="Width" Value="{DynamicResource IconElementThemeWidth}" />
<Setter Property="Template">
<ControlTemplate>
<Viewbox Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}">
<Path Fill="{TemplateBinding Foreground}"
Data="{TemplateBinding Data}" />
Data="{TemplateBinding Data}"
Stretch="Uniform" />
</Viewbox>
</ControlTemplate>
</Setter>

8
src/Avalonia.Themes.Default/ToggleSwitch.xaml

@ -87,7 +87,6 @@
Grid.Row="0"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="{DynamicResource ToggleSwitchTopHeaderMargin}"
VerticalAlignment="Top"/>
<Grid Grid.Row="1"
@ -163,6 +162,13 @@
</Setter>
</Style>
<Style Selector="ToggleSwitch /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Margin" Value="{DynamicResource ToggleSwitchTopHeaderMargin}" />
</Style>
<Style Selector="ToggleSwitch /template/ ContentPresenter#PART_ContentPresenter:empty">
<Setter Property="Margin" Value="0" />
</Style>
<!-- NormalState -->
<Style Selector="ToggleSwitch /template/ Grid#SwitchAreaGrid">
<Setter Property="Background" Value="{DynamicResource ToggleSwitchContainerBackground}"/>

2
src/Avalonia.Themes.Fluent/Accents/Base.xaml

@ -12,7 +12,7 @@
<Color x:Key="SystemColorHotlightColor">#FF0066CC</Color>
<Color x:Key="SystemColorWindowColor">#FFFFFFFF</Color>
<Color x:Key="SystemColorWindowTextColor">#FF000000</Color>
<FontFamily x:Key="ContentControlThemeFontFamily">avares://Avalonia.Themes.Fluent/Assets#Roboto</FontFamily>
<FontFamily x:Key="ContentControlThemeFontFamily">avares://Avalonia.Themes.Fluent/Assets#Inter</FontFamily>
<sys:Double x:Key="ControlContentThemeFontSize">14</sys:Double>
<SolidColorBrush x:Key="SystemControlTransparentBrush" Color="Transparent" />

2
src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml

@ -439,7 +439,7 @@
<StaticResource x:Key="CalendarViewNavigationButtonBorderBrush" ResourceKey="SystemControlTransparentBrush" />
<!--Resources for NotificationCard.xaml -->
<SolidColorBrush x:Key="NotificationCardBackgroundBrush" Color="#444444" Opacity="0.75"/>
<SolidColorBrush x:Key="NotificationCardBackgroundBrush" Color="#444444" />
<SolidColorBrush x:Key="NotificationCardProgressBackgroundBrush" Color="#9A9A9A" />
<SolidColorBrush x:Key="NotificationCardInformationBackgroundBrush" Color="#007ACC" Opacity="0.75"/>
<SolidColorBrush x:Key="NotificationCardSuccessBackgroundBrush" Color="#1F9E45" Opacity="0.75"/>

BIN
src/Avalonia.Themes.Fluent/Assets/Inter-Bold.ttf

Binary file not shown.

BIN
src/Avalonia.Themes.Fluent/Assets/Inter-ExtraLight.ttf

Binary file not shown.

BIN
src/Avalonia.Themes.Fluent/Assets/Inter-Light.ttf

Binary file not shown.

BIN
src/Avalonia.Themes.Fluent/Assets/Inter-Medium.ttf

Binary file not shown.

BIN
src/Avalonia.Themes.Fluent/Assets/Inter-Regular.ttf

Binary file not shown.

BIN
src/Avalonia.Themes.Fluent/Assets/Inter-SemiBold.ttf

Binary file not shown.

BIN
src/Avalonia.Themes.Fluent/Assets/Inter-Thin.ttf

Binary file not shown.

BIN
src/Avalonia.Themes.Fluent/Assets/Roboto-Black.ttf

Binary file not shown.

BIN
src/Avalonia.Themes.Fluent/Assets/Roboto-Bold.ttf

Binary file not shown.

BIN
src/Avalonia.Themes.Fluent/Assets/Roboto-Light.ttf

Binary file not shown.

BIN
src/Avalonia.Themes.Fluent/Assets/Roboto-Medium.ttf

Binary file not shown.

BIN
src/Avalonia.Themes.Fluent/Assets/Roboto-Regular.ttf

Binary file not shown.

BIN
src/Avalonia.Themes.Fluent/Assets/Roboto-Thin.ttf

Binary file not shown.

33
src/Avalonia.Themes.Fluent/CheckBox.xaml

@ -1,13 +1,10 @@
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.PreviewWith>
<Border Padding="20">
<CheckBox IsThreeState="True" IsChecked="True" />
<CheckBox IsThreeState="True" IsChecked="True" Content="Content" Foreground="Gold" />
</Border>
</Design.PreviewWith>
<Style Selector="CheckBox">
<Setter Property="Background" Value="{DynamicResource CheckBoxBackgroundUnchecked}" />
<Setter Property="Foreground" Value="{DynamicResource CheckBoxForegroundUnchecked}" />
<Setter Property="BorderBrush" Value="{DynamicResource CheckBoxBorderBrushUnchecked}" />
<Setter Property="Padding" Value="8,0,0,0" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
@ -21,10 +18,12 @@
<Setter Property="Template">
<ControlTemplate>
<Grid x:Name="RootGrid" ColumnDefinitions="20,*">
<Border x:Name="PART_Border" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{DynamicResource ControlCornerRadius}" />
<Border x:Name="PART_Border"
Grid.ColumnSpan="2"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{DynamicResource ControlCornerRadius}" />
<Grid VerticalAlignment="Top" Height="32">
<Border x:Name="NormalRectangle"
@ -55,11 +54,11 @@
</Style>
<!-- Unchecked Normal State -->
<Style Selector="CheckBox /template/ ContentPresenter#ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource CheckBoxForegroundUnchecked}" />
<Style Selector="CheckBox">
<Setter Property="Foreground" Value="{DynamicResource CheckBoxForegroundUnchecked}" />
</Style>
<Style Selector="CheckBox /template/ Border#PART_Border">
<Style Selector="CheckBox">
<Setter Property="Background" Value="{DynamicResource CheckBoxBackgroundUnchecked}" />
<Setter Property="BorderBrush" Value="{DynamicResource CheckBoxBorderBrushUnchecked}" />
</Style>
@ -133,11 +132,11 @@
<!-- Checked Normal State -->
<Style Selector="CheckBox:checked /template/ ContentPresenter#ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource CheckBoxForegroundChecked}" />
<Style Selector="CheckBox:checked">
<Setter Property="Foreground" Value="{DynamicResource CheckBoxForegroundChecked}" />
</Style>
<Style Selector="CheckBox:checked /template/ Border#PART_Border">
<Style Selector="CheckBox:checked">
<Setter Property="Background" Value="{DynamicResource CheckBoxBackgroundChecked}" />
<Setter Property="BorderBrush" Value="{DynamicResource CheckBoxBorderBrushChecked}" />
</Style>
@ -213,11 +212,11 @@
<!-- Indeterminate Normal State -->
<Style Selector="CheckBox:indeterminate /template/ ContentPresenter#ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource CheckBoxForegroundIndeterminate}" />
<Style Selector="CheckBox:indeterminate">
<Setter Property="Foreground" Value="{DynamicResource CheckBoxForegroundIndeterminate}" />
</Style>
<Style Selector="CheckBox:indeterminate /template/ Border#PART_Border">
<Style Selector="CheckBox:indeterminate">
<Setter Property="Background" Value="{DynamicResource CheckBoxBackgroundIndeterminate}" />
<Setter Property="BorderBrush" Value="{DynamicResource CheckBoxBorderBrushIndeterminate}" />
</Style>

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

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

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

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

8
src/Avalonia.Themes.Fluent/DataValidationErrors.xaml

@ -31,12 +31,16 @@
</Setter>
<Setter Property="ErrorTemplate">
<DataTemplate>
<Canvas Width="14" Height="14" Margin="4 0 1 0"
<Canvas Name="PART_ErrorTemplateCanvas"
Width="14" Height="14" Margin="4 0 1 0"
Background="Transparent">
<Canvas.Styles>
<Style Selector="ToolTip">
<Style Selector="Canvas#PART_ErrorTemplateCanvas ToolTip">
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlErrorTextForegroundBrush}"/>
</Style>
<Style Selector="Canvas#PART_ErrorTemplateCanvas ToolTip TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
</Canvas.Styles>
<ToolTip.Tip>
<ItemsControl Items="{Binding}"/>

85
src/Avalonia.Themes.Fluent/ListBoxItem.xaml

@ -1,9 +1,13 @@
<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">
<Design.PreviewWith>
<Border Padding="0">
<ListBox>
<ListBoxItem IsEnabled="False">Disabled</ListBoxItem>
<ListBoxItem>Test</ListBoxItem>
<ListBoxItem Background="#66000000"
Padding="20">
Test
</ListBoxItem>
<ListBoxItem>Test</ListBoxItem>
</ListBox>
</Border>
@ -17,11 +21,7 @@
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="Template">
<ControlTemplate>
<Border x:Name="LayoutRoot" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}">
<Panel>
<Rectangle x:Name="PressedBackground" IsHitTestVisible="False" />
<ContentPresenter Name="PART_ContentPresenter"
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
@ -29,69 +29,66 @@
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</Panel>
</Border>
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />
</ControlTemplate>
</Setter>
</Style>
<Style Selector="ListBoxItem /template/ ContentPresenter">
<Style Selector="ListBoxItem /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="TextBlock.FontWeight" Value="Normal" />
<Setter Property="TextBlock.FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
</Style>
<!-- Disabled State -->
<Style Selector="ListBoxItem:disabled /template/ ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlDisabledBaseMediumLowBrush}"/>
<!-- Disabled State -->
<Style Selector="ListBoxItem:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlDisabledBaseMediumLowBrush}" />
</Style>
<!-- PointerOver State -->
<Style Selector="ListBoxItem:pointerover /template/ Rectangle#PressedBackground">
<Setter Property="Fill" Value="{DynamicResource SystemControlHighlightListLowBrush}"/>
<!-- PointerOver State -->
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SystemControlHighlightListLowBrush}" />
</Style>
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}"/>
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
</Style>
<!-- Pressed State -->
<Style Selector="ListBoxItem:pressed /template/ Rectangle#PressedBackground">
<Setter Property="Fill" Value="{DynamicResource SystemControlHighlightListMediumBrush}"/>
<!-- Pressed State -->
<Style Selector="ListBoxItem:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SystemControlHighlightListMediumBrush}" />
</Style>
<Style Selector="ListBoxItem:pressed /template/ ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
</Style>
<!-- Selected State -->
<Style Selector="ListBoxItem:selected /template/ Rectangle#PressedBackground">
<Setter Property="Fill" Value="{DynamicResource SystemControlHighlightListAccentLowBrush}"/>
<!-- Selected State -->
<Style Selector="ListBoxItem:selected /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SystemControlHighlightListAccentLowBrush}" />
</Style>
<Style Selector="ListBoxItem:selected /template/ ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
</Style>
<!-- Selected Unfocused State -->
<Style Selector="ListBoxItem:selected:not(:focus) /template/ Rectangle#PressedBackground">
<Setter Property="Fill" Value="{DynamicResource SystemControlHighlightListAccentLowBrush}"/>
<!-- Selected Unfocused State -->
<Style Selector="ListBoxItem:selected:not(:focus) /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SystemControlHighlightListAccentLowBrush}" />
</Style>
<Style Selector="ListBoxItem:selected:not(:focus) /template/ ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}"/>
<Style Selector="ListBoxItem:selected:not(:focus) /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
</Style>
<!-- Selected PointerOver State -->
<Style Selector="ListBoxItem:selected:pointerover /template/ Rectangle#PressedBackground">
<Setter Property="Fill" Value="{DynamicResource SystemControlHighlightListAccentMediumBrush}"/>
<!-- Selected PointerOver State -->
<Style Selector="ListBoxItem:selected:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SystemControlHighlightListAccentMediumBrush}" />
</Style>
<Style Selector="ListBoxItem:selected:pointerover /template/ ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
</Style>
<!-- Selected Pressed State -->
<Style Selector="ListBoxItem:selected:pressed /template/ Rectangle#PressedBackground">
<Setter Property="Fill" Value="{DynamicResource SystemControlHighlightListAccentHighBrush}"/>
<!-- Selected Pressed State -->
<Style Selector="ListBoxItem:selected:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SystemControlHighlightListAccentHighBrush}" />
</Style>
<Style Selector="ListBoxItem:selected:pressed /template/ ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}"/>
<Style Selector="ListBoxItem:selected:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
</Style>
</Styles>

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

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

7
src/Avalonia.Themes.Fluent/PathIcon.xaml

@ -10,14 +10,15 @@
</Design.PreviewWith>
<Style Selector="PathIcon">
<Setter Property="Foreground" Value="{DynamicResource TextControlForeground}" />
<Setter Property="Height" Value="16" />
<Setter Property="Width" Value="16" />
<Setter Property="Height" Value="{DynamicResource IconElementThemeHeight}" />
<Setter Property="Width" Value="{DynamicResource IconElementThemeWidth}" />
<Setter Property="Template">
<ControlTemplate>
<Viewbox Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}">
<Path Fill="{TemplateBinding Foreground}"
Data="{TemplateBinding Data}" />
Data="{TemplateBinding Data}"
Stretch="Uniform" />
</Viewbox>
</ControlTemplate>
</Setter>

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">
<Setter Property="Background"
Value="Transparent" />
@ -66,4 +82,78 @@
<Setter Property="Opacity" Value="1" />
</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>

10
src/Avalonia.Themes.Fluent/ToggleSwitch.xaml

@ -31,7 +31,7 @@
Text="The previewer Shows a preview off your code, this could slow down your system"
TextWrapping="Wrap"/>
<ToggleSwitch
Content="Previewer"
Background="Green"
IsChecked="True" />
</StackPanel>
</StackPanel>
@ -53,7 +53,6 @@
Grid.Row="0"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="{DynamicResource ToggleSwitchTopHeaderMargin}"
VerticalAlignment="Top"/>
<Grid Grid.Row="1"
@ -129,6 +128,13 @@
</Setter>
</Style>
<Style Selector="ToggleSwitch /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Margin" Value="{DynamicResource ToggleSwitchTopHeaderMargin}" />
</Style>
<Style Selector="ToggleSwitch /template/ ContentPresenter#PART_ContentPresenter:empty">
<Setter Property="Margin" Value="0" />
</Style>
<!-- NormalState -->
<Style Selector="ToggleSwitch /template/ Grid#SwitchAreaGrid">
<Setter Property="Background" Value="{DynamicResource ToggleSwitchContainerBackground}"/>

61
src/Avalonia.Visuals/Animation/CompositePageTransition.cs

@ -0,0 +1,61 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Metadata;
namespace Avalonia.Animation
{
/// <summary>
/// Defines a composite page transition that can be used to combine multiple transitions.
/// </summary>
/// <remarks>
/// <para>
/// Instantiate the <see cref="CompositePageTransition" /> in XAML and initialize the
/// <see cref="Transitions" /> property in order to have many animations triggered at once.
/// For example, you can combine <see cref="CrossFade"/> and <see cref="PageSlide"/>.
/// <code>
/// <![CDATA[
/// <reactiveUi:RoutedViewHost Router="{Binding Router}">
/// <reactiveUi:RoutedViewHost.PageTransition>
/// <CompositePageTransition>
/// <PageSlide Duration="0.5" />
/// <CrossFade Duration="0.5" />
/// </CompositePageTransition>
/// </reactiveUi:RoutedViewHost.PageTransition>
/// </reactiveUi:RoutedViewHost>
/// ]]>
/// </code>
/// </para>
/// </remarks>
public class CompositePageTransition : IPageTransition
{
/// <summary>
/// Gets or sets the transitions to be executed. Can be defined from XAML.
/// </summary>
[Content]
public List<IPageTransition> PageTransitions { get; set; } = new List<IPageTransition>();
/// <summary>
/// Starts the animation.
/// </summary>
/// <param name="from">
/// The control that is being transitioned away from. May be null.
/// </param>
/// <param name="to">
/// The control that is being transitioned to. May be null.
/// </param>
/// <param name="forward">
/// Defines the direction of the transition.
/// </param>
/// <returns>
/// A <see cref="Task"/> that tracks the progress of the animation.
/// </returns>
public Task Start(Visual from, Visual to, bool forward)
{
var transitionTasks = PageTransitions
.Select(transition => transition.Start(from, to, forward))
.ToList();
return Task.WhenAll(transitionTasks);
}
}
}

29
src/Avalonia.Visuals/Animation/CrossFade.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Animation.Easings;
using Avalonia.Styling;
using Avalonia.VisualTree;
@ -74,14 +75,26 @@ namespace Avalonia.Animation
/// </summary>
public TimeSpan Duration
{
get
{
return _fadeOutAnimation.Duration;
}
set
{
_fadeOutAnimation.Duration = _fadeInAnimation.Duration = value;
}
get => _fadeOutAnimation.Duration;
set => _fadeOutAnimation.Duration = _fadeInAnimation.Duration = value;
}
/// <summary>
/// Gets or sets element entrance easing.
/// </summary>
public Easing FadeInEasing
{
get => _fadeInAnimation.Easing;
set => _fadeInAnimation.Easing = value;
}
/// <summary>
/// Gets or sets element exit easing.
/// </summary>
public Easing FadeOutEasing
{
get => _fadeOutAnimation.Easing;
set => _fadeOutAnimation.Easing = value;
}
/// <summary>

44
src/Avalonia.Visuals/Animation/PageSlide.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Animation.Easings;
using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.VisualTree;
@ -48,6 +49,16 @@ namespace Avalonia.Animation
/// Gets the duration of the animation.
/// </summary>
public SlideAxis Orientation { get; set; }
/// <summary>
/// Gets or sets element entrance easing.
/// </summary>
public Easing SlideInEasing { get; set; } = new LinearEasing();
/// <summary>
/// Gets or sets element exit easing.
/// </summary>
public Easing SlideOutEasing { get; set; } = new LinearEasing();
/// <summary>
/// Starts the animation.
@ -75,18 +86,12 @@ namespace Avalonia.Animation
{
var animation = new Animation
{
Children =
Easing = SlideOutEasing,
Children =
{
new KeyFrame
{
Setters =
{
new Setter
{
Property = translateProperty,
Value = 0d
}
},
Setters = { new Setter { Property = translateProperty, Value = 0d } },
Cue = new Cue(0d)
},
new KeyFrame
@ -100,10 +105,10 @@ namespace Avalonia.Animation
}
},
Cue = new Cue(1d)
}
}
}
},
Duration = Duration
};
animation.Duration = Duration;
tasks.Add(animation.RunAsync(from));
}
@ -112,9 +117,9 @@ namespace Avalonia.Animation
to.IsVisible = true;
var animation = new Animation
{
Easing = SlideInEasing,
Children =
{
new KeyFrame
{
Setters =
@ -129,19 +134,12 @@ namespace Avalonia.Animation
},
new KeyFrame
{
Setters =
{
new Setter
{
Property = translateProperty,
Value = 0d
}
},
Setters = { new Setter { Property = translateProperty, Value = 0d } },
Cue = new Cue(1d)
}
}
},
Duration = Duration
};
animation.Duration = Duration;
tasks.Add(animation.RunAsync(to));
}

6
src/Avalonia.Visuals/ApiCompatBaseline.txt

@ -24,10 +24,14 @@ CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.Tex
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 Avalonia.Platform.IDrawingContextLayerImpl Avalonia.Platform.IDrawingContextImpl.CreateLayer(Avalonia.Size)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IRenderTargetBitmapImpl Avalonia.Platform.IDrawingContextImpl.CreateLayer(Avalonia.Size)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public Avalonia.Platform.IRenderTargetBitmapImpl Avalonia.Platform.IDrawingContextImpl.CreateLayer(Avalonia.Size)' does not exist in the implementation but it does exist 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: 31
MembersMustExist : Member 'public Avalonia.Utilities.IRef<Avalonia.Platform.IRenderTargetBitmapImpl> Avalonia.Rendering.RenderLayer.Bitmap.get()' does not exist in the implementation but it does exist in the contract.
Total Issues: 35

17
src/Avalonia.Visuals/Media/GlyphRun.cs

@ -18,7 +18,7 @@ namespace Avalonia.Media
private double _fontRenderingEmSize;
private Size? _size;
private int _biDiLevel;
private Point? _baselineOrigin;
private Point _baselineOrigin;
private ReadOnlySlice<ushort> _glyphIndices;
private ReadOnlySlice<double> _glyphAdvances;
@ -97,9 +97,7 @@ namespace Avalonia.Media
{
get
{
_baselineOrigin ??= CalculateBaselineOrigin();
return _baselineOrigin.Value;
return _baselineOrigin;
}
set => Set(ref _baselineOrigin, value);
}
@ -540,15 +538,6 @@ namespace Avalonia.Media
return GlyphAdvances[index];
}
/// <summary>
/// Calculates the default baseline origin of the <see cref="GlyphRun"/>.
/// </summary>
/// <returns>The baseline origin.</returns>
private Point CalculateBaselineOrigin()
{
return new Point(0, -GlyphTypeface.Ascent * Scale);
}
/// <summary>
/// Calculates the size of the <see cref="GlyphRun"/>.
/// </summary>
@ -611,8 +600,6 @@ namespace Avalonia.Media
throw new InvalidOperationException();
}
_baselineOrigin = new Point(0, -GlyphTypeface.Ascent * Scale);
var platformRenderInterface = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
_glyphRunImpl = platformRenderInterface.CreateGlyphRun(this, out var width);

2
src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs

@ -52,7 +52,7 @@ namespace Avalonia.Media.TextFormatting
return;
}
if (Properties.Typeface == null)
if (Properties.Typeface == default)
{
return;
}

67
src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs

@ -43,18 +43,23 @@ namespace Avalonia.Media.TextFormatting
}
/// <summary>
/// Measures the number of characters that fits into available width.
/// Measures the number of characters that fit into available width.
/// </summary>
/// <param name="textCharacters">The text run.</param>
/// <param name="availableWidth">The available width.</param>
/// <returns></returns>
internal static int MeasureCharacters(ShapedTextCharacters textCharacters, double availableWidth)
/// <param name="count">The count of fitting characters.</param>
/// <returns>
/// <c>true</c> if characters fit into the available width; otherwise, <c>false</c>.
/// </returns>
internal static bool TryMeasureCharacters(ShapedTextCharacters textCharacters, double availableWidth, out int count)
{
var glyphRun = textCharacters.GlyphRun;
if (glyphRun.Size.Width < availableWidth)
{
return glyphRun.Characters.Length;
count = glyphRun.Characters.Length;
return true;
}
var glyphCount = 0;
@ -96,21 +101,34 @@ namespace Avalonia.Media.TextFormatting
}
}
if (glyphCount == 0)
{
count = 0;
return false;
}
if (glyphCount == glyphRun.GlyphIndices.Length)
{
return glyphRun.Characters.Length;
count = glyphRun.Characters.Length;
return true;
}
if (glyphRun.GlyphClusters.IsEmpty)
{
return glyphCount;
count = glyphCount;
return true;
}
var firstCluster = glyphRun.GlyphClusters[0];
var lastCluster = glyphRun.GlyphClusters[glyphCount];
return lastCluster - firstCluster;
count = lastCluster - firstCluster;
return count > 0;
}
/// <summary>
@ -350,29 +368,38 @@ namespace Avalonia.Media.TextFormatting
if (currentWidth + currentRun.Size.Width > availableWidth)
{
var measuredLength = MeasureCharacters(currentRun, paragraphWidth - currentWidth);
var breakFound = false;
var currentBreakPosition = 0;
if (measuredLength < currentRun.Text.Length)
if (TryMeasureCharacters(currentRun, paragraphWidth - currentWidth, out var measuredLength))
{
var lineBreaker = new LineBreakEnumerator(currentRun.Text);
while (currentBreakPosition < measuredLength && lineBreaker.MoveNext())
if (measuredLength < currentRun.Text.Length)
{
var nextBreakPosition = lineBreaker.Current.PositionWrap;
var lineBreaker = new LineBreakEnumerator(currentRun.Text);
if (nextBreakPosition == 0 || nextBreakPosition > measuredLength)
while (currentBreakPosition < measuredLength && lineBreaker.MoveNext())
{
break;
}
var nextBreakPosition = lineBreaker.Current.PositionWrap;
if (nextBreakPosition == 0 || nextBreakPosition > measuredLength)
{
break;
}
breakFound = lineBreaker.Current.Required ||
lineBreaker.Current.PositionWrap != currentRun.Text.Length;
breakFound = lineBreaker.Current.Required ||
lineBreaker.Current.PositionWrap != currentRun.Text.Length;
currentBreakPosition = nextBreakPosition;
currentBreakPosition = nextBreakPosition;
}
}
}
else
{
// Make sure we wrap at least one character.
if (currentLength == 0)
{
measuredLength = 1;
}
}

44
src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs

@ -39,7 +39,9 @@ namespace Avalonia.Media.TextFormatting
foreach (var textRun in _textRuns)
{
using (drawingContext.PushPostTransform(Matrix.CreateTranslation(currentX, 0)))
var offsetY = LineMetrics.TextBaseline;
using (drawingContext.PushPostTransform(Matrix.CreateTranslation(currentX, offsetY)))
{
textRun.Draw(drawingContext);
}
@ -75,37 +77,35 @@ namespace Avalonia.Media.TextFormatting
if (currentWidth > availableWidth)
{
var measuredLength = TextFormatterImpl.MeasureCharacters(currentRun, availableWidth);
var currentBreakPosition = 0;
if (measuredLength < textRange.End)
if (TextFormatterImpl.TryMeasureCharacters(currentRun, availableWidth, out var measuredLength))
{
var lineBreaker = new LineBreakEnumerator(currentRun.Text);
while (currentBreakPosition < measuredLength && lineBreaker.MoveNext())
if (collapsingProperties.Style == TextCollapsingStyle.TrailingWord && measuredLength < textRange.End)
{
var nextBreakPosition = lineBreaker.Current.PositionWrap;
var currentBreakPosition = 0;
if (nextBreakPosition == 0)
{
break;
}
var lineBreaker = new LineBreakEnumerator(currentRun.Text);
if (nextBreakPosition > measuredLength)
while (currentBreakPosition < measuredLength && lineBreaker.MoveNext())
{
break;
var nextBreakPosition = lineBreaker.Current.PositionWrap;
if (nextBreakPosition == 0)
{
break;
}
if (nextBreakPosition > measuredLength)
{
break;
}
currentBreakPosition = nextBreakPosition;
}
currentBreakPosition = nextBreakPosition;
measuredLength = currentBreakPosition;
}
}
if (collapsingProperties.Style == TextCollapsingStyle.TrailingWord)
{
measuredLength = currentBreakPosition;
}
collapsedLength += measuredLength;
var splitResult = TextFormatterImpl.SplitTextRuns(_textRuns, collapsedLength);

16
src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs

@ -98,7 +98,7 @@ namespace Avalonia.Platform
/// has to do a format conversion each time a standard render target bitmap is rendered,
/// but a layer created via this method has no such overhead.
/// </remarks>
IRenderTargetBitmapImpl CreateLayer(Size size);
IDrawingContextLayerImpl CreateLayer(Size size);
/// <summary>
/// Pushes a clip rectangle.
@ -155,4 +155,18 @@ namespace Avalonia.Platform
/// <param name="custom">Custom draw operation</param>
void Custom(ICustomDrawOperation custom);
}
public interface IDrawingContextLayerImpl : IRenderTargetBitmapImpl
{
/// <summary>
/// Does optimized blit with Src blend mode
/// </summary>
/// <param name="context"></param>
void Blit(IDrawingContextImpl context);
/// <summary>
/// Returns true if layer supports optimized blit
/// </summary>
bool CanBlit { get; }
}
}

19
src/Avalonia.Visuals/Point.cs

@ -267,5 +267,24 @@ namespace Avalonia
{
return new Point(_x, y);
}
/// <summary>
/// Deconstructs the point into its X and Y coordinates.
/// </summary>
/// <param name="x">The X coordinate.</param>
/// <param name="y">The Y coordinate.</param>
public void Deconstruct(out double x, out double y)
{
x = this._x;
y = this._y;
}
/// <summary>
/// Gets a value indicating whether the X and Y coordinates are zero.
/// </summary>
public bool IsDefault
{
get { return (_x == 0) && (_y == 0); }
}
}
}

8
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -489,6 +489,7 @@ namespace Avalonia.Rendering
var clientRect = new Rect(scene.Size);
var firstLayer = true;
foreach (var layer in scene.Layers)
{
var bitmap = Layers[layer.LayerRoot].Bitmap;
@ -501,7 +502,10 @@ namespace Avalonia.Rendering
if (layer.OpacityMask == null)
{
context.DrawBitmap(bitmap, layer.Opacity, sourceRect, clientRect);
if (firstLayer && bitmap.Item.CanBlit)
bitmap.Item.Blit(context);
else
context.DrawBitmap(bitmap, layer.Opacity, sourceRect, clientRect);
}
else
{
@ -512,6 +516,8 @@ namespace Avalonia.Rendering
{
context.PopGeometryClip();
}
firstLayer = false;
}
if (_overlay != null)

2
src/Avalonia.Visuals/Rendering/RenderLayer.cs

@ -20,7 +20,7 @@ namespace Avalonia.Rendering
IsEmpty = true;
}
public IRef<IRenderTargetBitmapImpl> Bitmap { get; private set; }
public IRef<IDrawingContextLayerImpl> Bitmap { get; private set; }
public bool IsEmpty { get; set; }
public double Scaling { get; private set; }
public Size Size { get; private set; }

2
src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

@ -218,7 +218,7 @@ namespace Avalonia.Rendering.SceneGraph
++_drawOperationindex;
}
}
public IRenderTargetBitmapImpl CreateLayer(Size size)
public IDrawingContextLayerImpl CreateLayer(Size size)
{
throw new NotSupportedException("Creating layers on a deferred drawing context not supported");
}

19
src/Avalonia.Visuals/Size.cs

@ -276,5 +276,24 @@ namespace Avalonia
{
return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", _width, _height);
}
/// <summary>
/// Deconstructs the size into its Width and Height values.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
public void Deconstruct(out double width, out double height)
{
width = this._width;
height = this._height;
}
/// <summary>
/// Gets a value indicating whether the Width and Height values are zero.
/// </summary>
public bool IsDefault
{
get { return (_width == 0) && (_height == 0); }
}
}
}

23
src/Avalonia.Visuals/Thickness.cs

@ -272,5 +272,28 @@ namespace Avalonia
{
return $"{_left},{_top},{_right},{_bottom}";
}
/// <summary>
/// Deconstructor the thickness into its left, top, right and bottom thickness values.
/// </summary>
/// <param name="left">The thickness on the left.</param>
/// <param name="top">The thickness on the top.</param>
/// <param name="right">The thickness on the right.</param>
/// <param name="bottom">The thickness on the bottom.</param>
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;
}
/// <summary>
/// Gets a value indicating whether the left, top, right and bottom thickness values are zero.
/// </summary>
public bool IsDefault
{
get { return (_left == 0) && (_top == 0) && (_right == 0) && (_bottom == 0); }
}
}
}

19
src/Avalonia.Visuals/Vector.cs

@ -333,5 +333,24 @@ namespace Avalonia
/// </summary>
public static Vector UnitY
=> new Vector(0, 1);
/// <summary>
/// Deconstructs the vector into its X and Y components.
/// </summary>
/// <param name="x">The X component.</param>
/// <param name="y">The Y component.</param>
public void Deconstruct(out double x, out double y)
{
x = this._x;
y = this._y;
}
/// <summary>
/// Gets a value indicating whether the X and Y components are zero.
/// </summary>
public bool IsDefault
{
get { return (_x == 0) && (_y == 0); }
}
}
}

12
src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs

@ -141,17 +141,7 @@ namespace Avalonia.LinuxFramebuffer.Output
_platformGl = new EglPlatformOpenGlInterface(_eglDisplay);
_eglSurface = _platformGl.CreateWindowSurface(_gbmTargetSurface);
EglContext CreateContext(EglContext share)
{
var offSurf = gbm_surface_create(device, 1, 1, GbmColorFormats.GBM_FORMAT_XRGB8888,
GbmBoFlags.GBM_BO_USE_RENDERING);
if (offSurf == null)
throw new InvalidOperationException("Unable to create 1x1 sized GBM surface");
return _eglDisplay.CreateContext(share, _platformGl.CreateWindowSurface(offSurf));
}
_deferredContext = CreateContext(null);
_deferredContext = _platformGl.PrimaryEglContext;
using (_deferredContext.MakeCurrent(_eglSurface))
{

57
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -31,6 +31,7 @@ namespace Avalonia.Skia
private bool _disposed;
private GRContext _grContext;
public GRContext GrContext => _grContext;
private ISkiaGpu _gpu;
private readonly SKPaint _strokePaint = new SKPaint();
private readonly SKPaint _fillPaint = new SKPaint();
private readonly SKPaint _boxShadowPaint = new SKPaint();
@ -70,6 +71,11 @@ namespace Avalonia.Skia
/// GPU-accelerated context (optional)
/// </summary>
public GRContext GrContext;
/// <summary>
/// Skia GPU provider context (optional)
/// </summary>
public ISkiaGpu Gpu;
}
/// <summary>
@ -84,6 +90,7 @@ namespace Avalonia.Skia
_disposables = disposables;
_canTextUseLcdRendering = !createInfo.DisableTextLcdRendering;
_grContext = createInfo.GrContext;
_gpu = createInfo.Gpu;
if (_grContext != null)
Monitor.Enter(_grContext);
Surface = createInfo.Surface;
@ -420,9 +427,9 @@ namespace Avalonia.Skia
}
/// <inheritdoc />
public IRenderTargetBitmapImpl CreateLayer(Size size)
public IDrawingContextLayerImpl CreateLayer(Size size)
{
return CreateRenderTarget(size);
return CreateRenderTarget( size);
}
/// <inheritdoc />
@ -588,14 +595,45 @@ namespace Avalonia.Skia
var center = radialGradient.Center.ToPixels(targetSize).ToSKPoint();
var radius = (float)(radialGradient.Radius * targetSize.Width);
// TODO: There is no SetAlpha in SkiaSharp
//paint.setAlpha(128);
var origin = radialGradient.GradientOrigin.ToPixels(targetSize).ToSKPoint();
// would be nice to cache these shaders possibly?
using (var shader =
SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode))
if (origin.Equals(center))
{
paintWrapper.Paint.Shader = shader;
// when the origin is the same as the center the Skia RadialGradient acts the same as D2D
using (var shader =
SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode))
{
paintWrapper.Paint.Shader = shader;
}
}
else
{
// when the origin is different to the center use a two point ConicalGradient to match the behaviour of D2D
// reverse the order of the stops to match D2D
var reversedColors = new SKColor[stopColors.Length];
Array.Copy(stopColors, reversedColors, stopColors.Length);
Array.Reverse(reversedColors);
// and then reverse the reference point of the stops
var reversedStops = new float[stopOffsets.Length];
for (var i = 0; i < stopOffsets.Length; i++)
{
reversedStops[i] = stopOffsets[i];
if (reversedStops[i] > 0 && reversedStops[i] < 1)
{
reversedStops[i] = Math.Abs(1 - stopOffsets[i]);
}
}
// compose with a background colour of the final stop to match D2D's behaviour of filling with the final color
using (var shader = SKShader.CreateCompose(
SKShader.CreateColor(reversedColors[0]),
SKShader.CreateTwoPointConicalGradient(center, radius, origin, 0, reversedColors, reversedStops, tileMode)
))
{
paintWrapper.Paint.Shader = shader;
}
}
break;
@ -930,7 +968,8 @@ namespace Avalonia.Skia
Dpi = _dpi,
Format = format,
DisableTextLcdRendering = !_canTextUseLcdRendering,
GrContext = _grContext
GrContext = _grContext,
Gpu = _gpu
};
return new SurfaceRenderTarget(createInfo);

14
src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using Avalonia.OpenGL.Imaging;
using SkiaSharp;
@ -15,6 +16,19 @@ namespace Avalonia.Skia
/// <param name="surfaces">Surfaces.</param>
/// <returns>Created render target or <see langword="null"/> if it fails.</returns>
ISkiaGpuRenderTarget TryCreateRenderTarget(IEnumerable<object> surfaces);
/// <summary>
/// Creates an offscreen render target surface
/// </summary>
/// <param name="size">size in pixels</param>
ISkiaSurface TryCreateSurface(PixelSize size);
}
public interface ISkiaSurface : IDisposable
{
SKSurface Surface { get; }
bool CanBlit { get; }
void Blit(SKCanvas canvas);
}
public interface IOpenGlAwareSkiaGpu : ISkiaGpu

134
src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs

@ -0,0 +1,134 @@
using System;
using Avalonia.OpenGL;
using SkiaSharp;
using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.Skia
{
public class FboSkiaSurface : ISkiaSurface
{
private readonly GRContext _grContext;
private readonly IGlContext _glContext;
private readonly PixelSize _pixelSize;
private int _fbo;
private int _depthStencil;
private int _texture;
private static readonly bool[] TrueFalse = new[] { true, false };
public FboSkiaSurface(GRContext grContext, IGlContext glContext, PixelSize pixelSize)
{
_grContext = grContext;
_glContext = glContext;
_pixelSize = pixelSize;
var InternalFormat = glContext.Version.Type == GlProfileType.OpenGLES ? GL_RGBA : GL_RGBA8;
var gl = glContext.GlInterface;
// Save old bindings
gl.GetIntegerv(GL_FRAMEBUFFER_BINDING, out var oldFbo);
gl.GetIntegerv(GL_RENDERBUFFER_BINDING, out var oldRenderbuffer);
gl.GetIntegerv(GL_TEXTURE_BINDING_2D, out var oldTexture);
var arr = new int[2];
// Generate FBO
gl.GenFramebuffers(1, arr);
_fbo = arr[0];
gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo);
// Create a texture to render into
gl.GenTextures(1, arr);
_texture = arr[0];
gl.BindTexture(GL_TEXTURE_2D, _texture);
gl.TexImage2D(GL_TEXTURE_2D, 0,
InternalFormat, pixelSize.Width, pixelSize.Height,
0, GL_RGBA, GL_UNSIGNED_BYTE, IntPtr.Zero);
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
var success = false;
foreach (var useStencil8 in TrueFalse)
{
gl.GenRenderbuffers(1, arr);
_depthStencil = arr[0];
gl.BindRenderbuffer(GL_RENDERBUFFER, _depthStencil);
if (useStencil8)
{
gl.RenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, pixelSize.Width, pixelSize.Height);
gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthStencil);
}
else
{
gl.RenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, pixelSize.Width, pixelSize.Height);
gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthStencil);
gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthStencil);
}
var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER);
if (status == GL_FRAMEBUFFER_COMPLETE)
{
success = true;
break;
}
else
{
gl.BindRenderbuffer(GL_RENDERBUFFER, oldRenderbuffer);
gl.DeleteRenderbuffers(1, arr);
}
}
gl.BindRenderbuffer(GL_RENDERBUFFER, oldRenderbuffer);
gl.BindTexture(GL_TEXTURE_2D, oldTexture);
gl.BindFramebuffer(GL_FRAMEBUFFER, oldFbo);
if (!success)
{
arr[0] = _fbo;
gl.DeleteFramebuffers(1, arr);
arr[0] = _texture;
gl.DeleteTextures(1, arr);
throw new OpenGlException("Unable to create FBO with stencil");
}
var target = new GRBackendRenderTarget(pixelSize.Width, pixelSize.Height, 0, 8,
new GRGlFramebufferInfo((uint)_fbo, SKColorType.Rgba8888.ToGlSizedFormat()));
Surface = SKSurface.Create(_grContext, target,
GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888);
CanBlit = gl.BlitFramebuffer != null;
}
public void Dispose()
{
using (_glContext.EnsureCurrent())
{
Surface?.Dispose();
Surface = null;
var gl = _glContext.GlInterface;
if (_fbo != 0)
{
gl.DeleteFramebuffers(1, new[] { _fbo });
gl.DeleteTextures(1, new[] { _texture });
gl.DeleteRenderbuffers(1, new[] { _depthStencil });
_fbo = _texture = _depthStencil = 0;
}
}
}
public SKSurface Surface { get; private set; }
public bool CanBlit { get; }
public void Blit(SKCanvas canvas)
{
// This should set the render target as the current FBO
// which is definitely not the best method, but it works
canvas.Clear();
canvas.Flush();
var gl = _glContext.GlInterface;
gl.GetIntegerv(GL_READ_FRAMEBUFFER_BINDING, out var oldRead);
gl.BindFramebuffer(GL_READ_FRAMEBUFFER, _fbo);
gl.BlitFramebuffer(0, 0, _pixelSize.Width, _pixelSize.Height, 0, 0, _pixelSize.Width, _pixelSize.Height,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
gl.BindFramebuffer(GL_READ_FRAMEBUFFER, oldRead);
}
}
}

32
src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs

@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Avalonia.Logging;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Imaging;
using Avalonia.OpenGL.Surfaces;
@ -10,6 +13,7 @@ namespace Avalonia.Skia
{
private GRContext _grContext;
private IGlContext _glContext;
private bool? _canCreateSurfaces;
public GlSkiaGpu(IPlatformOpenGlInterface openGl, long? maxResourceBytes)
{
@ -43,6 +47,34 @@ namespace Avalonia.Skia
return null;
}
public ISkiaSurface TryCreateSurface(PixelSize size)
{
// Only windows platform needs our FBO trickery
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return null;
// Blit feature requires glBlitFramebuffer
if (_glContext.GlInterface.BlitFramebuffer == null)
return null;
size = new PixelSize(Math.Max(size.Width, 1), Math.Max(size.Height, 1));
if (_canCreateSurfaces == false)
return null;
try
{
var surface = new FboSkiaSurface(_grContext, _glContext, size);
_canCreateSurfaces = true;
return surface;
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error, "OpenGL")
?.Log(this, "Unable to create a Skia-compatible FBO manually");
_canCreateSurfaces ??= false;
return null;
}
}
public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi) => new GlOpenGlBitmapImpl(_glContext, size, dpi);
}
}

7
src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs

@ -8,10 +8,12 @@ namespace Avalonia.Skia
/// </summary>
internal class SkiaGpuRenderTarget : IRenderTargetWithCorruptionInfo
{
private readonly ISkiaGpu _skiaGpu;
private readonly ISkiaGpuRenderTarget _renderTarget;
public SkiaGpuRenderTarget(ISkiaGpuRenderTarget renderTarget)
public SkiaGpuRenderTarget(ISkiaGpu skiaGpu, ISkiaGpuRenderTarget renderTarget)
{
_skiaGpu = skiaGpu;
_renderTarget = renderTarget;
}
@ -30,7 +32,8 @@ namespace Avalonia.Skia
Surface = session.SkSurface,
Dpi = SkiaPlatform.DefaultDpi * session.ScaleFactor,
VisualBrushRenderer = visualBrushRenderer,
DisableTextLcdRendering = true
DisableTextLcdRendering = true,
Gpu = _skiaGpu
};
return new DrawingContextImpl(nfo, session);

2
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@ -138,7 +138,7 @@ namespace Avalonia.Skia
var gpuRenderTarget = _skiaGpu?.TryCreateRenderTarget(surfaces);
if (gpuRenderTarget != null)
{
return new SkiaGpuRenderTarget(gpuRenderTarget);
return new SkiaGpuRenderTarget(_skiaGpu, gpuRenderTarget);
}
foreach (var surface in surfaces)

72
src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs

@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Reactive.Disposables;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Skia.Helpers;
@ -11,12 +12,31 @@ namespace Avalonia.Skia
/// <summary>
/// Skia render target that writes to a surface.
/// </summary>
internal class SurfaceRenderTarget : IRenderTargetBitmapImpl, IDrawableBitmapImpl
internal class SurfaceRenderTarget : IDrawingContextLayerImpl, IDrawableBitmapImpl
{
private readonly SKSurface _surface;
private readonly ISkiaSurface _surface;
private readonly SKCanvas _canvas;
private readonly bool _disableLcdRendering;
private readonly GRContext _grContext;
private readonly ISkiaGpu _gpu;
class SkiaSurfaceWrapper : ISkiaSurface
{
public SKSurface Surface { get; private set; }
public bool CanBlit => false;
public void Blit(SKCanvas canvas) => throw new NotSupportedException();
public SkiaSurfaceWrapper(SKSurface surface)
{
Surface = surface;
}
public void Dispose()
{
Surface?.Dispose();
Surface = null;
}
}
/// <summary>
/// Create new surface render target.
@ -29,9 +49,14 @@ namespace Avalonia.Skia
_disableLcdRendering = createInfo.DisableTextLcdRendering;
_grContext = createInfo.GrContext;
_surface = CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height, createInfo.Format);
_gpu = createInfo.Gpu;
_canvas = _surface?.Canvas;
_surface = _gpu?.TryCreateSurface(PixelSize);
if (_surface == null)
_surface = new SkiaSurfaceWrapper(CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height,
createInfo.Format));
_canvas = _surface?.Surface.Canvas;
if (_surface == null || _canvas == null)
{
@ -70,11 +95,12 @@ namespace Avalonia.Skia
var createInfo = new DrawingContextImpl.CreateInfo
{
Surface = _surface,
Surface = _surface.Surface,
Dpi = Dpi,
VisualBrushRenderer = visualBrushRenderer,
DisableTextLcdRendering = _disableLcdRendering,
GrContext = _grContext
GrContext = _grContext,
Gpu = _gpu
};
return new DrawingContextImpl(createInfo, Disposable.Create(() => Version++));
@ -106,19 +132,31 @@ namespace Avalonia.Skia
}
}
/// <inheritdoc />
public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, SKPaint paint)
public void Blit(IDrawingContextImpl contextImpl)
{
if (sourceRect.Left == 0 && sourceRect.Top == 0 && sourceRect.Size == destRect.Size)
var context = (DrawingContextImpl)contextImpl;
if (_surface.CanBlit)
{
_surface.Canvas.Flush();
_surface.Draw(context.Canvas, destRect.Left, destRect.Top, paint);
_surface.Surface.Canvas.Flush();
_surface.Blit(context.Canvas);
}
else
using (var image = SnapshotImage())
{
context.Canvas.DrawImage(image, sourceRect, destRect, paint);
}
{
var oldMatrix = context.Canvas.TotalMatrix;
context.Canvas.ResetMatrix();
_surface.Surface.Draw(context.Canvas, 0, 0, null);
context.Canvas.SetMatrix(oldMatrix);
}
}
public bool CanBlit => true;
/// <inheritdoc />
public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, SKPaint paint)
{
using var image = SnapshotImage();
context.Canvas.DrawImage(image, sourceRect, destRect, paint);
}
/// <summary>
@ -127,7 +165,7 @@ namespace Avalonia.Skia
/// <returns>Image snapshot.</returns>
public SKImage SnapshotImage()
{
return _surface.Snapshot();
return _surface.Surface.Snapshot();
}
/// <summary>
@ -178,6 +216,8 @@ namespace Avalonia.Skia
/// GPU-accelerated context (optional)
/// </summary>
public GRContext GrContext;
public ISkiaGpu Gpu;
}
}
}

2
src/Windows/Avalonia.Direct2D1/ExternalRenderTarget.cs

@ -38,7 +38,7 @@ namespace Avalonia.Direct2D1
});
}
public IRenderTargetBitmapImpl CreateLayer(Size size)
public IDrawingContextLayerImpl CreateLayer(Size size)
{
var renderTarget = _externalRenderTargetProvider.GetOrCreateRenderTarget();
return D2DRenderTargetBitmapImpl.CreateCompatible(renderTarget, size);

2
src/Windows/Avalonia.Direct2D1/ILayerFactory.cs

@ -4,6 +4,6 @@ namespace Avalonia.Direct2D1
{
public interface ILayerFactory
{
IRenderTargetBitmapImpl CreateLayer(Size size);
IDrawingContextLayerImpl CreateLayer(Size size);
}
}

4
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@ -335,7 +335,7 @@ namespace Avalonia.Direct2D1.Media
}
}
public IRenderTargetBitmapImpl CreateLayer(Size size)
public IDrawingContextLayerImpl CreateLayer(Size size)
{
if (_layerFactory != null)
{
@ -346,7 +346,7 @@ namespace Avalonia.Direct2D1.Media
var platform = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
var dpi = new Vector(_deviceContext.DotsPerInch.Width, _deviceContext.DotsPerInch.Height);
var pixelSize = PixelSize.FromSizeWithDpi(size, dpi);
return platform.CreateRenderTargetBitmap(pixelSize, dpi);
return (IDrawingContextLayerImpl)platform.CreateRenderTargetBitmap(pixelSize, dpi);
}
}

11
src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs

@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Utilities;
@ -8,7 +9,7 @@ using D2DBitmap = SharpDX.Direct2D1.Bitmap;
namespace Avalonia.Direct2D1.Media.Imaging
{
public class D2DRenderTargetBitmapImpl : D2DBitmapImpl, IRenderTargetBitmapImpl, ILayerFactory
public class D2DRenderTargetBitmapImpl : D2DBitmapImpl, IDrawingContextLayerImpl, ILayerFactory
{
private readonly BitmapRenderTarget _renderTarget;
@ -34,7 +35,11 @@ namespace Avalonia.Direct2D1.Media.Imaging
return new DrawingContextImpl(visualBrushRenderer, this, _renderTarget, null, () => Version++);
}
public IRenderTargetBitmapImpl CreateLayer(Size size)
public void Blit(IDrawingContextImpl context) => throw new NotSupportedException();
public bool CanBlit => false;
public IDrawingContextLayerImpl CreateLayer(Size size)
{
return CreateCompatible(_renderTarget, size);
}

5
src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs

@ -5,7 +5,7 @@ using SharpDX.Direct2D1;
namespace Avalonia.Direct2D1.Media
{
public class WicRenderTargetBitmapImpl : WicBitmapImpl, IRenderTargetBitmapImpl
public class WicRenderTargetBitmapImpl : WicBitmapImpl, IDrawingContextLayerImpl
{
private readonly WicRenderTarget _renderTarget;
@ -45,5 +45,8 @@ namespace Avalonia.Direct2D1.Media
finishedCallback?.Invoke();
});
}
public void Blit(IDrawingContextImpl context) => throw new NotSupportedException();
public bool CanBlit => false;
}
}

2
src/Windows/Avalonia.Direct2D1/RenderTarget.cs

@ -30,7 +30,7 @@ namespace Avalonia.Direct2D1
return new DrawingContextImpl(visualBrushRenderer, this, _renderTarget);
}
public IRenderTargetBitmapImpl CreateLayer(Size size)
public IDrawingContextLayerImpl CreateLayer(Size size)
{
return D2DRenderTargetBitmapImpl.CreateCompatible(_renderTarget, size);
}

2
src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs

@ -35,7 +35,7 @@ namespace Avalonia.Direct2D1
return new DrawingContextImpl(visualBrushRenderer, this, _deviceContext, _swapChain);
}
public IRenderTargetBitmapImpl CreateLayer(Size size)
public IDrawingContextLayerImpl CreateLayer(Size size)
{
if (_deviceContext == null)
{

92
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -1011,6 +1011,10 @@ namespace Avalonia.Win32.Interop
[DllImport("user32.dll")]
public static extern bool InvalidateRect(IntPtr hWnd, RECT* lpRect, bool bErase);
[DllImport("user32.dll")]
public static extern bool ValidateRect(IntPtr hWnd, IntPtr lpRect);
[DllImport("user32.dll")]
public static extern bool IsWindowEnabled(IntPtr hWnd);
@ -1292,6 +1296,41 @@ namespace Avalonia.Win32.Interop
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hObject);
[DllImport("gdi32.dll")]
public static extern int ChoosePixelFormat(IntPtr hdc, ref PixelFormatDescriptor pfd);
[DllImport("gdi32.dll")]
public static extern int DescribePixelFormat(IntPtr hdc, ref PixelFormatDescriptor pfd);
[DllImport("gdi32.dll")]
public static extern int SetPixelFormat(IntPtr hdc, int iPixelFormat, ref PixelFormatDescriptor pfd);
[DllImport("gdi32.dll")]
public static extern int DescribePixelFormat(IntPtr hdc, int iPixelFormat, int bytes, ref PixelFormatDescriptor pfd);
[DllImport("gdi32.dll")]
public static extern bool SwapBuffers(IntPtr hdc);
[DllImport("opengl32.dll")]
public static extern IntPtr wglCreateContext(IntPtr hdc);
[DllImport("opengl32.dll")]
public static extern bool wglDeleteContext(IntPtr context);
[DllImport("opengl32.dll")]
public static extern bool wglMakeCurrent(IntPtr hdc, IntPtr context);
[DllImport("opengl32.dll")]
public static extern IntPtr wglGetCurrentContext();
[DllImport("opengl32.dll")]
public static extern IntPtr wglGetCurrentDC();
[DllImport("opengl32.dll", CharSet = CharSet.Ansi)]
public static extern IntPtr wglGetProcAddress(string name);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateFileMapping(IntPtr hFile,
IntPtr lpFileMappingAttributes,
@ -2121,4 +2160,57 @@ namespace Avalonia.Win32.Interop
public bool fNC;
public bool fWide;
}
[Flags]
internal enum PixelFormatDescriptorFlags : uint
{
PFD_DOUBLEBUFFER = 0x00000001,
PFD_STEREO = 0x00000002,
PFD_DRAW_TO_WINDOW = 0x00000004,
PFD_DRAW_TO_BITMAP = 0x00000008,
PFD_SUPPORT_GDI = 0x00000010,
PFD_SUPPORT_OPENGL = 0x00000020,
PFD_GENERIC_FORMAT = 0x00000040,
PFD_NEED_PALETTE = 0x00000080,
PFD_NEED_SYSTEM_PALETTE = 0x00000100,
PFD_SWAP_EXCHANGE = 0x00000200,
PFD_SWAP_COPY = 0x00000400,
PFD_SWAP_LAYER_BUFFERS = 0x00000800,
PFD_GENERIC_ACCELERATED = 0x00001000,
PFD_SUPPORT_DIRECTDRAW = 0x00002000,
PFD_DEPTH_DONTCARE = 0x20000000,
PFD_DOUBLEBUFFER_DONTCARE = 0x40000000,
PFD_STEREO_DONTCARE = 0x80000000,
}
[StructLayout(LayoutKind.Sequential)]
internal struct PixelFormatDescriptor
{
public ushort Size;
public ushort Version;
public PixelFormatDescriptorFlags Flags;
public byte PixelType;
public byte ColorBits;
public byte RedBits;
public byte RedShift;
public byte GreenBits;
public byte GreenShift;
public byte BlueBits;
public byte BlueShift;
public byte AlphaBits;
public byte AlphaShift;
public byte AccumBits;
public byte AccumRedBits;
public byte AccumGreenBits;
public byte AccumBlueBits;
public byte AccumAlphaBits;
public byte DepthBits;
public byte StencilBits;
public byte AuxBuffers;
public byte LayerType;
private byte Reserved;
public uint LayerMask;
public uint VisibleMask;
public uint DamageMask;
}
}

62
src/Windows/Avalonia.Win32/OpenGl/WglConsts.cs

@ -0,0 +1,62 @@
namespace Avalonia.Win32.OpenGl
{
internal class WglConsts
{
public const int WGL_CONTEXT_MAJOR_VERSION_ARB = 0x2091;
public const int WGL_CONTEXT_MINOR_VERSION_ARB = 0x2092;
public const int WGL_CONTEXT_LAYER_PLANE_ARB = 0x2093;
public const int WGL_CONTEXT_FLAGS_ARB = 0x2094;
public const int WGL_CONTEXT_PROFILE_MASK_ARB = 0x9126;
public const int WGL_NUMBER_PIXEL_FORMATS_ARB = 0x2000;
public const int WGL_DRAW_TO_WINDOW_ARB = 0x2001;
public const int WGL_DRAW_TO_BITMAP_ARB = 0x2002;
public const int WGL_ACCELERATION_ARB = 0x2003;
public const int WGL_NEED_PALETTE_ARB = 0x2004;
public const int WGL_NEED_SYSTEM_PALETTE_ARB = 0x2005;
public const int WGL_SWAP_LAYER_BUFFERS_ARB = 0x2006;
public const int WGL_SWAP_METHOD_ARB = 0x2007;
public const int WGL_NUMBER_OVERLAYS_ARB = 0x2008;
public const int WGL_NUMBER_UNDERLAYS_ARB = 0x2009;
public const int WGL_TRANSPARENT_ARB = 0x200A;
public const int WGL_TRANSPARENT_RED_VALUE_ARB = 0x2037;
public const int WGL_TRANSPARENT_GREEN_VALUE_ARB = 0x2038;
public const int WGL_TRANSPARENT_BLUE_VALUE_ARB = 0x2039;
public const int WGL_TRANSPARENT_ALPHA_VALUE_ARB = 0x203A;
public const int WGL_TRANSPARENT_INDEX_VALUE_ARB = 0x203B;
public const int WGL_SHARE_DEPTH_ARB = 0x200C;
public const int WGL_SHARE_STENCIL_ARB = 0x200D;
public const int WGL_SHARE_ACCUM_ARB = 0x200E;
public const int WGL_SUPPORT_GDI_ARB = 0x200F;
public const int WGL_SUPPORT_OPENGL_ARB = 0x2010;
public const int WGL_DOUBLE_BUFFER_ARB = 0x2011;
public const int WGL_STEREO_ARB = 0x2012;
public const int WGL_PIXEL_TYPE_ARB = 0x2013;
public const int WGL_COLOR_BITS_ARB = 0x2014;
public const int WGL_RED_BITS_ARB = 0x2015;
public const int WGL_RED_SHIFT_ARB = 0x2016;
public const int WGL_GREEN_BITS_ARB = 0x2017;
public const int WGL_GREEN_SHIFT_ARB = 0x2018;
public const int WGL_BLUE_BITS_ARB = 0x2019;
public const int WGL_BLUE_SHIFT_ARB = 0x201A;
public const int WGL_ALPHA_BITS_ARB = 0x201B;
public const int WGL_ALPHA_SHIFT_ARB = 0x201C;
public const int WGL_ACCUM_BITS_ARB = 0x201D;
public const int WGL_ACCUM_RED_BITS_ARB = 0x201E;
public const int WGL_ACCUM_GREEN_BITS_ARB = 0x201F;
public const int WGL_ACCUM_BLUE_BITS_ARB = 0x2020;
public const int WGL_ACCUM_ALPHA_BITS_ARB = 0x2021;
public const int WGL_DEPTH_BITS_ARB = 0x2022;
public const int WGL_STENCIL_BITS_ARB = 0x2023;
public const int WGL_AUX_BUFFERS_ARB = 0x2024;
public const int WGL_NO_ACCELERATION_ARB = 0x2025;
public const int WGL_GENERIC_ACCELERATION_ARB = 0x2026;
public const int WGL_FULL_ACCELERATION_ARB = 0x2027;
public const int WGL_SWAP_EXCHANGE_ARB = 0x2028;
public const int WGL_SWAP_COPY_ARB = 0x2029;
public const int WGL_SWAP_UNDEFINED_ARB = 0x202A;
public const int WGL_TYPE_RGBA_ARB = 0x202B;
public const int WGL_TYPE_COLORINDEX_ARB = 0x202C;
}
}

85
src/Windows/Avalonia.Win32/OpenGl/WglContext.cs

@ -0,0 +1,85 @@
using System;
using System.Reactive.Disposables;
using Avalonia.OpenGL;
using Avalonia.Win32.Interop;
using static Avalonia.Win32.Interop.UnmanagedMethods;
using static Avalonia.Win32.OpenGl.WglConsts;
namespace Avalonia.Win32.OpenGl
{
class WglContext : IGlContext
{
private object _lock = new object();
private readonly WglContext _sharedWith;
private readonly IntPtr _context;
private readonly IntPtr _hWnd;
private readonly IntPtr _dc;
private readonly int _pixelFormat;
private readonly PixelFormatDescriptor _formatDescriptor;
public IntPtr Handle => _context;
public WglContext(WglContext sharedWith, GlVersion version, IntPtr context, IntPtr hWnd, IntPtr dc, int pixelFormat,
PixelFormatDescriptor formatDescriptor)
{
Version = version;
_sharedWith = sharedWith;
_context = context;
_hWnd = hWnd;
_dc = dc;
_pixelFormat = pixelFormat;
_formatDescriptor = formatDescriptor;
StencilSize = formatDescriptor.StencilBits;
using (MakeCurrent())
GlInterface = new GlInterface(version, proc =>
{
var ext = wglGetProcAddress(proc);
if (ext != IntPtr.Zero)
return ext;
return GetProcAddress(WglDisplay.OpenGl32Handle, proc);
});
}
public void Dispose()
{
wglDeleteContext(_context);
ReleaseDC(_hWnd, _dc);
DestroyWindow(_hWnd);
}
public GlVersion Version { get; }
public GlInterface GlInterface { get; }
public int SampleCount { get; }
public int StencilSize { get; }
private bool IsCurrent => wglGetCurrentContext() == _context && wglGetCurrentDC() == _dc;
public IDisposable MakeCurrent()
{
if(IsCurrent)
return Disposable.Empty;
return new WglRestoreContext(_dc, _context, _lock);
}
public IDisposable EnsureCurrent() => MakeCurrent();
public IntPtr CreateConfiguredDeviceContext(IntPtr hWnd)
{
var dc = GetDC(hWnd);
var fmt = _formatDescriptor;
SetPixelFormat(dc, _pixelFormat, ref fmt);
return dc;
}
public IDisposable MakeCurrent(IntPtr hdc) => new WglRestoreContext(hdc, _context, _lock);
public bool IsSharedWith(IGlContext context)
{
var c = (WglContext)context;
return c == this
|| c._sharedWith == this
|| _sharedWith == context
|| _sharedWith != null && _sharedWith == c._sharedWith;
}
}
}

183
src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs

@ -0,0 +1,183 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.OpenGL;
using Avalonia.Win32.Interop;
using static Avalonia.Win32.Interop.UnmanagedMethods;
using static Avalonia.Win32.OpenGl.WglConsts;
namespace Avalonia.Win32.OpenGl
{
internal class WglDisplay
{
private static bool? _initialized;
private static ushort _windowClass;
private static readonly WndProc _wndProcDelegate = WndProc;
private static readonly DebugCallbackDelegate _debugCallback = DebugCallback;
private static IntPtr _bootstrapContext;
private static IntPtr _bootstrapWindow;
private static IntPtr _bootstrapDc;
private static PixelFormatDescriptor _defaultPfd;
private static int _defaultPixelFormat;
public static IntPtr OpenGl32Handle = LoadLibrary("opengl32");
private delegate bool WglChoosePixelFormatARBDelegate(IntPtr hdc, int[] piAttribIList, float[] pfAttribFList,
int nMaxFormats, int[] piFormats, out int nNumFormats);
private static WglChoosePixelFormatARBDelegate WglChoosePixelFormatArb;
private delegate IntPtr WglCreateContextAttribsARBDelegate(IntPtr hDC, IntPtr hShareContext, int[] attribList);
private static WglCreateContextAttribsARBDelegate WglCreateContextAttribsArb;
private delegate void GlDebugMessageCallbackDelegate(IntPtr callback, IntPtr userParam);
private static GlDebugMessageCallbackDelegate GlDebugMessageCallback;
private delegate void DebugCallbackDelegate(int source, int type, int id, int severity, int len, IntPtr message,
IntPtr userParam);
static bool Initialize()
{
if (!_initialized.HasValue)
_initialized = InitializeCore();
return _initialized.Value;
}
static bool InitializeCore()
{
var wndClassEx = new WNDCLASSEX
{
cbSize = Marshal.SizeOf<WNDCLASSEX>(),
hInstance = GetModuleHandle(null),
lpfnWndProc = _wndProcDelegate,
lpszClassName = "AvaloniaGlWindow-" + Guid.NewGuid(),
style = (int)ClassStyles.CS_OWNDC
};
_windowClass = RegisterClassEx(ref wndClassEx);
_bootstrapWindow = CreateOffscreenWindow();
_bootstrapDc = GetDC(_bootstrapWindow);
_defaultPfd = new PixelFormatDescriptor
{
Size = (ushort)Marshal.SizeOf<PixelFormatDescriptor>(),
Version = 1,
Flags = PixelFormatDescriptorFlags.PFD_DRAW_TO_WINDOW |
PixelFormatDescriptorFlags.PFD_SUPPORT_OPENGL | PixelFormatDescriptorFlags.PFD_DOUBLEBUFFER,
DepthBits = 24,
StencilBits = 8,
ColorBits = 32
};
_defaultPixelFormat = ChoosePixelFormat(_bootstrapDc, ref _defaultPfd);
SetPixelFormat(_bootstrapDc, _defaultPixelFormat, ref _defaultPfd);
_bootstrapContext = wglCreateContext(_bootstrapDc);
if (_bootstrapContext == IntPtr.Zero)
return false;
wglMakeCurrent(_bootstrapDc, _bootstrapContext);
WglCreateContextAttribsArb = Marshal.GetDelegateForFunctionPointer<WglCreateContextAttribsARBDelegate>(
wglGetProcAddress("wglCreateContextAttribsARB"));
WglChoosePixelFormatArb =
Marshal.GetDelegateForFunctionPointer<WglChoosePixelFormatARBDelegate>(
wglGetProcAddress("wglChoosePixelFormatARB"));
GlDebugMessageCallback =
Marshal.GetDelegateForFunctionPointer<GlDebugMessageCallbackDelegate>(
wglGetProcAddress("glDebugMessageCallback"));
var formats = new int[1];
WglChoosePixelFormatArb(_bootstrapDc, new int[]
{
WGL_DRAW_TO_WINDOW_ARB, 1,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_SUPPORT_OPENGL_ARB, 1,
WGL_DOUBLE_BUFFER_ARB, 1,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB, 32,
WGL_DEPTH_BITS_ARB, 0,
WGL_STENCIL_BITS_ARB, 0,
0, // End
}, null, 1, formats, out int numFormats);
if (numFormats != 0)
{
DescribePixelFormat(_bootstrapDc, formats[0], Marshal.SizeOf<PixelFormatDescriptor>(), ref _defaultPfd);
_defaultPixelFormat = formats[0];
}
wglMakeCurrent(IntPtr.Zero, IntPtr.Zero);
return true;
}
static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
return DefWindowProc(hWnd, msg, wParam, lParam);
}
private static void DebugCallback(int source, int type, int id, int severity, int len, IntPtr message, IntPtr userparam)
{
var err = Marshal.PtrToStringAnsi(message, len);
Console.Error.WriteLine(err);
}
public static WglContext CreateContext(GlVersion[] versions, IGlContext share)
{
if (!Initialize())
return null;
var shareContext = (WglContext)share;
using (new WglRestoreContext(_bootstrapDc, _bootstrapContext, null))
{
var window = CreateOffscreenWindow();
var dc = GetDC(window);
SetPixelFormat(dc, _defaultPixelFormat, ref _defaultPfd);
foreach (var version in versions)
{
if(version.Type != GlProfileType.OpenGL)
continue;
var context = WglCreateContextAttribsArb(dc, shareContext?.Handle ?? IntPtr.Zero,
new[]
{
// major
WGL_CONTEXT_MAJOR_VERSION_ARB, version.Major,
// minor
WGL_CONTEXT_MINOR_VERSION_ARB, version.Minor,
// core profile
WGL_CONTEXT_PROFILE_MASK_ARB, 1,
// debug
// WGL_CONTEXT_FLAGS_ARB, 1,
// end
0, 0
});
using(new WglRestoreContext(dc, context, null))
GlDebugMessageCallback(Marshal.GetFunctionPointerForDelegate(_debugCallback), IntPtr.Zero);
if (context != IntPtr.Zero)
return new WglContext(shareContext, version, context, window, dc,
_defaultPixelFormat, _defaultPfd);
}
ReleaseDC(window, dc);
DestroyWindow(window);
return null;
}
}
static IntPtr CreateOffscreenWindow() =>
CreateWindowEx(
0,
_windowClass,
null,
(int)WindowStyles.WS_OVERLAPPEDWINDOW,
0,
0,
640,
480,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero);
}
}

85
src/Windows/Avalonia.Win32/OpenGl/WglGlPlatformSurface.cs

@ -0,0 +1,85 @@
using System;
using System.Diagnostics;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Win32.Interop;
using static Avalonia.OpenGL.GlConsts;
using static Avalonia.Win32.Interop.UnmanagedMethods;
namespace Avalonia.Win32.OpenGl
{
class WglGlPlatformSurface: IGlPlatformSurface
{
private readonly WglContext _context;
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
public WglGlPlatformSurface(WglContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info)
{
_context = context;
_info = info;
}
public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
{
return new RenderTarget(_context, _info);
}
class RenderTarget : IGlPlatformSurfaceRenderTarget
{
private readonly WglContext _context;
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
private IntPtr _hdc;
public RenderTarget(WglContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info)
{
_context = context;
_info = info;
_hdc = context.CreateConfiguredDeviceContext(info.Handle);
}
public void Dispose()
{
UnmanagedMethods.ReleaseDC(_hdc, _info.Handle);
}
public IGlPlatformSurfaceRenderingSession BeginDraw()
{
var oldContext = _context.MakeCurrent(_hdc);
// Reset to default FBO first
_context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, 0);
return new Session(_context, _hdc, _info, oldContext);
}
class Session : IGlPlatformSurfaceRenderingSession
{
private readonly WglContext _context;
private readonly IntPtr _hdc;
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
private readonly IDisposable _clearContext;
public IGlContext Context => _context;
public Session(WglContext context, IntPtr hdc, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info,
IDisposable clearContext)
{
_context = context;
_hdc = hdc;
_info = info;
_clearContext = clearContext;
}
public void Dispose()
{
_context.GlInterface.Flush();
UnmanagedMethods.SwapBuffers(_hdc);
_clearContext.Dispose();
}
public PixelSize Size => _info.Size;
public double Scaling => _info.Scaling;
public bool IsYFlipped { get; }
}
}
}
}

39
src/Windows/Avalonia.Win32/OpenGl/WglPlatformOpenGlInterface.cs

@ -0,0 +1,39 @@
using System;
using System.Linq;
using Avalonia.Logging;
using Avalonia.OpenGL;
namespace Avalonia.Win32.OpenGl
{
class WglPlatformOpenGlInterface : IPlatformOpenGlInterface
{
public WglContext PrimaryContext { get; }
IGlContext IPlatformOpenGlInterface.PrimaryContext => PrimaryContext;
public IGlContext CreateSharedContext() => WglDisplay.CreateContext(new[] { PrimaryContext.Version }, PrimaryContext);
public bool CanShareContexts => true;
public bool CanCreateContexts => true;
public IGlContext CreateContext() => WglDisplay.CreateContext(new[] { PrimaryContext.Version }, null);
private WglPlatformOpenGlInterface(WglContext primary)
{
PrimaryContext = primary;
}
public static WglPlatformOpenGlInterface TryCreate()
{
try
{
var opts = AvaloniaLocator.Current.GetService<Win32PlatformOptions>() ?? new Win32PlatformOptions();
var primary = WglDisplay.CreateContext(opts.WglProfiles.ToArray(), null);
return new WglPlatformOpenGlInterface(primary);
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("WGL", "Unable to initialize WGL: " + e);
}
return null;
}
}
}

39
src/Windows/Avalonia.Win32/OpenGl/WglRestoreContext.cs

@ -0,0 +1,39 @@
using System;
using System.Threading;
using Avalonia.OpenGL;
using static Avalonia.Win32.Interop.UnmanagedMethods;
namespace Avalonia.Win32.OpenGl
{
internal class WglRestoreContext : IDisposable
{
private readonly object _monitor;
private readonly IntPtr _oldDc;
private readonly IntPtr _oldContext;
public WglRestoreContext(IntPtr gc, IntPtr context, object monitor, bool takeMonitor = true)
{
_monitor = monitor;
_oldDc = wglGetCurrentDC();
_oldContext = wglGetCurrentContext();
if (monitor != null && takeMonitor)
Monitor.Enter(monitor);
if (!wglMakeCurrent(gc, context))
{
if(monitor != null && takeMonitor)
Monitor.Exit(monitor);
throw new OpenGlException("Unable to make the context current");
}
}
public void Dispose()
{
if (!wglMakeCurrent(_oldDc, _oldContext))
wglMakeCurrent(IntPtr.Zero, IntPtr.Zero);
if (_monitor != null)
Monitor.Exit(_monitor);
}
}
}

18
src/Windows/Avalonia.Win32/Win32GlManager.cs

@ -1,27 +1,29 @@
using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.Win32.OpenGl;
namespace Avalonia.Win32
{
static class Win32GlManager
{
/// <summary>This property is initialized if drawing platform requests OpenGL support</summary>
public static EglPlatformOpenGlInterface EglPlatformInterface { get; private set; }
private static bool s_attemptedToInitialize;
public static void Initialize()
{
AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>().ToFunc(() =>
AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>().ToLazy<IPlatformOpenGlInterface>(() =>
{
if (!s_attemptedToInitialize)
var opts = AvaloniaLocator.Current.GetService<Win32PlatformOptions>();
if (opts?.UseWgl == true)
{
EglPlatformInterface = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay());
s_attemptedToInitialize = true;
var wgl = WglPlatformOpenGlInterface.TryCreate();
return wgl;
}
if (opts?.AllowEglInitialization == true)
return EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay());
return EglPlatformInterface;
return null;
});
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save