Browse Source

Merge branch 'master' into master

pull/4589/head
Steven Kirk 6 years ago
committed by GitHub
parent
commit
b01fd5ffec
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      nukebuild/Build.cs
  2. 5
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
  3. 58
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
  4. 1
      samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
  5. 8
      samples/RenderDemo/MainWindow.xaml
  6. 3
      samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
  7. 48
      samples/RenderDemo/ViewModels/MainWindowViewModel.cs
  8. 3
      src/Avalonia.Base/ApiCompatBaseline.txt
  9. 17
      src/Avalonia.Base/AvaloniaProperty.cs
  10. 48
      src/Avalonia.Base/AvaloniaProperty`1.cs
  11. 17
      src/Avalonia.Base/DirectPropertyBase.cs
  12. 2
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  13. 2
      src/Avalonia.Controls.DataGrid/DataGridCell.cs
  14. 2
      src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
  15. 2
      src/Avalonia.Controls.DataGrid/DataGridRow.cs
  16. 2
      src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
  17. 2
      src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs
  18. 96
      src/Avalonia.Controls/AutoCompleteBox.cs
  19. 2
      src/Avalonia.Controls/Button.cs
  20. 2
      src/Avalonia.Controls/ButtonSpinner.cs
  21. 2
      src/Avalonia.Controls/Calendar/CalendarButton.cs
  22. 2
      src/Avalonia.Controls/Calendar/CalendarDayButton.cs
  23. 2
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  24. 2
      src/Avalonia.Controls/Chrome/CaptionButtons.cs
  25. 2
      src/Avalonia.Controls/Chrome/TitleBar.cs
  26. 2
      src/Avalonia.Controls/DataValidationErrors.cs
  27. 4
      src/Avalonia.Controls/DateTimePickers/DatePicker.cs
  28. 4
      src/Avalonia.Controls/DateTimePickers/TimePicker.cs
  29. 3
      src/Avalonia.Controls/Expander.cs
  30. 9
      src/Avalonia.Controls/IconElement.cs
  31. 2
      src/Avalonia.Controls/ItemsControl.cs
  32. 2
      src/Avalonia.Controls/ListBoxItem.cs
  33. 2
      src/Avalonia.Controls/MenuItem.cs
  34. 4
      src/Avalonia.Controls/Mixins/SelectableMixin.cs
  35. 2
      src/Avalonia.Controls/NativeMenu.Export.cs
  36. 2
      src/Avalonia.Controls/NativeMenuItem.cs
  37. 2
      src/Avalonia.Controls/Notifications/NotificationCard.cs
  38. 2
      src/Avalonia.Controls/Notifications/WindowNotificationManager.cs
  39. 21
      src/Avalonia.Controls/PathIcon.cs
  40. 2
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  41. 4
      src/Avalonia.Controls/Primitives/AccessText.cs
  42. 3
      src/Avalonia.Controls/Primitives/IPopupHost.cs
  43. 2
      src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs
  44. 2
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  45. 2
      src/Avalonia.Controls/Primitives/Thumb.cs
  46. 2
      src/Avalonia.Controls/Primitives/ToggleButton.cs
  47. 2
      src/Avalonia.Controls/Primitives/Track.cs
  48. 2
      src/Avalonia.Controls/ProgressBar.cs
  49. 2
      src/Avalonia.Controls/Slider.cs
  50. 7
      src/Avalonia.Controls/SplitView.cs
  51. 2
      src/Avalonia.Controls/TabItem.cs
  52. 7
      src/Avalonia.Controls/TextBlock.cs
  53. 2
      src/Avalonia.Controls/TextBox.cs
  54. 4
      src/Avalonia.Controls/ToggleSwitch.cs
  55. 2
      src/Avalonia.Controls/ToolTip.cs
  56. 2
      src/Avalonia.Controls/TreeViewItem.cs
  57. 2
      src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
  58. 2
      src/Avalonia.Input/InputElement.cs
  59. 8
      src/Avalonia.Native/Avalonia.Native.csproj
  60. 8
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  61. 41
      src/Avalonia.Native/AvaloniaNativePlatformOpenGlInterface.cs
  62. 6
      src/Avalonia.Native/PopupImpl.cs
  63. 6
      src/Avalonia.Native/WindowImpl.cs
  64. 2
      src/Avalonia.Native/WindowImplBase.cs
  65. 1
      src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
  66. 10
      src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs
  67. 192
      src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
  68. 2
      src/Avalonia.OpenGL/Egl/EglConsts.cs
  69. 69
      src/Avalonia.OpenGL/Egl/EglContext.cs
  70. 53
      src/Avalonia.OpenGL/Egl/EglDisplay.cs
  71. 2
      src/Avalonia.OpenGL/Egl/EglErrors.cs
  72. 54
      src/Avalonia.OpenGL/Egl/EglGlPlatformSurface.cs
  73. 47
      src/Avalonia.OpenGL/Egl/EglGlPlatformSurfaceBase.cs
  74. 2
      src/Avalonia.OpenGL/Egl/EglInterface.cs
  75. 72
      src/Avalonia.OpenGL/Egl/EglPlatformOpenGlInterface.cs
  76. 11
      src/Avalonia.OpenGL/Egl/EglSurface.cs
  77. 43
      src/Avalonia.OpenGL/EglGlPlatformFeature.cs
  78. 51
      src/Avalonia.OpenGL/EglGlPlatformSurface.cs
  79. 13
      src/Avalonia.OpenGL/GlInterface.cs
  80. 2
      src/Avalonia.OpenGL/IGlContext.cs
  81. 2
      src/Avalonia.OpenGL/IOpenGlAwarePlatformRenderInterface.cs
  82. 13
      src/Avalonia.OpenGL/IPlatformOpenGlInterface.cs
  83. 8
      src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs
  84. 17
      src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs
  85. 13
      src/Avalonia.OpenGL/Imaging/IOpenGlTextureBitmapImpl.cs
  86. 34
      src/Avalonia.OpenGL/Imaging/OpenGlBitmap.cs
  87. 1
      src/Avalonia.OpenGL/OpenGlException.cs
  88. 2
      src/Avalonia.OpenGL/Surfaces/IGlPlatformSurface.cs
  89. 2
      src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderTarget.cs
  90. 2
      src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderingSession.cs
  91. 18
      src/Avalonia.Styling/Controls/Metadata/PseudoClassesAttribute.cs
  92. 3
      src/Avalonia.Themes.Default/Accents/BaseDark.xaml
  93. 3
      src/Avalonia.Themes.Default/Accents/BaseLight.xaml
  94. 3
      src/Avalonia.Themes.Default/DefaultTheme.xaml
  95. 17
      src/Avalonia.Themes.Default/PathIcon.xaml
  96. 2
      src/Avalonia.Themes.Fluent/Accents/Base.xaml
  97. 4
      src/Avalonia.Themes.Fluent/Button.xaml
  98. 3
      src/Avalonia.Themes.Fluent/FluentTheme.xaml
  99. 25
      src/Avalonia.Themes.Fluent/PathIcon.xaml
  100. 20
      src/Avalonia.Visuals/ApiCompatBaseline.txt

14
nukebuild/Build.cs

@ -138,9 +138,19 @@ partial class Build : NukeBuild
.SetWorkingDirectory(webappDir) .SetWorkingDirectory(webappDir)
.SetCommand("dist")); .SetCommand("dist"));
}); });
Target Compile => _ => _ Target CompileNative => _ => _
.DependsOn(Clean) .DependsOn(Clean)
.OnlyWhenStatic(() => EnvironmentInfo.IsOsx)
.Executes(() =>
{
var project = $"{RootDirectory}/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/";
var args = $"-project {project} -configuration {Parameters.Configuration} CONFIGURATION_BUILD_DIR={RootDirectory}/Build/Products/Release";
ProcessTasks.StartProcess("xcodebuild", args).AssertZeroExitCode();
});
Target Compile => _ => _
.DependsOn(Clean, CompileNative)
.DependsOn(CompileHtmlPreviewer) .DependsOn(CompileHtmlPreviewer)
.Executes(async () => .Executes(async () =>
{ {

5
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml

@ -51,6 +51,11 @@
Width="200" Width="200"
Margin="0,0,0,8" Margin="0,0,0,8"
FilterMode="None"/> FilterMode="None"/>
<TextBlock Text="Custom Autocomplete"/>
<AutoCompleteBox Name="CustomAutocompleteBox"
Width="200"
Margin="0,0,0,8"
FilterMode="None"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>

58
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs

@ -92,13 +92,28 @@ namespace ControlCatalog.Pages
} }
public StateData[] States { get; private set; } public StateData[] States { get; private set; }
private LinkedList<string>[] BuildAllSentences()
{
return new string[]
{
"Hello world",
"No this is Patrick",
"Never gonna give you up",
"How does one patch KDE2 under FreeBSD"
}
.Select(x => new LinkedList<string>(x.Split(' ')))
.ToArray();
}
public LinkedList<string>[] Sentences { get; private set; }
public AutoCompleteBoxPage() public AutoCompleteBoxPage()
{ {
this.InitializeComponent(); this.InitializeComponent();
States = BuildAllStates(); States = BuildAllStates();
Sentences = BuildAllSentences();
foreach (AutoCompleteBox box in GetAllAutoCompleteBox()) foreach (AutoCompleteBox box in GetAllAutoCompleteBox().Where(x => x.Name != "CustomAutocompleteBox"))
{ {
box.Items = States; box.Items = States;
} }
@ -116,6 +131,11 @@ namespace ControlCatalog.Pages
var asyncBox = this.FindControl<AutoCompleteBox>("AsyncBox"); var asyncBox = this.FindControl<AutoCompleteBox>("AsyncBox");
asyncBox.AsyncPopulator = PopulateAsync; asyncBox.AsyncPopulator = PopulateAsync;
var customAutocompleteBox = this.FindControl<AutoCompleteBox>("CustomAutocompleteBox");
customAutocompleteBox.Items = Sentences.SelectMany(x => x);
customAutocompleteBox.TextFilter = LastWordContains;
customAutocompleteBox.TextSelector = AppendWord;
} }
private IEnumerable<AutoCompleteBox> GetAllAutoCompleteBox() private IEnumerable<AutoCompleteBox> GetAllAutoCompleteBox()
{ {
@ -137,6 +157,42 @@ namespace ControlCatalog.Pages
.ToList(); .ToList();
} }
private bool LastWordContains(string searchText, string item)
{
var words = searchText.Split(' ');
var options = Sentences.Select(x => x.First).ToArray();
for (var i = 0; i < words.Length; ++i)
{
var word = words[i];
for (var j = 0; j < options.Length; ++j)
{
var option = options[j];
if (option == null)
continue;
if (i == words.Length - 1)
{
options[j] = option.Value.ToLower().Contains(word.ToLower()) ? option : null;
}
else
{
options[j] = option.Value.Equals(word, StringComparison.InvariantCultureIgnoreCase) ? option.Next : null;
}
}
}
return options.Any(x => x != null && x.Value == item);
}
private string AppendWord(string text, string item)
{
string[] parts = text.Split(' ');
if (parts.Length == 0)
return item;
parts[parts.Length - 1] = item;
return string.Join(" ", parts);
}
private void InitializeComponent() private void InitializeComponent()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);

1
samples/ControlCatalog/Pages/OpenGlPage.xaml.cs

@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Controls;
using Avalonia.Platform.Interop; using Avalonia.Platform.Interop;
using Avalonia.Threading; using Avalonia.Threading;
using static Avalonia.OpenGL.GlConsts; using static Avalonia.OpenGL.GlConsts;

8
samples/RenderDemo/MainWindow.xaml

@ -3,8 +3,8 @@
x:Class="RenderDemo.MainWindow" x:Class="RenderDemo.MainWindow"
Title="AvaloniaUI Rendering Test" Title="AvaloniaUI Rendering Test"
xmlns:pages="clr-namespace:RenderDemo.Pages" xmlns:pages="clr-namespace:RenderDemo.Pages"
Width="800" Width="{Binding Width, Mode=TwoWay}"
Height="600"> Height="{Binding Height, Mode=TwoWay}">
<DockPanel> <DockPanel>
<Menu DockPanel.Dock="Top"> <Menu DockPanel.Dock="Top">
<MenuItem Header="Rendering"> <MenuItem Header="Rendering">
@ -24,6 +24,10 @@
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Tests">
<MenuItem Header="Resize window"
Command="{Binding ResizeWindow}"/>
</MenuItem>
</Menu> </Menu>
<TabControl Classes="sidebar"> <TabControl Classes="sidebar">
<TabItem Header="Animations"> <TabItem Header="Animations">

3
samples/RenderDemo/Pages/GlyphRunPage.xaml.cs

@ -61,7 +61,6 @@ namespace RenderDemo.Pages
{ {
Foreground = Brushes.Black, Foreground = Brushes.Black,
GlyphRun = new GlyphRun(_glyphTypeface, _fontSize, _glyphIndices), GlyphRun = new GlyphRun(_glyphTypeface, _fontSize, _glyphIndices),
BaselineOrigin = new Point(0, -_glyphTypeface.Ascent * scale)
}; };
drawingGroup.Children.Add(glyphRunDrawing); drawingGroup.Children.Add(glyphRunDrawing);
@ -69,7 +68,7 @@ namespace RenderDemo.Pages
var geometryDrawing = new GeometryDrawing var geometryDrawing = new GeometryDrawing
{ {
Pen = new Pen(Brushes.Black), Pen = new Pen(Brushes.Black),
Geometry = new RectangleGeometry { Rect = glyphRunDrawing.GlyphRun.Bounds } Geometry = new RectangleGeometry { Rect = new Rect(glyphRunDrawing.GlyphRun.Size) }
}; };
drawingGroup.Children.Add(geometryDrawing); drawingGroup.Children.Add(geometryDrawing);

48
samples/RenderDemo/ViewModels/MainWindowViewModel.cs

@ -1,5 +1,6 @@
using System; using System.Reactive;
using System.Reactive; using System.Threading.Tasks;
using ReactiveUI; using ReactiveUI;
namespace RenderDemo.ViewModels namespace RenderDemo.ViewModels
@ -8,26 +9,61 @@ namespace RenderDemo.ViewModels
{ {
private bool drawDirtyRects = false; private bool drawDirtyRects = false;
private bool drawFps = true; private bool drawFps = true;
private double width = 800;
private double height = 600;
public MainWindowViewModel() public MainWindowViewModel()
{ {
ToggleDrawDirtyRects = ReactiveCommand.Create(() => DrawDirtyRects = !DrawDirtyRects); ToggleDrawDirtyRects = ReactiveCommand.Create(() => DrawDirtyRects = !DrawDirtyRects);
ToggleDrawFps = ReactiveCommand.Create(() => DrawFps = !DrawFps); ToggleDrawFps = ReactiveCommand.Create(() => DrawFps = !DrawFps);
ResizeWindow = ReactiveCommand.CreateFromTask(ResizeWindowAsync);
} }
public bool DrawDirtyRects public bool DrawDirtyRects
{ {
get { return drawDirtyRects; } get => drawDirtyRects;
set { this.RaiseAndSetIfChanged(ref drawDirtyRects, value); } set => this.RaiseAndSetIfChanged(ref drawDirtyRects, value);
} }
public bool DrawFps public bool DrawFps
{ {
get { return drawFps; } get => drawFps;
set { this.RaiseAndSetIfChanged(ref drawFps, value); } set => this.RaiseAndSetIfChanged(ref drawFps, value);
}
public double Width
{
get => width;
set => this.RaiseAndSetIfChanged(ref width, value);
}
public double Height
{
get => height;
set => this.RaiseAndSetIfChanged(ref height, value);
} }
public ReactiveCommand<Unit, bool> ToggleDrawDirtyRects { get; } public ReactiveCommand<Unit, bool> ToggleDrawDirtyRects { get; }
public ReactiveCommand<Unit, bool> ToggleDrawFps { get; } public ReactiveCommand<Unit, bool> ToggleDrawFps { get; }
public ReactiveCommand<Unit, Unit> ResizeWindow { get; }
private async Task ResizeWindowAsync()
{
for (int i = 0; i < 30; i++)
{
Width += 10;
Height += 5;
await Task.Delay(10);
}
await Task.Delay(10);
for (int i = 0; i < 30; i++)
{
Width -= 10;
Height -= 5;
await Task.Delay(10);
}
}
} }
} }

3
src/Avalonia.Base/ApiCompatBaseline.txt

@ -0,0 +1,3 @@
Compat issues with assembly Avalonia.Base:
CannotAddAbstractMembers : Member 'protected System.IObservable<Avalonia.AvaloniaPropertyChangedEventArgs> Avalonia.AvaloniaProperty.GetChanged()' is abstract in the implementation but is missing in the contract.
Total Issues: 1

17
src/Avalonia.Base/AvaloniaProperty.cs

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reactive.Subjects;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Data.Core; using Avalonia.Data.Core;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -18,7 +17,6 @@ namespace Avalonia
public static readonly object UnsetValue = new UnsetValueType(); public static readonly object UnsetValue = new UnsetValueType();
private static int s_nextId; private static int s_nextId;
private readonly Subject<AvaloniaPropertyChangedEventArgs> _changed;
private readonly PropertyMetadata _defaultMetadata; private readonly PropertyMetadata _defaultMetadata;
private readonly Dictionary<Type, PropertyMetadata> _metadata; private readonly Dictionary<Type, PropertyMetadata> _metadata;
private readonly Dictionary<Type, PropertyMetadata> _metadataCache = new Dictionary<Type, PropertyMetadata>(); private readonly Dictionary<Type, PropertyMetadata> _metadataCache = new Dictionary<Type, PropertyMetadata>();
@ -50,7 +48,6 @@ namespace Avalonia
throw new ArgumentException("'name' may not contain periods."); throw new ArgumentException("'name' may not contain periods.");
} }
_changed = new Subject<AvaloniaPropertyChangedEventArgs>();
_metadata = new Dictionary<Type, PropertyMetadata>(); _metadata = new Dictionary<Type, PropertyMetadata>();
Name = name; Name = name;
@ -77,7 +74,6 @@ namespace Avalonia
Contract.Requires<ArgumentNullException>(source != null); Contract.Requires<ArgumentNullException>(source != null);
Contract.Requires<ArgumentNullException>(ownerType != null); Contract.Requires<ArgumentNullException>(ownerType != null);
_changed = source._changed;
_metadata = new Dictionary<Type, PropertyMetadata>(); _metadata = new Dictionary<Type, PropertyMetadata>();
Name = source.Name; Name = source.Name;
@ -139,7 +135,7 @@ namespace Avalonia
/// An observable that is fired when this property changes on any /// An observable that is fired when this property changes on any
/// <see cref="AvaloniaObject"/> instance. /// <see cref="AvaloniaObject"/> instance.
/// </value> /// </value>
public IObservable<AvaloniaPropertyChangedEventArgs> Changed => _changed; public IObservable<AvaloniaPropertyChangedEventArgs> Changed => GetChanged();
/// <summary> /// <summary>
/// Gets a method that gets called before and after the property starts being notified on an /// Gets a method that gets called before and after the property starts being notified on an
@ -474,15 +470,6 @@ namespace Avalonia
public abstract void Accept<TData>(IAvaloniaPropertyVisitor<TData> vistor, ref TData data) public abstract void Accept<TData>(IAvaloniaPropertyVisitor<TData> vistor, ref TData data)
where TData : struct; where TData : struct;
/// <summary>
/// Notifies the <see cref="Changed"/> observable.
/// </summary>
/// <param name="e">The observable arguments.</param>
internal void NotifyChanged(AvaloniaPropertyChangedEventArgs e)
{
_changed.OnNext(e);
}
/// <summary> /// <summary>
/// Routes an untyped ClearValue call to a typed call. /// Routes an untyped ClearValue call to a typed call.
/// </summary> /// </summary>
@ -553,6 +540,8 @@ namespace Avalonia
_hasMetadataOverrides = true; _hasMetadataOverrides = true;
} }
protected abstract IObservable<AvaloniaPropertyChangedEventArgs> GetChanged();
private PropertyMetadata GetMetadataWithOverrides(Type type) private PropertyMetadata GetMetadataWithOverrides(Type type)
{ {
if (type is null) if (type is null)

48
src/Avalonia.Base/AvaloniaProperty`1.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Reactive.Subjects;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -10,6 +11,8 @@ namespace Avalonia
/// <typeparam name="TValue">The value type of the property.</typeparam> /// <typeparam name="TValue">The value type of the property.</typeparam>
public abstract class AvaloniaProperty<TValue> : AvaloniaProperty public abstract class AvaloniaProperty<TValue> : AvaloniaProperty
{ {
private readonly Subject<AvaloniaPropertyChangedEventArgs<TValue>> _changed;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AvaloniaProperty{TValue}"/> class. /// Initializes a new instance of the <see cref="AvaloniaProperty{TValue}"/> class.
/// </summary> /// </summary>
@ -24,22 +27,61 @@ namespace Avalonia
Action<IAvaloniaObject, bool> notifying = null) Action<IAvaloniaObject, bool> notifying = null)
: base(name, typeof(TValue), ownerType, metadata, notifying) : base(name, typeof(TValue), ownerType, metadata, notifying)
{ {
_changed = new Subject<AvaloniaPropertyChangedEventArgs<TValue>>();
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AvaloniaProperty"/> class. /// Initializes a new instance of the <see cref="AvaloniaProperty{TValue}"/> class.
/// </summary> /// </summary>
/// <param name="source">The property to copy.</param> /// <param name="source">The property to copy.</param>
/// <param name="ownerType">The new owner type.</param> /// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param> /// <param name="metadata">Optional overridden metadata.</param>
[Obsolete("Use constructor with AvaloniaProperty<TValue> instead.", true)]
protected AvaloniaProperty( protected AvaloniaProperty(
AvaloniaProperty source, AvaloniaProperty source,
Type ownerType, Type ownerType,
PropertyMetadata metadata)
: this(source as AvaloniaProperty<TValue> ?? throw new InvalidOperationException(), ownerType, metadata)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AvaloniaProperty{TValue}"/> class.
/// </summary>
/// <param name="source">The property to copy.</param>
/// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param>
protected AvaloniaProperty(
AvaloniaProperty<TValue> source,
Type ownerType,
PropertyMetadata metadata) PropertyMetadata metadata)
: base(source, ownerType, metadata) : base(source, ownerType, metadata)
{ {
_changed = source._changed;
}
/// <summary>
/// Gets an observable that is fired when this property changes on any
/// <see cref="AvaloniaObject"/> instance.
/// </summary>
/// <value>
/// An observable that is fired when this property changes on any
/// <see cref="AvaloniaObject"/> instance.
/// </value>
public new IObservable<AvaloniaPropertyChangedEventArgs<TValue>> Changed => _changed;
/// <summary>
/// Notifies the <see cref="Changed"/> observable.
/// </summary>
/// <param name="e">The observable arguments.</param>
internal void NotifyChanged(AvaloniaPropertyChangedEventArgs<TValue> e)
{
_changed.OnNext(e);
} }
protected override IObservable<AvaloniaPropertyChangedEventArgs> GetChanged() => Changed;
protected BindingValue<object> TryConvert(object value) protected BindingValue<object> TryConvert(object value)
{ {
if (value == UnsetValue) if (value == UnsetValue)

17
src/Avalonia.Base/DirectPropertyBase.cs

@ -32,15 +32,30 @@ namespace Avalonia
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AvaloniaProperty"/> class. /// Initializes a new instance of the <see cref="DirectPropertyBase{TValue}"/> class.
/// </summary> /// </summary>
/// <param name="source">The property to copy.</param> /// <param name="source">The property to copy.</param>
/// <param name="ownerType">The new owner type.</param> /// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param> /// <param name="metadata">Optional overridden metadata.</param>
[Obsolete("Use constructor with DirectPropertyBase<TValue> instead.", true)]
protected DirectPropertyBase( protected DirectPropertyBase(
AvaloniaProperty source, AvaloniaProperty source,
Type ownerType, Type ownerType,
PropertyMetadata metadata) PropertyMetadata metadata)
: this(source as DirectPropertyBase<TValue> ?? throw new InvalidOperationException(), ownerType, metadata)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DirectPropertyBase{TValue}"/> class.
/// </summary>
/// <param name="source">The property to copy.</param>
/// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param>
protected DirectPropertyBase(
DirectPropertyBase<TValue> source,
Type ownerType,
PropertyMetadata metadata)
: base(source, ownerType, metadata) : base(source, ownerType, metadata)
{ {
} }

2
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -24,12 +24,14 @@ using Avalonia.Input.Platform;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Avalonia.Controls.Utils; using Avalonia.Controls.Utils;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Controls.Metadata;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary> /// <summary>
/// Displays data in a customizable grid. /// Displays data in a customizable grid.
/// </summary> /// </summary>
[PseudoClasses(":invalid")]
public partial class DataGrid : TemplatedControl public partial class DataGrid : TemplatedControl
{ {
private const string DATAGRID_elementRowsPresenterName = "PART_RowsPresenter"; private const string DATAGRID_elementRowsPresenterName = "PART_RowsPresenter";

2
src/Avalonia.Controls.DataGrid/DataGridCell.cs

@ -3,6 +3,7 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
using Avalonia.Input; using Avalonia.Input;
@ -12,6 +13,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> cell. /// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> cell.
/// </summary> /// </summary>
[PseudoClasses(":selected", ":current", ":edited", ":invalid")]
public class DataGridCell : ContentControl public class DataGridCell : ContentControl
{ {
private const string DATAGRIDCELL_elementRightGridLine = "PART_RightGridLine"; private const string DATAGRIDCELL_elementRightGridLine = "PART_RightGridLine";

2
src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs

@ -14,12 +14,14 @@ using Avalonia.Utilities;
using System; using System;
using Avalonia.Controls.Utils; using Avalonia.Controls.Utils;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Metadata;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary> /// <summary>
/// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> column header. /// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> column header.
/// </summary> /// </summary>
[PseudoClasses(":dragIndicator", ":pressed", ":sortascending", ":sortdescending")]
public class DataGridColumnHeader : ContentControl public class DataGridColumnHeader : ContentControl
{ {
private enum DragMode private enum DragMode

2
src/Avalonia.Controls.DataGrid/DataGridRow.cs

@ -3,6 +3,7 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
@ -20,6 +21,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Represents a <see cref="T:Avalonia.Controls.DataGrid" /> row. /// Represents a <see cref="T:Avalonia.Controls.DataGrid" /> row.
/// </summary> /// </summary>
[PseudoClasses(":selected", ":editing", ":invalid")]
public class DataGridRow : TemplatedControl public class DataGridRow : TemplatedControl
{ {

2
src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs

@ -3,6 +3,7 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
@ -13,6 +14,7 @@ using System.Reactive.Linq;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
[PseudoClasses(":pressed", ":current", ":expanded")]
public class DataGridRowGroupHeader : TemplatedControl public class DataGridRowGroupHeader : TemplatedControl
{ {
private const string DATAGRIDROWGROUPHEADER_expanderButton = "ExpanderButton"; private const string DATAGRIDROWGROUPHEADER_expanderButton = "ExpanderButton";

2
src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs

@ -3,6 +3,7 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
using Avalonia.Controls.Metadata;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Media; using Avalonia.Media;
using System.Diagnostics; using System.Diagnostics;
@ -12,6 +13,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> row header. /// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> row header.
/// </summary> /// </summary>
[PseudoClasses(":invalid", ":selected", ":editing", ":current")]
public class DataGridRowHeader : ContentControl public class DataGridRowHeader : ContentControl
{ {
private const string DATAGRIDROWHEADER_elementRootName = "PART_Root"; private const string DATAGRIDROWHEADER_elementRootName = "PART_Root";

96
src/Avalonia.Controls/AutoCompleteBox.cs

@ -14,6 +14,7 @@ using System.Reactive.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Controls.Utils; using Avalonia.Controls.Utils;
@ -30,6 +31,7 @@ namespace Avalonia.Controls
/// <see cref="E:Avalonia.Controls.AutoCompleteBox.Populated" /> /// <see cref="E:Avalonia.Controls.AutoCompleteBox.Populated" />
/// event. /// event.
/// </summary> /// </summary>
[PseudoClasses(":dropdownopen")]
public class PopulatedEventArgs : EventArgs public class PopulatedEventArgs : EventArgs
{ {
/// <summary> /// <summary>
@ -225,6 +227,27 @@ namespace Avalonia.Controls
Custom = 13, Custom = 13,
} }
/// <summary>
/// Represents the selector used by the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control to
/// determine how the specified text should be modified with an item.
/// </summary>
/// <returns>
/// Modified text that will be used by the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" />.
/// </returns>
/// <param name="search">The string used as the basis for filtering.</param>
/// <param name="item">
/// The selected item that should be combined with the
/// <paramref name="search" /> parameter.
/// </param>
/// <typeparam name="T">
/// The type used for filtering the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" />.
/// This type can be either a string or an object.
/// </typeparam>
public delegate string AutoCompleteSelector<T>(string search, T item);
/// <summary> /// <summary>
/// Represents a control that provides a text box for user input and a /// Represents a control that provides a text box for user input and a
/// drop-down that contains possible matches based on the input in the text /// drop-down that contains possible matches based on the input in the text
@ -362,6 +385,9 @@ namespace Avalonia.Controls
private AutoCompleteFilterPredicate<object> _itemFilter; private AutoCompleteFilterPredicate<object> _itemFilter;
private AutoCompleteFilterPredicate<string> _textFilter = AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith); private AutoCompleteFilterPredicate<string> _textFilter = AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith);
private AutoCompleteSelector<object> _itemSelector;
private AutoCompleteSelector<string> _textSelector;
public static readonly RoutedEvent<SelectionChangedEventArgs> SelectionChangedEvent = public static readonly RoutedEvent<SelectionChangedEventArgs> SelectionChangedEvent =
RoutedEvent.Register<SelectionChangedEventArgs>(nameof(SelectionChanged), RoutingStrategies.Bubble, typeof(AutoCompleteBox)); RoutedEvent.Register<SelectionChangedEventArgs>(nameof(SelectionChanged), RoutingStrategies.Bubble, typeof(AutoCompleteBox));
@ -528,6 +554,34 @@ namespace Avalonia.Controls
(o, v) => o.TextFilter = v, (o, v) => o.TextFilter = v,
unsetValue: AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith)); unsetValue: AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith));
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemSelector" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemSelector" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<object>> ItemSelectorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<object>>(
nameof(ItemSelector),
o => o.ItemSelector,
(o, v) => o.ItemSelector = v);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextSelector" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextSelector" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<string>> TextSelectorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<string>>(
nameof(TextSelector),
o => o.TextSelector,
(o, v) => o.TextSelector = v);
/// <summary> /// <summary>
/// Identifies the /// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
@ -1061,6 +1115,40 @@ namespace Avalonia.Controls
set { SetAndRaise(TextFilterProperty, ref _textFilter, value); } set { SetAndRaise(TextFilterProperty, ref _textFilter, value); }
} }
/// <summary>
/// Gets or sets the custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />.
/// </summary>
/// <value>
/// The custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />.
/// </value>
public AutoCompleteSelector<object> ItemSelector
{
get { return _itemSelector; }
set { SetAndRaise(ItemSelectorProperty, ref _itemSelector, value); }
}
/// <summary>
/// Gets or sets the custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// in a text-based way.
/// </summary>
/// <value>
/// The custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// in a text-based way.
/// </value>
public AutoCompleteSelector<string> TextSelector
{
get { return _textSelector; }
set { SetAndRaise(TextSelectorProperty, ref _textSelector, value); }
}
public Func<string, CancellationToken, Task<IEnumerable<object>>> AsyncPopulator public Func<string, CancellationToken, Task<IEnumerable<object>>> AsyncPopulator
{ {
get { return _asyncPopulator; } get { return _asyncPopulator; }
@ -2329,6 +2417,14 @@ namespace Avalonia.Controls
{ {
text = SearchText; text = SearchText;
} }
else if (TextSelector != null)
{
text = TextSelector(SearchText, FormatValue(newItem, true));
}
else if (ItemSelector != null)
{
text = ItemSelector(SearchText, newItem);
}
else else
{ {
text = FormatValue(newItem, true); text = FormatValue(newItem, true);

2
src/Avalonia.Controls/Button.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Controls.Metadata;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -28,6 +29,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A button control. /// A button control.
/// </summary> /// </summary>
[PseudoClasses(":pressed")]
public class Button : ContentControl public class Button : ContentControl
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/ButtonSpinner.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Input; using Avalonia.Input;
@ -15,6 +16,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Represents a spinner control that includes two Buttons. /// Represents a spinner control that includes two Buttons.
/// </summary> /// </summary>
[PseudoClasses(":left", ":right")]
public class ButtonSpinner : Spinner public class ButtonSpinner : Spinner
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/Calendar/CalendarButton.cs

@ -3,6 +3,7 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
using Avalonia.Controls.Metadata;
using Avalonia.Input; using Avalonia.Input;
using System; using System;
@ -12,6 +13,7 @@ namespace Avalonia.Controls.Primitives
/// Represents a button on a /// Represents a button on a
/// <see cref="T:Avalonia.Controls.Calendar" />. /// <see cref="T:Avalonia.Controls.Calendar" />.
/// </summary> /// </summary>
[PseudoClasses(":selected", ":inactive", ":btnfocused")]
public sealed class CalendarButton : Button public sealed class CalendarButton : Button
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/Calendar/CalendarDayButton.cs

@ -5,10 +5,12 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Avalonia.Controls.Metadata;
using Avalonia.Input; using Avalonia.Input;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
[PseudoClasses(":pressed", ":disabled", ":selected", ":inactive", ":today", ":blackout", ":dayfocused")]
public sealed class CalendarDayButton : Button public sealed class CalendarDayButton : Button
{ {
/// <summary> /// <summary>

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

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using Avalonia.Controls.Metadata;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -18,6 +19,7 @@ namespace Avalonia.Controls.Primitives
/// Represents the currently displayed month or year on a /// Represents the currently displayed month or year on a
/// <see cref="T:Avalonia.Controls.Calendar" />. /// <see cref="T:Avalonia.Controls.Calendar" />.
/// </summary> /// </summary>
[PseudoClasses(":calendardisabled")]
public sealed class CalendarItem : TemplatedControl public sealed class CalendarItem : TemplatedControl
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/Chrome/CaptionButtons.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
#nullable enable #nullable enable
@ -9,6 +10,7 @@ namespace Avalonia.Controls.Chrome
/// <summary> /// <summary>
/// Draws window minimize / maximize / close buttons in a <see cref="TitleBar"/> when managed client decorations are enabled. /// Draws window minimize / maximize / close buttons in a <see cref="TitleBar"/> when managed client decorations are enabled.
/// </summary> /// </summary>
[PseudoClasses(":minimized", ":normal", ":maximized", ":fullscreen")]
public class CaptionButtons : TemplatedControl public class CaptionButtons : TemplatedControl
{ {
private CompositeDisposable? _disposables; private CompositeDisposable? _disposables;

2
src/Avalonia.Controls/Chrome/TitleBar.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
#nullable enable #nullable enable
@ -9,6 +10,7 @@ namespace Avalonia.Controls.Chrome
/// <summary> /// <summary>
/// Draws a titlebar when managed client decorations are enabled. /// Draws a titlebar when managed client decorations are enabled.
/// </summary> /// </summary>
[PseudoClasses(":minimized", ":normal", ":maximized", ":fullscreen")]
public class TitleBar : TemplatedControl public class TitleBar : TemplatedControl
{ {
private CompositeDisposable? _disposables; private CompositeDisposable? _disposables;

2
src/Avalonia.Controls/DataValidationErrors.cs

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Data; using Avalonia.Data;
@ -14,6 +15,7 @@ namespace Avalonia.Controls
/// <remarks> /// <remarks>
/// You will probably only want to create instances inside of control templates. /// You will probably only want to create instances inside of control templates.
/// </remarks> /// </remarks>
[PseudoClasses(":error")]
public class DataValidationErrors : ContentControl public class DataValidationErrors : ContentControl
{ {
/// <summary> /// <summary>

4
src/Avalonia.Controls/DateTimePickers/DatePicker.cs

@ -1,4 +1,5 @@
using Avalonia.Controls.Primitives; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -11,6 +12,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A control to allow the user to select a date /// A control to allow the user to select a date
/// </summary> /// </summary>
[PseudoClasses(":hasnodate")]
public class DatePicker : TemplatedControl public class DatePicker : TemplatedControl
{ {
/// <summary> /// <summary>

4
src/Avalonia.Controls/DateTimePickers/TimePicker.cs

@ -1,4 +1,5 @@
using Avalonia.Controls.Primitives; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using System; using System;
@ -9,6 +10,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A control to allow the user to select a time /// A control to allow the user to select a time
/// </summary> /// </summary>
[PseudoClasses(":hasnotime")]
public class TimePicker : TemplatedControl public class TimePicker : TemplatedControl
{ {
/// <summary> /// <summary>

3
src/Avalonia.Controls/Expander.cs

@ -1,6 +1,6 @@
using Avalonia.Animation; using Avalonia.Animation;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Data;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -12,6 +12,7 @@ namespace Avalonia.Controls
Right Right
} }
[PseudoClasses(":expanded", ":up", ":down", ":left", ":right")]
public class Expander : HeaderedContentControl public class Expander : HeaderedContentControl
{ {
public static readonly StyledProperty<IPageTransition> ContentTransitionProperty = public static readonly StyledProperty<IPageTransition> ContentTransitionProperty =

9
src/Avalonia.Controls/IconElement.cs

@ -0,0 +1,9 @@
using Avalonia.Controls.Primitives;
namespace Avalonia.Controls
{
public abstract class IconElement : TemplatedControl
{
}
}

2
src/Avalonia.Controls/ItemsControl.cs

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.Generators; using Avalonia.Controls.Generators;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
@ -18,6 +19,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Displays a collection of items. /// Displays a collection of items.
/// </summary> /// </summary>
[PseudoClasses(":empty", ":singleitem")]
public class ItemsControl : TemplatedControl, IItemsPresenterHost, ICollectionChangedListener public class ItemsControl : TemplatedControl, IItemsPresenterHost, ICollectionChangedListener
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/ListBoxItem.cs

@ -1,3 +1,4 @@
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Input; using Avalonia.Input;
@ -6,6 +7,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A selectable item in a <see cref="ListBox"/>. /// A selectable item in a <see cref="ListBox"/>.
/// </summary> /// </summary>
[PseudoClasses(":pressed", ":selected")]
public class ListBoxItem : ContentControl, ISelectable public class ListBoxItem : ContentControl, ISelectable
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/MenuItem.cs

@ -4,6 +4,7 @@ using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Controls.Generators; using Avalonia.Controls.Generators;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
@ -20,6 +21,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A menu item control. /// A menu item control.
/// </summary> /// </summary>
[PseudoClasses(":separator", ":icon", ":open", ":pressed", ":selected")]
public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable
{ {
/// <summary> /// <summary>

4
src/Avalonia.Controls/Mixins/SelectableMixin.cs

@ -48,7 +48,7 @@ namespace Avalonia.Controls.Mixins
if (sender != null) if (sender != null)
{ {
((IPseudoClasses)sender.Classes).Set(":selected", (bool)x.NewValue); ((IPseudoClasses)sender.Classes).Set(":selected", x.NewValue.GetValueOrDefault());
sender.RaiseEvent(new RoutedEventArgs sender.RaiseEvent(new RoutedEventArgs
{ {
@ -58,4 +58,4 @@ namespace Avalonia.Controls.Mixins
}); });
} }
} }
} }

2
src/Avalonia.Controls/NativeMenu.Export.cs

@ -77,7 +77,7 @@ namespace Avalonia.Controls
{ {
if (args.Sender is TopLevel tl) if (args.Sender is TopLevel tl)
{ {
GetInfo(tl).Exporter?.SetNativeMenu((NativeMenu)args.NewValue); GetInfo(tl).Exporter?.SetNativeMenu(args.NewValue.GetValueOrDefault());
} }
}); });
} }

2
src/Avalonia.Controls/NativeMenuItem.cs

@ -23,7 +23,7 @@ namespace Avalonia.Controls
MenuProperty.Changed.Subscribe(args => MenuProperty.Changed.Subscribe(args =>
{ {
var item = (NativeMenuItem)args.Sender; var item = (NativeMenuItem)args.Sender;
var value = (NativeMenu)args.NewValue; var value = args.NewValue.GetValueOrDefault();
if (value.Parent != null && value.Parent != item) if (value.Parent != null && value.Parent != item)
throw new InvalidOperationException("NativeMenu already has a parent"); throw new InvalidOperationException("NativeMenu already has a parent");
value.Parent = item; value.Parent = item;

2
src/Avalonia.Controls/Notifications/NotificationCard.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Controls.Metadata;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
@ -9,6 +10,7 @@ namespace Avalonia.Controls.Notifications
/// <summary> /// <summary>
/// Control that represents and displays a notification. /// Control that represents and displays a notification.
/// </summary> /// </summary>
[PseudoClasses(":error", ":information", ":success", ":warning")]
public class NotificationCard : ContentControl public class NotificationCard : ContentControl
{ {
private bool _isClosed; private bool _isClosed;

2
src/Avalonia.Controls/Notifications/WindowNotificationManager.cs

@ -7,12 +7,14 @@ using Avalonia.Controls.Primitives;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using Avalonia.Controls.Metadata;
namespace Avalonia.Controls.Notifications namespace Avalonia.Controls.Notifications
{ {
/// <summary> /// <summary>
/// An <see cref="INotificationManager"/> that displays notifications in a <see cref="Window"/>. /// An <see cref="INotificationManager"/> that displays notifications in a <see cref="Window"/>.
/// </summary> /// </summary>
[PseudoClasses(":topleft", ":topright", ":bottomleft", ":bottomright")]
public class WindowNotificationManager : TemplatedControl, IManagedNotificationManager, ICustomSimpleHitTest public class WindowNotificationManager : TemplatedControl, IManagedNotificationManager, ICustomSimpleHitTest
{ {
private IList _items; private IList _items;

21
src/Avalonia.Controls/PathIcon.cs

@ -0,0 +1,21 @@
using Avalonia.Media;
namespace Avalonia.Controls
{
public class PathIcon : IconElement
{
static PathIcon()
{
AffectsRender<PathIcon>(DataProperty);
}
public static readonly StyledProperty<Geometry> DataProperty =
AvaloniaProperty.Register<PathIcon, Geometry>(nameof(Data));
public Geometry Data
{
get { return GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
}
}

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

@ -82,7 +82,7 @@ namespace Avalonia.Controls.Presenters
TextAlignmentProperty, TextWrappingProperty, TextBlock.FontSizeProperty, TextAlignmentProperty, TextWrappingProperty, TextBlock.FontSizeProperty,
TextBlock.FontStyleProperty, TextBlock.FontWeightProperty, TextBlock.FontFamilyProperty); TextBlock.FontStyleProperty, TextBlock.FontWeightProperty, TextBlock.FontFamilyProperty);
Observable.Merge(TextProperty.Changed, TextBlock.ForegroundProperty.Changed, Observable.Merge<AvaloniaPropertyChangedEventArgs>(TextProperty.Changed, TextBlock.ForegroundProperty.Changed,
TextAlignmentProperty.Changed, TextWrappingProperty.Changed, TextAlignmentProperty.Changed, TextWrappingProperty.Changed,
TextBlock.FontSizeProperty.Changed, TextBlock.FontStyleProperty.Changed, TextBlock.FontSizeProperty.Changed, TextBlock.FontStyleProperty.Changed,
TextBlock.FontWeightProperty.Changed, TextBlock.FontFamilyProperty.Changed, TextBlock.FontWeightProperty.Changed, TextBlock.FontFamilyProperty.Changed,

4
src/Avalonia.Controls/Primitives/AccessText.cs

@ -126,7 +126,7 @@ namespace Avalonia.Controls.Primitives
if (shapedTextCharacters.GlyphRun.Characters.End < textPosition) if (shapedTextCharacters.GlyphRun.Characters.End < textPosition)
{ {
currentX += shapedTextCharacters.GlyphRun.Bounds.Width; currentX += shapedTextCharacters.Size.Width;
continue; continue;
} }
@ -143,7 +143,7 @@ namespace Avalonia.Controls.Primitives
width = 0.0; width = 0.0;
} }
return new Rect(currentX, currentY, width, shapedTextCharacters.GlyphRun.Bounds.Height); return new Rect(currentX, currentY, width, shapedTextCharacters.Size.Height);
} }
} }

3
src/Avalonia.Controls/Primitives/IPopupHost.cs

@ -1,6 +1,7 @@
using System; using System;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Input;
using Avalonia.VisualTree; using Avalonia.VisualTree;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
@ -13,7 +14,7 @@ namespace Avalonia.Controls.Primitives
/// (<see cref="PopupRoot"/>) or an <see cref="OverlayPopupHost"/> which is created /// (<see cref="PopupRoot"/>) or an <see cref="OverlayPopupHost"/> which is created
/// on an <see cref="OverlayLayer"/>. /// on an <see cref="OverlayLayer"/>.
/// </remarks> /// </remarks>
public interface IPopupHost : IDisposable public interface IPopupHost : IDisposable, IFocusScope
{ {
/// <summary> /// <summary>
/// Sets the control to display in the popup. /// Sets the control to display in the popup.

2
src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs

@ -221,7 +221,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
if (!FitsInBounds(unconstrainedRect, PopupAnchor.Bottom)) if (!FitsInBounds(unconstrainedRect, PopupAnchor.Bottom))
{ {
unconstrainedRect = unconstrainedRect.WithHeight(bounds.Height - unconstrainedRect.Y); unconstrainedRect = unconstrainedRect.WithHeight(bounds.Bottom - unconstrainedRect.Y);
} }
if (IsValid(unconstrainedRect)) if (IsValid(unconstrainedRect))

2
src/Avalonia.Controls/Primitives/ScrollBar.cs

@ -4,6 +4,7 @@ using Avalonia.Interactivity;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.Controls.Metadata;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
@ -21,6 +22,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// A scrollbar control. /// A scrollbar control.
/// </summary> /// </summary>
[PseudoClasses(":vertical", ":horizontal")]
public class ScrollBar : RangeBase public class ScrollBar : RangeBase
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/Primitives/Thumb.cs

@ -1,9 +1,11 @@
using System; using System;
using Avalonia.Controls.Metadata;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
[PseudoClasses(":pressed")]
public class Thumb : TemplatedControl public class Thumb : TemplatedControl
{ {
public static readonly RoutedEvent<VectorEventArgs> DragStartedEvent = public static readonly RoutedEvent<VectorEventArgs> DragStartedEvent =

2
src/Avalonia.Controls/Primitives/ToggleButton.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.Controls.Metadata;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -7,6 +8,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Represents a control that a user can select (check) or clear (uncheck). Base class for controls that can switch states. /// Represents a control that a user can select (check) or clear (uncheck). Base class for controls that can switch states.
/// </summary> /// </summary>
[PseudoClasses(":checked", ":unchecked", ":indeterminate")]
public class ToggleButton : Button public class ToggleButton : Button
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/Primitives/Track.cs

@ -4,6 +4,7 @@
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
using System; using System;
using Avalonia.Controls.Metadata;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Layout; using Avalonia.Layout;
@ -12,6 +13,7 @@ using Avalonia.Utilities;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
[PseudoClasses(":vertical", ":horizontal")]
public class Track : Control public class Track : Control
{ {
public static readonly DirectProperty<Track, double> MinimumProperty = public static readonly DirectProperty<Track, double> MinimumProperty =

2
src/Avalonia.Controls/ProgressBar.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
@ -8,6 +9,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A control used to indicate the progress of an operation. /// A control used to indicate the progress of an operation.
/// </summary> /// </summary>
[PseudoClasses(":vertical", ":horizontal", ":indeterminate")]
public class ProgressBar : RangeBase public class ProgressBar : RangeBase
{ {
public class ProgressBarTemplateProperties : AvaloniaObject public class ProgressBarTemplateProperties : AvaloniaObject

2
src/Avalonia.Controls/Slider.cs

@ -1,5 +1,6 @@
using System; using System;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
@ -39,6 +40,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A control that lets the user select from a range of values by moving a Thumb control along a Track. /// A control that lets the user select from a range of values by moving a Thumb control along a Track.
/// </summary> /// </summary>
[PseudoClasses(":vertical", ":horizontal", ":pressed")]
public class Slider : RangeBase public class Slider : RangeBase
{ {
/// <summary> /// <summary>

7
src/Avalonia.Controls/SplitView.cs

@ -1,4 +1,5 @@
using Avalonia.Controls.Primitives; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -73,6 +74,10 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A control with two views: A collapsible pane and an area for content /// A control with two views: A collapsible pane and an area for content
/// </summary> /// </summary>
[PseudoClasses(":open", ":closed")]
[PseudoClasses(":compactoverlay", ":compactinline", ":overlay", ":inline")]
[PseudoClasses(":left", ":right")]
[PseudoClasses(":lightdismiss")]
public class SplitView : TemplatedControl public class SplitView : TemplatedControl
{ {
/* /*

2
src/Avalonia.Controls/TabItem.cs

@ -1,3 +1,4 @@
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
@ -6,6 +7,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// An item in a <see cref="TabStrip"/> or <see cref="TabControl"/>. /// An item in a <see cref="TabStrip"/> or <see cref="TabControl"/>.
/// </summary> /// </summary>
[PseudoClasses(":pressed", ":selected")]
public class TabItem : HeaderedContentControl, ISelectable public class TabItem : HeaderedContentControl, ISelectable
{ {
/// <summary> /// <summary>

7
src/Avalonia.Controls/TextBlock.cs

@ -138,7 +138,7 @@ namespace Avalonia.Controls
FontStyleProperty, TextWrappingProperty, FontFamilyProperty, FontStyleProperty, TextWrappingProperty, FontFamilyProperty,
TextTrimmingProperty, TextProperty, PaddingProperty, LineHeightProperty, MaxLinesProperty); TextTrimmingProperty, TextProperty, PaddingProperty, LineHeightProperty, MaxLinesProperty);
Observable.Merge(TextProperty.Changed, ForegroundProperty.Changed, Observable.Merge<AvaloniaPropertyChangedEventArgs>(TextProperty.Changed, ForegroundProperty.Changed,
TextAlignmentProperty.Changed, TextWrappingProperty.Changed, TextAlignmentProperty.Changed, TextWrappingProperty.Changed,
TextTrimmingProperty.Changed, FontSizeProperty.Changed, TextTrimmingProperty.Changed, FontSizeProperty.Changed,
FontStyleProperty.Changed, FontWeightProperty.Changed, FontStyleProperty.Changed, FontWeightProperty.Changed,
@ -434,7 +434,10 @@ namespace Avalonia.Controls
var padding = Padding; var padding = Padding;
TextLayout.Draw(context, new Point(padding.Left + offsetX, padding.Top)); using (context.PushPostTransform(Matrix.CreateTranslation(padding.Left + offsetX, padding.Top)))
{
TextLayout.Draw(context);
}
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/TextBox.cs

@ -13,9 +13,11 @@ using Avalonia.Metadata;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.Controls.Metadata;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
[PseudoClasses(":empty")]
public class TextBox : TemplatedControl, UndoRedoHelper<TextBox.UndoRedoState>.IUndoRedoHost public class TextBox : TemplatedControl, UndoRedoHelper<TextBox.UndoRedoState>.IUndoRedoHost
{ {
public static KeyGesture CutGesture { get; } = AvaloniaLocator.Current public static KeyGesture CutGesture { get; } = AvaloniaLocator.Current

4
src/Avalonia.Controls/ToggleSwitch.cs

@ -1,4 +1,5 @@
using Avalonia.Controls.Presenters; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
@ -8,6 +9,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A Toggle Switch control. /// A Toggle Switch control.
/// </summary> /// </summary>
[PseudoClasses(":dragging")]
public class ToggleSwitch : ToggleButton public class ToggleSwitch : ToggleButton
{ {
private Panel _knobsPanel; private Panel _knobsPanel;

2
src/Avalonia.Controls/ToolTip.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.VisualTree; using Avalonia.VisualTree;
@ -14,6 +15,7 @@ namespace Avalonia.Controls
/// To add a tooltip to a control, use the <see cref="TipProperty"/> attached property, /// To add a tooltip to a control, use the <see cref="TipProperty"/> attached property,
/// assigning the content that you want displayed. /// assigning the content that you want displayed.
/// </remarks> /// </remarks>
[PseudoClasses(":open")]
public class ToolTip : ContentControl public class ToolTip : ContentControl
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/TreeViewItem.cs

@ -1,5 +1,6 @@
using System.Linq; using System.Linq;
using Avalonia.Controls.Generators; using Avalonia.Controls.Generators;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
@ -11,6 +12,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// An item in a <see cref="TreeView"/>. /// An item in a <see cref="TreeView"/>.
/// </summary> /// </summary>
[PseudoClasses(":pressed", ":selected")]
public class TreeViewItem : HeaderedItemsControl, ISelectable public class TreeViewItem : HeaderedItemsControl, ISelectable
{ {
/// <summary> /// <summary>

2
src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs

@ -385,7 +385,7 @@ namespace Avalonia.Headless
} }
public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin) public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{ {
} }

2
src/Avalonia.Input/InputElement.cs

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Input.GestureRecognizers; using Avalonia.Input.GestureRecognizers;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -12,6 +13,7 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// Implements input-related functionality for a control. /// Implements input-related functionality for a control.
/// </summary> /// </summary>
[PseudoClasses(":disabled", ":focus", ":focus-visible", ":pointerover")]
public class InputElement : Interactive, IInputElement public class InputElement : Interactive, IInputElement
{ {
/// <summary> /// <summary>

8
src/Avalonia.Native/Avalonia.Native.csproj

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<IsPackable>false</IsPackable> <PackAvaloniaNative Condition="'$(PackAvaloniaNative)' == ''">$([MSBuild]::IsOSPlatform(OSX))</PackAvaloniaNative>
<IsPackable>$(PackAvaloniaNative)</IsPackable>
<IsPackable Condition="'$([MSBuild]::IsOSPlatform(OSX))' == 'True'">true</IsPackable> <IsPackable Condition="'$([MSBuild]::IsOSPlatform(OSX))' == 'True'">true</IsPackable>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<CastXmlPath Condition="Exists('/usr/bin/castxml')">/usr/bin/castxml</CastXmlPath> <CastXmlPath Condition="Exists('/usr/bin/castxml')">/usr/bin/castxml</CastXmlPath>
@ -10,8 +11,9 @@
<SharpGenGenerateConsumerBindMapping>false</SharpGenGenerateConsumerBindMapping> <SharpGenGenerateConsumerBindMapping>false</SharpGenGenerateConsumerBindMapping>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release' AND '$([MSBuild]::IsOSPlatform(OSX))' == 'true'"> <ItemGroup Condition="'$(PackAvaloniaNative)' == 'true'">
<Content Include="../../Build/Products/Release/libAvalonia.Native.OSX.dylib"> <Content Include="../../Build/Products/Release/libAvalonia.Native.OSX.dylib">
<Link>libAvaloniaNative.dylib</Link>
<PackagePath>runtimes/osx/native/libAvaloniaNative.dylib</PackagePath> <PackagePath>runtimes/osx/native/libAvaloniaNative.dylib</PackagePath>
<Pack>true</Pack> <Pack>true</Pack>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@ -26,4 +28,4 @@
<ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" /> <ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\Avalonia.Dialogs\Avalonia.Dialogs.csproj" /> <ProjectReference Include="..\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

8
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -16,7 +16,7 @@ namespace Avalonia.Native
{ {
private readonly IAvaloniaNativeFactory _factory; private readonly IAvaloniaNativeFactory _factory;
private AvaloniaNativePlatformOptions _options; private AvaloniaNativePlatformOptions _options;
private GlPlatformFeature _glFeature; private AvaloniaNativePlatformOpenGlInterface _platformGl;
[DllImport("libAvaloniaNative")] [DllImport("libAvaloniaNative")]
static extern IntPtr CreateAvaloniaNative(); static extern IntPtr CreateAvaloniaNative();
@ -116,8 +116,8 @@ namespace Avalonia.Native
{ {
try try
{ {
AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>() AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>()
.ToConstant(_glFeature = new GlPlatformFeature(_factory.ObtainGlDisplay())); .ToConstant(_platformGl = new AvaloniaNativePlatformOpenGlInterface(_factory.ObtainGlDisplay()));
} }
catch (Exception) catch (Exception)
{ {
@ -128,7 +128,7 @@ namespace Avalonia.Native
public IWindowImpl CreateWindow() public IWindowImpl CreateWindow()
{ {
return new WindowImpl(_factory, _options, _glFeature); return new WindowImpl(_factory, _options, _platformGl);
} }
public IWindowImpl CreateEmbeddableWindow() public IWindowImpl CreateEmbeddableWindow()

41
src/Avalonia.Native/GlPlatformFeature.cs → src/Avalonia.Native/AvaloniaNativePlatformOpenGlInterface.cs

@ -2,21 +2,20 @@
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.Native.Interop; using Avalonia.Native.Interop;
using System.Drawing; using System.Drawing;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Threading; using Avalonia.Threading;
namespace Avalonia.Native namespace Avalonia.Native
{ {
class GlPlatformFeature : IWindowingPlatformGlFeature class AvaloniaNativePlatformOpenGlInterface : IPlatformOpenGlInterface
{ {
private readonly IAvnGlDisplay _display; private readonly IAvnGlDisplay _display;
public GlPlatformFeature(IAvnGlDisplay display) public AvaloniaNativePlatformOpenGlInterface(IAvnGlDisplay display)
{ {
_display = display; _display = display;
var immediate = display.CreateContext(null); var immediate = display.CreateContext(null);
var deferred = display.CreateContext(immediate);
int major, minor; int major, minor;
GlInterface glInterface; GlInterface glInterface;
using (immediate.MakeCurrent()) using (immediate.MakeCurrent())
@ -33,19 +32,22 @@ namespace Avalonia.Native
} }
GlDisplay = new GlDisplay(display, glInterface, immediate.SampleCount, immediate.StencilSize); GlDisplay = new GlDisplay(display, glInterface, immediate.SampleCount, immediate.StencilSize);
MainContext = new GlContext(GlDisplay, null, immediate, _version);
ImmediateContext = new GlContext(GlDisplay, immediate, _version);
DeferredContext = new GlContext(GlDisplay, deferred, _version);
} }
internal IGlContext ImmediateContext { get; } internal GlContext MainContext { get; }
public IGlContext MainContext => DeferredContext; public IGlContext PrimaryContext => MainContext;
internal GlContext DeferredContext { get; }
public bool CanShareContexts => true;
public bool CanCreateContexts => true;
internal GlDisplay GlDisplay; internal GlDisplay GlDisplay;
private readonly GlVersion _version; private readonly GlVersion _version;
public IGlContext CreateSharedContext() => new GlContext(GlDisplay,
MainContext, _display.CreateContext(MainContext.Context), _version);
public IGlContext CreateContext() => new GlContext(GlDisplay, public IGlContext CreateContext() => new GlContext(GlDisplay,
_display.CreateContext(((GlContext)ImmediateContext).Context), _version); null, _display.CreateContext(null), _version);
} }
class GlDisplay class GlDisplay
@ -72,11 +74,13 @@ namespace Avalonia.Native
class GlContext : IGlContext class GlContext : IGlContext
{ {
private readonly GlDisplay _display; private readonly GlDisplay _display;
private readonly GlContext _sharedWith;
public IAvnGlContext Context { get; private set; } public IAvnGlContext Context { get; private set; }
public GlContext(GlDisplay display, IAvnGlContext context, GlVersion version) public GlContext(GlDisplay display, GlContext sharedWith, IAvnGlContext context, GlVersion version)
{ {
_display = display; _display = display;
_sharedWith = sharedWith;
Context = context; Context = context;
Version = version; Version = version;
} }
@ -86,6 +90,17 @@ namespace Avalonia.Native
public int SampleCount => _display.SampleCount; public int SampleCount => _display.SampleCount;
public int StencilSize => _display.StencilSize; public int StencilSize => _display.StencilSize;
public IDisposable MakeCurrent() => Context.MakeCurrent(); public IDisposable MakeCurrent() => Context.MakeCurrent();
public IDisposable EnsureCurrent() => MakeCurrent();
public bool IsSharedWith(IGlContext context)
{
var c = (GlContext)context;
return c == this
|| c._sharedWith == this
|| _sharedWith == context
|| _sharedWith != null && _sharedWith == c._sharedWith;
}
public void Dispose() public void Dispose()
{ {
@ -108,7 +123,7 @@ namespace Avalonia.Native
public IGlPlatformSurfaceRenderingSession BeginDraw() public IGlPlatformSurfaceRenderingSession BeginDraw()
{ {
var feature = (GlPlatformFeature)AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>(); var feature = (AvaloniaNativePlatformOpenGlInterface)AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
return new GlPlatformSurfaceRenderingSession(_context, _target.BeginDrawing()); return new GlPlatformSurfaceRenderingSession(_context, _target.BeginDrawing());
} }

6
src/Avalonia.Native/PopupImpl.cs

@ -9,12 +9,12 @@ namespace Avalonia.Native
{ {
private readonly IAvaloniaNativeFactory _factory; private readonly IAvaloniaNativeFactory _factory;
private readonly AvaloniaNativePlatformOptions _opts; private readonly AvaloniaNativePlatformOptions _opts;
private readonly GlPlatformFeature _glFeature; private readonly AvaloniaNativePlatformOpenGlInterface _glFeature;
private readonly IWindowBaseImpl _parent; private readonly IWindowBaseImpl _parent;
public PopupImpl(IAvaloniaNativeFactory factory, public PopupImpl(IAvaloniaNativeFactory factory,
AvaloniaNativePlatformOptions opts, AvaloniaNativePlatformOptions opts,
GlPlatformFeature glFeature, AvaloniaNativePlatformOpenGlInterface glFeature,
IWindowBaseImpl parent) : base(opts, glFeature) IWindowBaseImpl parent) : base(opts, glFeature)
{ {
_factory = factory; _factory = factory;
@ -23,7 +23,7 @@ namespace Avalonia.Native
_parent = parent; _parent = parent;
using (var e = new PopupEvents(this)) using (var e = new PopupEvents(this))
{ {
var context = _opts.UseGpu ? glFeature?.DeferredContext : null; var context = _opts.UseGpu ? glFeature?.MainContext : null;
Init(factory.CreatePopup(e, context?.Context), factory.CreateScreens(), context); Init(factory.CreatePopup(e, context?.Context), factory.CreateScreens(), context);
} }
PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize)); PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize));

6
src/Avalonia.Native/WindowImpl.cs

@ -14,19 +14,19 @@ namespace Avalonia.Native
{ {
private readonly IAvaloniaNativeFactory _factory; private readonly IAvaloniaNativeFactory _factory;
private readonly AvaloniaNativePlatformOptions _opts; private readonly AvaloniaNativePlatformOptions _opts;
private readonly GlPlatformFeature _glFeature; private readonly AvaloniaNativePlatformOpenGlInterface _glFeature;
IAvnWindow _native; IAvnWindow _native;
private double _extendTitleBarHeight = -1; private double _extendTitleBarHeight = -1;
internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts, internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
GlPlatformFeature glFeature) : base(opts, glFeature) AvaloniaNativePlatformOpenGlInterface glFeature) : base(opts, glFeature)
{ {
_factory = factory; _factory = factory;
_opts = opts; _opts = opts;
_glFeature = glFeature; _glFeature = glFeature;
using (var e = new WindowEvents(this)) using (var e = new WindowEvents(this))
{ {
var context = _opts.UseGpu ? glFeature?.DeferredContext : null; var context = _opts.UseGpu ? glFeature?.MainContext : null;
Init(_native = factory.CreateWindow(e, context?.Context), factory.CreateScreens(), context); Init(_native = factory.CreateWindow(e, context?.Context), factory.CreateScreens(), context);
} }

2
src/Avalonia.Native/WindowImplBase.cs

@ -61,7 +61,7 @@ namespace Avalonia.Native
private NativeControlHostImpl _nativeControlHost; private NativeControlHostImpl _nativeControlHost;
private IGlContext _glContext; private IGlContext _glContext;
internal WindowBaseImpl(AvaloniaNativePlatformOptions opts, GlPlatformFeature glFeature) internal WindowBaseImpl(AvaloniaNativePlatformOptions opts, AvaloniaNativePlatformOpenGlInterface glFeature)
{ {
_gpu = opts.UseGpu && glFeature != null; _gpu = opts.UseGpu && glFeature != null;
_deferredRendering = opts.UseDeferredRendering; _deferredRendering = opts.UseDeferredRendering;

1
src/Avalonia.OpenGL/Angle/AngleEglInterface.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Platform.Interop; using Avalonia.Platform.Interop;

10
src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs

@ -1,8 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.OpenGL.Egl;
using static Avalonia.OpenGL.EglConsts; using static Avalonia.OpenGL.Egl.EglConsts;
namespace Avalonia.OpenGL.Angle namespace Avalonia.OpenGL.Angle
{ {
@ -52,7 +52,7 @@ namespace Avalonia.OpenGL.Angle
} }
} }
private AngleWin32EglDisplay(EglInterface egl, AngleInfo info) : base(egl, info.Display) private AngleWin32EglDisplay(EglInterface egl, AngleInfo info) : base(egl, false, info.Display)
{ {
PlatformApi = info.PlatformApi; PlatformApi = info.PlatformApi;
} }
@ -78,11 +78,11 @@ namespace Avalonia.OpenGL.Angle
return d3dDeviceHandle; return d3dDeviceHandle;
} }
public EglSurface WrapDirect3D11Texture(IntPtr handle) public EglSurface WrapDirect3D11Texture(EglPlatformOpenGlInterface egl, IntPtr handle)
{ {
if (PlatformApi != AngleOptions.PlatformApi.DirectX11) if (PlatformApi != AngleOptions.PlatformApi.DirectX11)
throw new InvalidOperationException("Current platform API is " + PlatformApi); throw new InvalidOperationException("Current platform API is " + PlatformApi);
return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, EGL_NONE }); return egl.CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, EGL_NONE });
} }
} }
} }

192
src/Avalonia.OpenGL/OpenGlControlBase.cs → src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs

@ -3,44 +3,83 @@ using Avalonia.Controls;
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.OpenGL.Imaging; using Avalonia.OpenGL.Imaging;
using Avalonia.Rendering;
using Avalonia.VisualTree;
using static Avalonia.OpenGL.GlConsts; using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Controls
{ {
public abstract class OpenGlControlBase : Control public abstract class OpenGlControlBase : Control
{ {
private IGlContext _context; private IGlContext _context;
private int _fb, _texture, _renderBuffer; private int _fb, _depthBuffer;
private OpenGlTextureBitmap _bitmap; private OpenGlBitmap _bitmap;
private PixelSize _oldSize; private IOpenGlBitmapAttachment _attachment;
private PixelSize _depthBufferSize;
private bool _glFailed; private bool _glFailed;
private bool _initialized;
protected GlVersion GlVersion { get; private set; } protected GlVersion GlVersion { get; private set; }
public sealed override void Render(DrawingContext context) public sealed override void Render(DrawingContext context)
{ {
if(!EnsureInitialized()) if(!EnsureInitialized())
return; return;
using (_context.MakeCurrent()) using (_context.MakeCurrent())
{ {
using (_bitmap.Lock()) _context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, _fb);
{ EnsureTextureAttachment();
var gl = _context.GlInterface; EnsureDepthBufferAttachment(_context.GlInterface);
gl.BindFramebuffer(GL_FRAMEBUFFER, _fb); if(!CheckFramebufferStatus(_context.GlInterface))
if (_oldSize != GetPixelSize()) return;
ResizeTexture(gl);
OnOpenGlRender(_context.GlInterface, _fb);
OnOpenGlRender(gl, _fb); _attachment.Present();
gl.Flush();
}
} }
context.DrawImage(_bitmap, new Rect(_bitmap.Size), Bounds); context.DrawImage(_bitmap, new Rect(_bitmap.Size), Bounds);
base.Render(context); base.Render(context);
} }
private void CheckError(GlInterface gl)
{
int err;
while ((err = gl.GetError()) != GL_NO_ERROR)
Console.WriteLine(err);
}
void EnsureTextureAttachment()
{
_context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, _fb);
if (_bitmap == null || _attachment == null || _bitmap.PixelSize != GetPixelSize())
{
_attachment?.Dispose();
_attachment = null;
_bitmap?.Dispose();
_bitmap = null;
_bitmap = new OpenGlBitmap(GetPixelSize(), new Vector(96, 96));
_attachment = _bitmap.CreateFramebufferAttachment(_context);
}
}
void EnsureDepthBufferAttachment(GlInterface gl)
{
var size = GetPixelSize();
if (size == _depthBufferSize && _depthBuffer != 0)
return;
gl.GetIntegerv(GL_RENDERBUFFER_BINDING, out var oldRenderBuffer);
if (_depthBuffer != 0) gl.DeleteRenderbuffers(1, new[] { _depthBuffer });
var oneArr = new int[1];
gl.GenRenderbuffers(1, oneArr);
_depthBuffer = oneArr[0];
gl.BindRenderbuffer(GL_RENDERBUFFER, _depthBuffer);
gl.RenderbufferStorage(GL_RENDERBUFFER,
GlVersion.Type == GlProfileType.OpenGLES ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT,
size.Width, size.Height);
gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthBuffer);
gl.BindRenderbuffer(GL_RENDERBUFFER, oldRenderBuffer);
}
void DoCleanup(bool callUserDeinit) void DoCleanup()
{ {
if (_context != null) if (_context != null)
{ {
@ -50,16 +89,19 @@ namespace Avalonia.OpenGL
gl.BindTexture(GL_TEXTURE_2D, 0); gl.BindTexture(GL_TEXTURE_2D, 0);
gl.BindFramebuffer(GL_FRAMEBUFFER, 0); gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
gl.DeleteFramebuffers(1, new[] { _fb }); gl.DeleteFramebuffers(1, new[] { _fb });
using (_bitmap.Lock()) gl.DeleteRenderbuffers(1, new[] { _depthBuffer });
_bitmap.SetTexture(0, 0, new PixelSize(1, 1), 1); _attachment?.Dispose();
gl.DeleteTextures(1, new[] { _texture }); _attachment = null;
gl.DeleteRenderbuffers(1, new[] { _renderBuffer }); _bitmap?.Dispose();
_bitmap.Dispose(); _bitmap = null;
try try
{ {
if (callUserDeinit) if (_initialized)
{
_initialized = false;
OnOpenGlDeinit(_context.GlInterface, _fb); OnOpenGlDeinit(_context.GlInterface, _fb);
}
} }
finally finally
{ {
@ -72,11 +114,11 @@ namespace Avalonia.OpenGL
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{ {
DoCleanup(true); DoCleanup();
base.OnDetachedFromVisualTree(e); base.OnDetachedFromVisualTree(e);
} }
bool EnsureInitialized() private bool EnsureInitializedCore()
{ {
if (_context != null) if (_context != null)
return true; return true;
@ -84,34 +126,43 @@ namespace Avalonia.OpenGL
if (_glFailed) if (_glFailed)
return false; return false;
var feature = AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>(); var feature = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
if (feature == null) if (feature == null)
return false; return false;
if (!feature.CanShareContexts)
{
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL: current platform does not support multithreaded context sharing");
return false;
}
try try
{ {
_context = feature.CreateContext(); _context = feature.CreateSharedContext();
} }
catch (Exception e) catch (Exception e)
{ {
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase", Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL: unable to create additional OpenGL context: {exception}", e); "Unable to initialize OpenGL: unable to create additional OpenGL context: {exception}", e);
_glFailed = true;
return false; return false;
} }
GlVersion = _context.Version; GlVersion = _context.Version;
try try
{ {
_bitmap = new OpenGlTextureBitmap(); _bitmap = new OpenGlBitmap(GetPixelSize(), new Vector(96, 96));
if (!_bitmap.SupportsContext(_context))
{
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL: unable to create OpenGlBitmap: OpenGL context is not compatible");
return false;
}
} }
catch (Exception e) catch (Exception e)
{ {
_context.Dispose(); _context.Dispose();
_context = null; _context = null;
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase", Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL: unable to create OpenGlTextureBitmap: {exception}", e); "Unable to initialize OpenGL: unable to create OpenGlBitmap: {exception}", e);
_glFailed = true;
return false; return false;
} }
@ -119,80 +170,55 @@ namespace Avalonia.OpenGL
{ {
try try
{ {
_oldSize = GetPixelSize(); _depthBufferSize = GetPixelSize();
var gl = _context.GlInterface; var gl = _context.GlInterface;
var oneArr = new int[1]; var oneArr = new int[1];
gl.GenFramebuffers(1, oneArr); gl.GenFramebuffers(1, oneArr);
_fb = oneArr[0]; _fb = oneArr[0];
gl.BindFramebuffer(GL_FRAMEBUFFER, _fb); gl.BindFramebuffer(GL_FRAMEBUFFER, _fb);
gl.GenTextures(1, oneArr);
_texture = oneArr[0];
ResizeTexture(gl); EnsureDepthBufferAttachment(gl);
EnsureTextureAttachment();
gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER); return CheckFramebufferStatus(gl);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
int code;
while ((code = gl.GetError()) != 0)
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL FBO: {code}", code);
_glFailed = true;
return false;
}
} }
catch(Exception e) catch(Exception e)
{ {
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase", Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL FBO: {exception}", e); "Unable to initialize OpenGL FBO: {exception}", e);
_glFailed = true; return false;
} }
if (!_glFailed)
OnOpenGlInit(_context.GlInterface, _fb);
} }
}
if (_glFailed) private bool CheckFramebufferStatus(GlInterface gl)
{
var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{ {
DoCleanup(false); int code;
while ((code = gl.GetError()) != 0)
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL FBO: {code}", code);
return false;
} }
return true; return true;
} }
void ResizeTexture(GlInterface gl) private bool EnsureInitialized()
{ {
var size = GetPixelSize(); if (_initialized)
return true;
gl.GetIntegerv( GL_TEXTURE_BINDING_2D, out var oldTexture); _glFailed = !(_initialized = EnsureInitializedCore());
gl.BindTexture(GL_TEXTURE_2D, _texture); if (_glFailed)
gl.TexImage2D(GL_TEXTURE_2D, 0, return false;
GlVersion.Type == GlProfileType.OpenGLES ? GL_RGBA : GL_RGBA8, using (_context.MakeCurrent())
size.Width, size.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, IntPtr.Zero); OnOpenGlInit(_context.GlInterface, _fb);
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); return true;
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
gl.BindTexture(GL_TEXTURE_2D, oldTexture);
gl.GetIntegerv(GL_RENDERBUFFER_BINDING, out var oldRenderBuffer);
gl.DeleteRenderbuffers(1, new[] { _renderBuffer });
var oneArr = new int[1];
gl.GenRenderbuffers(1, oneArr);
_renderBuffer = oneArr[0];
gl.BindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
gl.RenderbufferStorage(GL_RENDERBUFFER,
GlVersion.Type == GlProfileType.OpenGLES ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT,
size.Width, size.Height);
gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _renderBuffer);
gl.BindRenderbuffer(GL_RENDERBUFFER, oldRenderBuffer);
using (_bitmap.Lock())
_bitmap.SetTexture(_texture, GL_RGBA8, size, 1);
} }
PixelSize GetPixelSize() private PixelSize GetPixelSize()
{ {
var scaling = VisualRoot.RenderScaling; var scaling = VisualRoot.RenderScaling;
return new PixelSize(Math.Max(1, (int)(Bounds.Width * scaling)), return new PixelSize(Math.Max(1, (int)(Bounds.Width * scaling)),

2
src/Avalonia.OpenGL/EglConsts.cs → src/Avalonia.OpenGL/Egl/EglConsts.cs

@ -1,6 +1,6 @@
// ReSharper disable UnusedMember.Global // ReSharper disable UnusedMember.Global
// ReSharper disable IdentifierTypo // ReSharper disable IdentifierTypo
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Egl
{ {
public static class EglConsts public static class EglConsts
{ {

69
src/Avalonia.OpenGL/EglContext.cs → src/Avalonia.OpenGL/Egl/EglContext.cs

@ -1,23 +1,25 @@
using System; using System;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Threading; using System.Threading;
using static Avalonia.OpenGL.EglConsts; using static Avalonia.OpenGL.Egl.EglConsts;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Egl
{ {
public class EglContext : IGlContext public class EglContext : IGlContext
{ {
private readonly EglDisplay _disp; private readonly EglDisplay _disp;
private readonly EglInterface _egl; private readonly EglInterface _egl;
private readonly EglContext _sharedWith;
private readonly object _lock = new object(); private readonly object _lock = new object();
public EglContext(EglDisplay display, EglInterface egl, IntPtr ctx, EglSurface offscreenSurface, public EglContext(EglDisplay display, EglInterface egl, EglContext sharedWith, IntPtr ctx, Func<EglContext, EglSurface> offscreenSurface,
GlVersion version, int sampleCount, int stencilSize) GlVersion version, int sampleCount, int stencilSize)
{ {
_disp = display; _disp = display;
_egl = egl; _egl = egl;
_sharedWith = sharedWith;
Context = ctx; Context = ctx;
OffscreenSurface = offscreenSurface; OffscreenSurface = offscreenSurface(this);
Version = version; Version = version;
SampleCount = sampleCount; SampleCount = sampleCount;
StencilSize = stencilSize; StencilSize = stencilSize;
@ -33,21 +35,17 @@ namespace Avalonia.OpenGL
public int StencilSize { get; } public int StencilSize { get; }
public EglDisplay Display => _disp; public EglDisplay Display => _disp;
public IDisposable Lock()
{
Monitor.Enter(_lock);
return Disposable.Create(() => Monitor.Exit(_lock));
}
class RestoreContext : IDisposable class RestoreContext : IDisposable
{ {
private readonly EglInterface _egl; private readonly EglInterface _egl;
private readonly object _l;
private readonly IntPtr _display; private readonly IntPtr _display;
private IntPtr _context, _read, _draw; private IntPtr _context, _read, _draw;
public RestoreContext(EglInterface egl, IntPtr defDisplay) public RestoreContext(EglInterface egl, IntPtr defDisplay, object l)
{ {
_egl = egl; _egl = egl;
_l = l;
_display = _egl.GetCurrentDisplay(); _display = _egl.GetCurrentDisplay();
if (_display == IntPtr.Zero) if (_display == IntPtr.Zero)
_display = defDisplay; _display = defDisplay;
@ -59,29 +57,52 @@ namespace Avalonia.OpenGL
public void Dispose() public void Dispose()
{ {
_egl.MakeCurrent(_display, _draw, _read, _context); _egl.MakeCurrent(_display, _draw, _read, _context);
Monitor.Exit(_l);
} }
} }
public IDisposable MakeCurrent() public IDisposable MakeCurrent() => MakeCurrent(OffscreenSurface);
public IDisposable MakeCurrent(EglSurface surface)
{ {
var old = new RestoreContext(_egl, _disp.Handle); Monitor.Enter(_lock);
_egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); var success = false;
if (!_egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, Context)) try
throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl); {
return old; 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))
throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl);
success = true;
return old;
}
finally
{
if(!success)
Monitor.Enter(_lock);
}
} }
public IDisposable MakeCurrent(EglSurface surface) public IDisposable EnsureCurrent()
{ {
var old = new RestoreContext(_egl, _disp.Handle); if(IsCurrent)
var surf = surface ?? OffscreenSurface; return Disposable.Empty;
_egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); return MakeCurrent();
if (!_egl.MakeCurrent(_disp.Handle, surf.DangerousGetHandle(), surf.DangerousGetHandle(), Context))
throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl);
return old;
} }
public bool IsSharedWith(IGlContext context)
{
var c = (EglContext)context;
return c == this
|| c._sharedWith == this
|| _sharedWith == context
|| _sharedWith != null && _sharedWith == c._sharedWith;
}
public bool IsCurrent => _egl.GetCurrentDisplay() == _disp.Handle && _egl.GetCurrentContext() == Context;
public void Dispose() public void Dispose()
{ {
_egl.DestroyContext(_disp.Handle, Context); _egl.DestroyContext(_disp.Handle, Context);

53
src/Avalonia.OpenGL/EglDisplay.cs → src/Avalonia.OpenGL/Egl/EglDisplay.cs

@ -1,26 +1,25 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using static Avalonia.OpenGL.Egl.EglConsts;
using Avalonia.Platform.Interop;
using static Avalonia.OpenGL.EglConsts;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Egl
{ {
public class EglDisplay public class EglDisplay
{ {
private readonly EglInterface _egl; private readonly EglInterface _egl;
public bool SupportsSharing { get; }
private readonly IntPtr _display; private readonly IntPtr _display;
private readonly IntPtr _config; private readonly IntPtr _config;
private readonly int[] _contextAttributes; private readonly int[] _contextAttributes;
private readonly int _surfaceType; private readonly int _surfaceType;
public IntPtr Handle => _display; public IntPtr Handle => _display;
public IntPtr Config => _config;
private int _sampleCount; private int _sampleCount;
private int _stencilSize; private int _stencilSize;
private GlVersion _version; private GlVersion _version;
public EglDisplay(EglInterface egl) : this(egl, -1, IntPtr.Zero, null) public EglDisplay(EglInterface egl, bool supportsSharing) : this(egl, supportsSharing, -1, IntPtr.Zero, null)
{ {
} }
@ -45,15 +44,16 @@ namespace Avalonia.OpenGL
return display; return display;
} }
public EglDisplay(EglInterface egl, int platformType, IntPtr platformDisplay, int[] attrs) public EglDisplay(EglInterface egl, bool supportsSharing, int platformType, IntPtr platformDisplay, int[] attrs)
: this(egl, CreateDisplay(egl, platformType, platformDisplay, attrs)) : this(egl, supportsSharing, CreateDisplay(egl, platformType, platformDisplay, attrs))
{ {
} }
public EglDisplay(EglInterface egl, IntPtr display) public EglDisplay(EglInterface egl, bool supportsSharing, IntPtr display)
{ {
_egl = egl; _egl = egl;
SupportsSharing = supportsSharing;
_display = display; _display = display;
if(_display == IntPtr.Zero) if(_display == IntPtr.Zero)
throw new ArgumentException(); throw new ArgumentException();
@ -136,7 +136,12 @@ namespace Avalonia.OpenGL
throw new OpenGlException("No suitable EGL config was found"); throw new OpenGlException("No suitable EGL config was found");
} }
public EglDisplay() : this(new EglInterface()) public EglDisplay() : this(false)
{
}
public EglDisplay(bool supportsSharing) : this(new EglInterface(), supportsSharing)
{ {
} }
@ -144,6 +149,9 @@ namespace Avalonia.OpenGL
public EglInterface EglInterface => _egl; public EglInterface EglInterface => _egl;
public EglContext CreateContext(IGlContext share) public EglContext CreateContext(IGlContext share)
{ {
if (share != null && !SupportsSharing)
throw new NotSupportedException("Context sharing is not supported by this display");
if((_surfaceType|EGL_PBUFFER_BIT) == 0) if((_surfaceType|EGL_PBUFFER_BIT) == 0)
throw new InvalidOperationException("Platform doesn't support PBUFFER surfaces"); throw new InvalidOperationException("Platform doesn't support PBUFFER surfaces");
var shareCtx = (EglContext)share; var shareCtx = (EglContext)share;
@ -158,37 +166,22 @@ namespace Avalonia.OpenGL
}); });
if (surf == IntPtr.Zero) if (surf == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglCreatePBufferSurface", _egl); throw OpenGlException.GetFormattedException("eglCreatePBufferSurface", _egl);
var rv = new EglContext(this, _egl, ctx, new EglSurface(this, _egl, surf), var rv = new EglContext(this, _egl, shareCtx, ctx, context => new EglSurface(this, context, surf),
_version, _sampleCount, _stencilSize); _version, _sampleCount, _stencilSize);
return rv; return rv;
} }
public EglContext CreateContext(EglContext share, EglSurface offscreenSurface) public EglContext CreateContext(EglContext share, EglSurface offscreenSurface)
{ {
if (share != null && !SupportsSharing)
throw new NotSupportedException("Context sharing is not supported by this display");
var ctx = _egl.CreateContext(_display, _config, share?.Context ?? IntPtr.Zero, _contextAttributes); var ctx = _egl.CreateContext(_display, _config, share?.Context ?? IntPtr.Zero, _contextAttributes);
if (ctx == IntPtr.Zero) if (ctx == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglCreateContext", _egl); throw OpenGlException.GetFormattedException("eglCreateContext", _egl);
var rv = new EglContext(this, _egl, ctx, offscreenSurface, _version, _sampleCount, _stencilSize); var rv = new EglContext(this, _egl, share, ctx, _ => offscreenSurface, _version, _sampleCount, _stencilSize);
rv.MakeCurrent(null); rv.MakeCurrent(null);
return rv; return rv;
} }
public EglSurface CreateWindowSurface(IntPtr window)
{
var s = _egl.CreateWindowSurface(_display, _config, window, new[] {EGL_NONE, EGL_NONE});
if (s == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglCreateWindowSurface", _egl);
return new EglSurface(this, _egl, s);
}
public EglSurface CreatePBufferFromClientBuffer (int bufferType, IntPtr handle, int[] attribs)
{
var s = _egl.CreatePbufferFromClientBuffer(_display, bufferType, handle,
_config, attribs);
if (s == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglCreatePbufferFromClientBuffer", _egl);
return new EglSurface(this, _egl, s);
}
} }
} }

2
src/Avalonia.OpenGL/EglErrors.cs → src/Avalonia.OpenGL/Egl/EglErrors.cs

@ -1,4 +1,4 @@
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Egl
{ {
public enum EglErrors public enum EglErrors
{ {

54
src/Avalonia.OpenGL/Egl/EglGlPlatformSurface.cs

@ -0,0 +1,54 @@
using Avalonia.OpenGL.Surfaces;
namespace Avalonia.OpenGL.Egl
{
public class EglGlPlatformSurface : EglGlPlatformSurfaceBase
{
private readonly EglPlatformOpenGlInterface _egl;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
public EglGlPlatformSurface(EglPlatformOpenGlInterface egl, IEglWindowGlPlatformSurfaceInfo info) : base()
{
_egl = egl;
_info = info;
}
public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
{
var glSurface = _egl.CreateWindowSurface(_info.Handle);
return new RenderTarget(_egl, glSurface, _info);
}
class RenderTarget : EglPlatformSurfaceRenderTargetBase
{
private readonly EglPlatformOpenGlInterface _egl;
private EglSurface _glSurface;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
private PixelSize _currentSize;
public RenderTarget(EglPlatformOpenGlInterface egl,
EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info) : base(egl)
{
_egl = egl;
_glSurface = glSurface;
_info = info;
_currentSize = info.Size;
}
public override void Dispose() => _glSurface.Dispose();
public override IGlPlatformSurfaceRenderingSession BeginDraw()
{
if (_info.Size != _currentSize || _glSurface == null)
{
_glSurface?.Dispose();
_glSurface = null;
_glSurface = _egl.CreateWindowSurface(_info.Handle);
_currentSize = _info.Size;
}
return base.BeginDraw(_glSurface, _info);
}
}
}
}

47
src/Avalonia.OpenGL/EglGlPlatformSurfaceBase.cs → src/Avalonia.OpenGL/Egl/EglGlPlatformSurfaceBase.cs

@ -1,6 +1,7 @@
using System; using System;
using Avalonia.OpenGL.Surfaces;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Egl
{ {
public abstract class EglGlPlatformSurfaceBase : IGlPlatformSurface public abstract class EglGlPlatformSurfaceBase : IGlPlatformSurface
{ {
@ -14,19 +15,15 @@ namespace Avalonia.OpenGL
public abstract IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(); public abstract IGlPlatformSurfaceRenderTarget CreateGlRenderTarget();
} }
public abstract class EglPlatformSurfaceRenderTargetBase : IGlPlatformSurfaceRenderTargetWithCorruptionInfo public abstract class EglPlatformSurfaceRenderTargetBase : IGlPlatformSurfaceRenderTarget
{ {
private readonly EglDisplay _display; private readonly EglPlatformOpenGlInterface _egl;
private readonly EglContext _context;
protected EglPlatformSurfaceRenderTargetBase(EglDisplay display, EglContext context) protected EglPlatformSurfaceRenderTargetBase(EglPlatformOpenGlInterface egl)
{ {
_display = display; _egl = egl;
_context = context;
} }
public abstract bool IsCorrupted { get; }
public virtual void Dispose() public virtual void Dispose()
{ {
@ -37,22 +34,25 @@ namespace Avalonia.OpenGL
protected IGlPlatformSurfaceRenderingSession BeginDraw(EglSurface surface, protected IGlPlatformSurfaceRenderingSession BeginDraw(EglSurface surface,
EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info, Action onFinish = null, bool isYFlipped = false) EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info, Action onFinish = null, bool isYFlipped = false)
{ {
var l = _context.Lock();
var restoreContext = _egl.PrimaryEglContext.MakeCurrent(surface);
var success = false;
try try
{ {
if (IsCorrupted) var egli = _egl.Display.EglInterface;
throw new RenderTargetCorruptedException(); egli.WaitClient();
var restoreContext = _context.MakeCurrent(surface); egli.WaitGL();
_display.EglInterface.WaitClient(); egli.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
_display.EglInterface.WaitGL();
_display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE); _egl.PrimaryContext.GlInterface.BindFramebuffer(GlConsts.GL_FRAMEBUFFER, 0);
return new Session(_display, _context, surface, info, l, restoreContext, onFinish, isYFlipped); success = true;
return new Session(_egl.Display, _egl.PrimaryEglContext, surface, info, restoreContext, onFinish, isYFlipped);
} }
catch finally
{ {
l.Dispose(); if(!success)
throw; restoreContext.Dispose();
} }
} }
@ -62,21 +62,19 @@ namespace Avalonia.OpenGL
private readonly EglSurface _glSurface; private readonly EglSurface _glSurface;
private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info; private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info;
private readonly EglDisplay _display; private readonly EglDisplay _display;
private readonly IDisposable _lock;
private readonly IDisposable _restoreContext; private readonly IDisposable _restoreContext;
private readonly Action _onFinish; private readonly Action _onFinish;
public Session(EglDisplay display, EglContext context, public Session(EglDisplay display, EglContext context,
EglSurface glSurface, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info, EglSurface glSurface, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info,
IDisposable @lock, IDisposable restoreContext, Action onFinish, bool isYFlipped) IDisposable restoreContext, Action onFinish, bool isYFlipped)
{ {
IsYFlipped = isYFlipped; IsYFlipped = isYFlipped;
_context = context; _context = context;
_display = display; _display = display;
_glSurface = glSurface; _glSurface = glSurface;
_info = info; _info = info;
_lock = @lock;
_restoreContext = restoreContext; _restoreContext = restoreContext;
_onFinish = onFinish; _onFinish = onFinish;
} }
@ -90,7 +88,6 @@ namespace Avalonia.OpenGL
_display.EglInterface.WaitGL(); _display.EglInterface.WaitGL();
_display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE); _display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
_restoreContext.Dispose(); _restoreContext.Dispose();
_lock.Dispose();
_onFinish?.Invoke(); _onFinish?.Invoke();
} }

2
src/Avalonia.OpenGL/EglInterface.cs → src/Avalonia.OpenGL/Egl/EglInterface.cs

@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Platform.Interop; using Avalonia.Platform.Interop;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Egl
{ {
public class EglInterface : GlInterfaceBase public class EglInterface : GlInterfaceBase
{ {

72
src/Avalonia.OpenGL/Egl/EglPlatformOpenGlInterface.cs

@ -0,0 +1,72 @@
using System;
using Avalonia.Logging;
using static Avalonia.OpenGL.Egl.EglConsts;
namespace Avalonia.OpenGL.Egl
{
public class EglPlatformOpenGlInterface : IPlatformOpenGlInterface
{
public EglDisplay Display { get; private set; }
public bool CanCreateContexts => true;
public bool CanShareContexts => Display.SupportsSharing;
public EglContext PrimaryEglContext { get; }
public IGlContext PrimaryContext => PrimaryEglContext;
public EglPlatformOpenGlInterface(EglDisplay display)
{
Display = display;
PrimaryEglContext = display.CreateContext(null);
}
public static void TryInitialize()
{
var feature = TryCreate();
if (feature != null)
AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>().ToConstant(feature);
}
public static EglPlatformOpenGlInterface TryCreate() => TryCreate(() => new EglDisplay());
public static EglPlatformOpenGlInterface TryCreate(Func<EglDisplay> displayFactory)
{
try
{
return new EglPlatformOpenGlInterface(displayFactory());
}
catch(Exception e)
{
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log(null, "Unable to initialize EGL-based rendering: {0}", e);
return null;
}
}
public IGlContext CreateContext() => Display.CreateContext(null);
public IGlContext CreateSharedContext() => Display.CreateContext(PrimaryEglContext);
public EglSurface CreateWindowSurface(IntPtr window)
{
using (PrimaryContext.MakeCurrent())
{
var s = Display.EglInterface.CreateWindowSurface(Display.Handle, Display.Config, window,
new[] { EGL_NONE, EGL_NONE });
if (s == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglCreateWindowSurface", Display.EglInterface);
return new EglSurface(Display, PrimaryEglContext, s);
}
}
public EglSurface CreatePBufferFromClientBuffer (int bufferType, IntPtr handle, int[] attribs)
{
using (PrimaryContext.MakeCurrent())
{
var s = Display.EglInterface.CreatePbufferFromClientBuffer(Display.Handle, bufferType, handle,
Display.Config, attribs);
if (s == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglCreatePbufferFromClientBuffer", Display.EglInterface);
return new EglSurface(Display, PrimaryEglContext, s);
}
}
}
}

11
src/Avalonia.OpenGL/EglSurface.cs → src/Avalonia.OpenGL/Egl/EglSurface.cs

@ -1,22 +1,25 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Egl
{ {
public class EglSurface : SafeHandle public class EglSurface : SafeHandle
{ {
private readonly EglDisplay _display; private readonly EglDisplay _display;
private readonly EglContext _context;
private readonly EglInterface _egl; private readonly EglInterface _egl;
public EglSurface(EglDisplay display, EglInterface egl, IntPtr surface) : base(surface, true) public EglSurface(EglDisplay display, EglContext context, IntPtr surface) : base(surface, true)
{ {
_display = display; _display = display;
_egl = egl; _context = context;
_egl = display.EglInterface;
} }
protected override bool ReleaseHandle() protected override bool ReleaseHandle()
{ {
_egl.DestroySurface(_display.Handle, handle); using (_context.MakeCurrent())
_egl.DestroySurface(_display.Handle, handle);
return true; return true;
} }

43
src/Avalonia.OpenGL/EglGlPlatformFeature.cs

@ -1,43 +0,0 @@
using System;
using Avalonia.Logging;
namespace Avalonia.OpenGL
{
public class EglGlPlatformFeature : IWindowingPlatformGlFeature
{
private EglDisplay _display;
public EglDisplay Display => _display;
public IGlContext CreateContext()
{
return _display.CreateContext(DeferredContext);
}
public EglContext DeferredContext { get; private set; }
public IGlContext MainContext => DeferredContext;
public static void TryInitialize()
{
var feature = TryCreate();
if (feature != null)
AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToConstant(feature);
}
public static EglGlPlatformFeature TryCreate() => TryCreate(() => new EglDisplay());
public static EglGlPlatformFeature TryCreate(Func<EglDisplay> displayFactory)
{
try
{
var disp = displayFactory();
return new EglGlPlatformFeature
{
_display = disp,
DeferredContext = disp.CreateContext(null)
};
}
catch(Exception e)
{
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log(null, "Unable to initialize EGL-based rendering: {0}", e);
return null;
}
}
}
}

51
src/Avalonia.OpenGL/EglGlPlatformSurface.cs

@ -1,51 +0,0 @@
using System;
using System.Threading;
namespace Avalonia.OpenGL
{
public class EglGlPlatformSurface : EglGlPlatformSurfaceBase
{
private readonly EglDisplay _display;
private readonly EglContext _context;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
public EglGlPlatformSurface(EglContext context, IEglWindowGlPlatformSurfaceInfo info) : base()
{
_display = context.Display;
_context = context;
_info = info;
}
public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
{
var glSurface = _display.CreateWindowSurface(_info.Handle);
return new RenderTarget(_display, _context, glSurface, _info);
}
class RenderTarget : EglPlatformSurfaceRenderTargetBase
{
private readonly EglDisplay _display;
private readonly EglContext _context;
private readonly EglSurface _glSurface;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
private PixelSize _initialSize;
public RenderTarget(EglDisplay display, EglContext context,
EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info) : base(display, context)
{
_display = display;
_context = context;
_glSurface = glSurface;
_info = info;
_initialSize = info.Size;
}
public override void Dispose() => _glSurface.Dispose();
public override bool IsCorrupted => _initialSize != _info.Size;
public override IGlPlatformSurfaceRenderingSession BeginDraw() => base.BeginDraw(_glSurface, _info);
}
}
}

13
src/Avalonia.OpenGL/GlInterface.cs

@ -82,6 +82,9 @@ namespace Avalonia.OpenGL
[GlEntryPoint("glFlush")] [GlEntryPoint("glFlush")]
public Action Flush { get; } public Action Flush { get; }
[GlEntryPoint("glFinish")]
public Action Finish { get; }
public delegate IntPtr GlGetString(int v); public delegate IntPtr GlGetString(int v);
[GlEntryPoint("glGetString")] [GlEntryPoint("glGetString")]
@ -144,6 +147,10 @@ namespace Avalonia.OpenGL
[GlEntryPoint("glBindTexture")] [GlEntryPoint("glBindTexture")]
public GlBindTexture BindTexture { get; } public GlBindTexture BindTexture { get; }
public delegate void GlActiveTexture(int texture);
[GlEntryPoint("glActiveTexture")]
public GlActiveTexture ActiveTexture { get; }
public delegate void GlDeleteTextures(int count, int[] textures); public delegate void GlDeleteTextures(int count, int[] textures);
[GlEntryPoint("glDeleteTextures")] [GlEntryPoint("glDeleteTextures")]
public GlDeleteTextures DeleteTextures { get; } public GlDeleteTextures DeleteTextures { get; }
@ -154,6 +161,12 @@ namespace Avalonia.OpenGL
[GlEntryPoint("glTexImage2D")] [GlEntryPoint("glTexImage2D")]
public GlTexImage2D TexImage2D { get; } public GlTexImage2D TexImage2D { get; }
public delegate void GlCopyTexSubImage2D(int target, int level, int xoffset, int yoffset, int x, int y,
int width, int height);
[GlEntryPoint("glCopyTexSubImage2D")]
public GlCopyTexSubImage2D CopyTexSubImage2D { get; }
public delegate void GlTexParameteri(int target, int name, int value); public delegate void GlTexParameteri(int target, int name, int value);
[GlEntryPoint("glTexParameteri")] [GlEntryPoint("glTexParameteri")]
public GlTexParameteri TexParameteri { get; } public GlTexParameteri TexParameteri { get; }

2
src/Avalonia.OpenGL/IGlContext.cs

@ -9,5 +9,7 @@ namespace Avalonia.OpenGL
int SampleCount { get; } int SampleCount { get; }
int StencilSize { get; } int StencilSize { get; }
IDisposable MakeCurrent(); IDisposable MakeCurrent();
IDisposable EnsureCurrent();
bool IsSharedWith(IGlContext context);
} }
} }

2
src/Avalonia.OpenGL/IOpenGlAwarePlatformRenderInterface.cs

@ -4,6 +4,6 @@ namespace Avalonia.OpenGL
{ {
public interface IOpenGlAwarePlatformRenderInterface public interface IOpenGlAwarePlatformRenderInterface
{ {
IOpenGlTextureBitmapImpl CreateOpenGlTextureBitmap(); IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi);
} }
} }

13
src/Avalonia.OpenGL/IPlatformOpenGlInterface.cs

@ -0,0 +1,13 @@
namespace Avalonia.OpenGL
{
public interface IPlatformOpenGlInterface
{
IGlContext PrimaryContext { get; }
IGlContext CreateSharedContext();
bool CanShareContexts { get; }
bool CanCreateContexts { get; }
IGlContext CreateContext();
/*IGlContext TryCreateContext(GlVersion version);
*/
}
}

8
src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs

@ -1,8 +0,0 @@
namespace Avalonia.OpenGL
{
public interface IWindowingPlatformGlFeature
{
IGlContext CreateContext();
IGlContext MainContext { get; }
}
}

17
src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs

@ -0,0 +1,17 @@
using System;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
namespace Avalonia.OpenGL.Imaging
{
public interface IOpenGlBitmapImpl : IBitmapImpl
{
IOpenGlBitmapAttachment CreateFramebufferAttachment(IGlContext context, Action presentCallback);
bool SupportsContext(IGlContext context);
}
public interface IOpenGlBitmapAttachment : IDisposable
{
void Present();
}
}

13
src/Avalonia.OpenGL/Imaging/IOpenGlTextureBitmapImpl.cs

@ -1,13 +0,0 @@
using System;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
namespace Avalonia.OpenGL.Imaging
{
public interface IOpenGlTextureBitmapImpl : IBitmapImpl
{
IDisposable Lock();
void SetBackBuffer(int textureId, int internalFormat, PixelSize pixelSize, double dpiScaling);
void SetDirty();
}
}

34
src/Avalonia.OpenGL/Imaging/OpenGlTextureBitmap.cs → src/Avalonia.OpenGL/Imaging/OpenGlBitmap.cs

@ -6,32 +6,30 @@ using Avalonia.Threading;
namespace Avalonia.OpenGL.Imaging namespace Avalonia.OpenGL.Imaging
{ {
public class OpenGlTextureBitmap : Bitmap, IAffectsRender public class OpenGlBitmap : Bitmap, IAffectsRender
{ {
private IOpenGlTextureBitmapImpl _impl; private IOpenGlBitmapImpl _impl;
static IOpenGlTextureBitmapImpl CreateOrThrow()
public OpenGlBitmap(PixelSize size, Vector dpi)
: base(CreateOrThrow(size, dpi))
{ {
if (!(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() is IOpenGlAwarePlatformRenderInterface _impl = (IOpenGlBitmapImpl)PlatformImpl.Item;
glAware))
throw new PlatformNotSupportedException("Rendering platform does not support OpenGL integration");
return glAware.CreateOpenGlTextureBitmap();
} }
public OpenGlTextureBitmap() static IOpenGlBitmapImpl CreateOrThrow(PixelSize size, Vector dpi)
: base(CreateOrThrow())
{ {
_impl = (IOpenGlTextureBitmapImpl)PlatformImpl.Item; if (!(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() is IOpenGlAwarePlatformRenderInterface
glAware))
throw new PlatformNotSupportedException("Rendering platform does not support OpenGL integration");
return glAware.CreateOpenGlBitmap(size, dpi);
} }
public IDisposable Lock() => _impl.Lock(); public IOpenGlBitmapAttachment CreateFramebufferAttachment(IGlContext context) =>
_impl.CreateFramebufferAttachment(context, SetIsDirty);
public void SetTexture(int textureId, int internalFormat, PixelSize size, double dpiScaling) public bool SupportsContext(IGlContext context) => _impl.SupportsContext(context);
{
_impl.SetBackBuffer(textureId, internalFormat, size, dpiScaling); void SetIsDirty()
SetIsDirty();
}
public void SetIsDirty()
{ {
if (Dispatcher.UIThread.CheckAccess()) if (Dispatcher.UIThread.CheckAccess())
CallInvalidated(); CallInvalidated();

1
src/Avalonia.OpenGL/OpenGlException.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.OpenGL.Egl;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL
{ {

2
src/Avalonia.OpenGL/IGlPlatformSurface.cs → src/Avalonia.OpenGL/Surfaces/IGlPlatformSurface.cs

@ -1,4 +1,4 @@
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Surfaces
{ {
public interface IGlPlatformSurface public interface IGlPlatformSurface
{ {

2
src/Avalonia.OpenGL/IGlPlatformSurfaceRenderTarget.cs → src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderTarget.cs

@ -1,6 +1,6 @@
using System; using System;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Surfaces
{ {
public interface IGlPlatformSurfaceRenderTarget : IDisposable public interface IGlPlatformSurfaceRenderTarget : IDisposable
{ {

2
src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs → src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderingSession.cs

@ -1,6 +1,6 @@
using System; using System;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Surfaces
{ {
public interface IGlPlatformSurfaceRenderingSession : IDisposable public interface IGlPlatformSurfaceRenderingSession : IDisposable
{ {

18
src/Avalonia.Styling/Controls/Metadata/PseudoClassesAttribute.cs

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
#nullable enable
namespace Avalonia.Controls.Metadata
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class PseudoClassesAttribute : Attribute
{
public PseudoClassesAttribute(params string[] pseudoClasses)
{
PseudoClasses = pseudoClasses;
}
public IReadOnlyList<string> PseudoClasses { get; }
}
}

3
src/Avalonia.Themes.Default/Accents/BaseDark.xaml

@ -72,5 +72,8 @@
<sys:Double x:Key="ScrollBarThickness">18</sys:Double> <sys:Double x:Key="ScrollBarThickness">18</sys:Double>
<sys:Double x:Key="ScrollBarThumbThickness">8</sys:Double> <sys:Double x:Key="ScrollBarThumbThickness">8</sys:Double>
<sys:Double x:Key="IconElementThemeHeight">20</sys:Double>
<sys:Double x:Key="IconElementThemeWidth">20</sys:Double>
</Style.Resources> </Style.Resources>
</Style> </Style>

3
src/Avalonia.Themes.Default/Accents/BaseLight.xaml

@ -75,5 +75,8 @@
<sys:Double x:Key="ScrollBarThickness">18</sys:Double> <sys:Double x:Key="ScrollBarThickness">18</sys:Double>
<sys:Double x:Key="ScrollBarThumbThickness">8</sys:Double> <sys:Double x:Key="ScrollBarThumbThickness">8</sys:Double>
<sys:Double x:Key="IconElementThemeHeight">20</sys:Double>
<sys:Double x:Key="IconElementThemeWidth">20</sys:Double>
</Style.Resources> </Style.Resources>
</Style> </Style>

3
src/Avalonia.Themes.Default/DefaultTheme.xaml

@ -12,7 +12,7 @@
<StyleInclude Source="resm:Avalonia.Themes.Default.CaptionButtons.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.CaptionButtons.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ComboBox.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.ComboBox.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ComboBoxItem.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.ComboBoxItem.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ContentControl.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.ContentControl.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.GridSplitter.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.GridSplitter.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ItemsControl.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.ItemsControl.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ListBox.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.ListBox.xaml?assembly=Avalonia.Themes.Default"/>
@ -21,6 +21,7 @@
<StyleInclude Source="resm:Avalonia.Themes.Default.ContextMenu.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.ContextMenu.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.MenuItem.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.MenuItem.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.OverlayPopupHost.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.OverlayPopupHost.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.PathIcon.xaml?assembly=Avalonia.Themes.Default" />
<StyleInclude Source="resm:Avalonia.Themes.Default.PopupRoot.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.PopupRoot.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ProgressBar.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.ProgressBar.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.RadioButton.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.RadioButton.xaml?assembly=Avalonia.Themes.Default"/>

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

@ -0,0 +1,17 @@
<Styles xmlns="https://github.com/avaloniaui"
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="Template">
<ControlTemplate>
<Viewbox Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}">
<Path Fill="{TemplateBinding Foreground}"
Data="{TemplateBinding Data}" />
</Viewbox>
</ControlTemplate>
</Setter>
</Style>
</Styles>

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

@ -20,5 +20,7 @@
<Thickness x:Key="TextControlBorderThemeThickness">1</Thickness> <Thickness x:Key="TextControlBorderThemeThickness">1</Thickness>
<Thickness x:Key="TextControlBorderThemeThicknessFocused">2</Thickness> <Thickness x:Key="TextControlBorderThemeThicknessFocused">2</Thickness>
<Thickness x:Key="TextControlThemePadding">10,6,6,5</Thickness> <Thickness x:Key="TextControlThemePadding">10,6,6,5</Thickness>
<sys:Double x:Key="IconElementThemeHeight">20</sys:Double>
<sys:Double x:Key="IconElementThemeWidth">20</sys:Double>
</Style.Resources> </Style.Resources>
</Style> </Style>

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

@ -77,7 +77,7 @@
<Setter Property="TextBlock.Foreground" Value="{DynamicResource AccentButtonForegroundPressed}" /> <Setter Property="TextBlock.Foreground" Value="{DynamicResource AccentButtonForegroundPressed}" />
</Style> </Style>
<Style Selector="Button"> <Style Selector="Button, RepeatButton, ToggleButton">
<Setter Property="RenderTransform" Value="none" /> <Setter Property="RenderTransform" Value="none" />
<Setter Property="Transitions"> <Setter Property="Transitions">
<Transitions> <Transitions>
@ -86,7 +86,7 @@
</Setter> </Setter>
</Style> </Style>
<Style Selector="Button:pressed"> <Style Selector="Button:pressed, RepeatButton:pressed, ToggleButton:pressed">
<Setter Property="RenderTransform" Value="scale(0.98)" /> <Setter Property="RenderTransform" Value="scale(0.98)" />
</Style> </Style>

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

@ -11,7 +11,7 @@
<StyleInclude Source="avares://Avalonia.Themes.Fluent/CheckBox.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/CheckBox.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/ComboBox.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/ComboBox.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/ComboBoxItem.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/ComboBoxItem.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/ContentControl.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/ContentControl.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/GridSplitter.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/GridSplitter.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/ItemsControl.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/ItemsControl.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/ListBox.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/ListBox.xaml"/>
@ -20,6 +20,7 @@
<StyleInclude Source="avares://Avalonia.Themes.Fluent/ContextMenu.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/ContextMenu.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/MenuItem.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/MenuItem.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/OverlayPopupHost.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/OverlayPopupHost.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/PathIcon.xaml" />
<StyleInclude Source="avares://Avalonia.Themes.Fluent/PopupRoot.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/PopupRoot.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/ProgressBar.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/ProgressBar.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/RadioButton.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/RadioButton.xaml"/>

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

@ -0,0 +1,25 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.PreviewWith>
<StackPanel>
<StackPanel.Resources>
<StreamGeometry x:Key="settings_regular">M14 9.50006C11.5147 9.50006 9.5 11.5148 9.5 14.0001C9.5 16.4853 11.5147 18.5001 14 18.5001C15.3488 18.5001 16.559 17.9066 17.3838 16.9666C18.0787 16.1746 18.5 15.1365 18.5 14.0001C18.5 13.5401 18.431 13.0963 18.3028 12.6784C17.7382 10.8381 16.0253 9.50006 14 9.50006ZM11 14.0001C11 12.3432 12.3431 11.0001 14 11.0001C15.6569 11.0001 17 12.3432 17 14.0001C17 15.6569 15.6569 17.0001 14 17.0001C12.3431 17.0001 11 15.6569 11 14.0001Z M21.7093 22.3948L19.9818 21.6364C19.4876 21.4197 18.9071 21.4515 18.44 21.7219C17.9729 21.9924 17.675 22.4693 17.6157 23.0066L17.408 24.8855C17.3651 25.273 17.084 25.5917 16.7055 25.682C14.9263 26.1061 13.0725 26.1061 11.2933 25.682C10.9148 25.5917 10.6336 25.273 10.5908 24.8855L10.3834 23.0093C10.3225 22.4731 10.0112 21.9976 9.54452 21.7281C9.07783 21.4586 8.51117 21.4269 8.01859 21.6424L6.29071 22.4009C5.93281 22.558 5.51493 22.4718 5.24806 22.1859C4.00474 20.8536 3.07924 19.2561 2.54122 17.5137C2.42533 17.1384 2.55922 16.7307 2.8749 16.4977L4.40219 15.3703C4.83721 15.0501 5.09414 14.5415 5.09414 14.0007C5.09414 13.4598 4.83721 12.9512 4.40162 12.6306L2.87529 11.5051C2.55914 11.272 2.42513 10.8638 2.54142 10.4882C3.08038 8.74734 4.00637 7.15163 5.24971 5.82114C5.51684 5.53528 5.93492 5.44941 6.29276 5.60691L8.01296 6.36404C8.50793 6.58168 9.07696 6.54881 9.54617 6.27415C10.0133 6.00264 10.3244 5.52527 10.3844 4.98794L10.5933 3.11017C10.637 2.71803 10.9245 2.39704 11.3089 2.31138C12.19 2.11504 13.0891 2.01071 14.0131 2.00006C14.9147 2.01047 15.8128 2.11485 16.6928 2.31149C17.077 2.39734 17.3643 2.71823 17.4079 3.11017L17.617 4.98937C17.7116 5.85221 18.4387 6.50572 19.3055 6.50663C19.5385 6.507 19.769 6.45838 19.9843 6.36294L21.7048 5.60568C22.0626 5.44818 22.4807 5.53405 22.7478 5.81991C23.9912 7.1504 24.9172 8.74611 25.4561 10.487C25.5723 10.8623 25.4386 11.2703 25.1228 11.5035L23.5978 12.6297C23.1628 12.95 22.9 13.4586 22.9 13.9994C22.9 14.5403 23.1628 15.0489 23.5988 15.3698L25.1251 16.4965C25.441 16.7296 25.5748 17.1376 25.4586 17.5131C24.9198 19.2536 23.9944 20.8492 22.7517 22.1799C22.4849 22.4657 22.0671 22.5518 21.7093 22.3948ZM16.263 22.1966C16.4982 21.4685 16.9889 20.8288 17.6884 20.4238C18.5702 19.9132 19.6536 19.8547 20.5841 20.2627L21.9281 20.8526C22.791 19.8538 23.4593 18.7013 23.8981 17.4552L22.7095 16.5778L22.7086 16.5771C21.898 15.98 21.4 15.0277 21.4 13.9994C21.4 12.9719 21.8974 12.0195 22.7073 11.4227L22.7085 11.4218L23.8957 10.545C23.4567 9.2988 22.7881 8.14636 21.9248 7.1477L20.5922 7.73425L20.5899 7.73527C20.1844 7.91463 19.7472 8.00722 19.3039 8.00663C17.6715 8.00453 16.3046 6.77431 16.1261 5.15465L16.1259 5.15291L15.9635 3.69304C15.3202 3.57328 14.6677 3.50872 14.013 3.50017C13.3389 3.50891 12.6821 3.57367 12.0377 3.69328L11.8751 5.15452C11.7625 6.16272 11.1793 7.05909 10.3019 7.56986C9.41937 8.0856 8.34453 8.14844 7.40869 7.73694L6.07273 7.14893C5.20949 8.14751 4.54092 9.29983 4.10196 10.5459L5.29181 11.4233C6.11115 12.0269 6.59414 12.9837 6.59414 14.0007C6.59414 15.0173 6.11142 15.9742 5.29237 16.5776L4.10161 17.4566C4.54002 18.7044 5.2085 19.8585 6.07205 20.8587L7.41742 20.2682C8.34745 19.8613 9.41573 19.9215 10.2947 20.4292C11.174 20.937 11.7593 21.832 11.8738 22.84L11.8744 22.8445L12.0362 24.3088C13.3326 24.5638 14.6662 24.5638 15.9626 24.3088L16.1247 22.8418C16.1491 22.6217 16.1955 22.4055 16.263 22.1966Z</StreamGeometry>
</StackPanel.Resources>
<PathIcon Data="{StaticResource settings_regular}" />
</StackPanel>
</Design.PreviewWith>
<Style Selector="PathIcon">
<Setter Property="Foreground" Value="{DynamicResource TextControlForeground}" />
<Setter Property="Height" Value="16" />
<Setter Property="Width" Value="16" />
<Setter Property="Template">
<ControlTemplate>
<Viewbox Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}">
<Path Fill="{TemplateBinding Foreground}"
Data="{TemplateBinding Data}" />
</Viewbox>
</ControlTemplate>
</Setter>
</Style>
</Styles>

20
src/Avalonia.Visuals/ApiCompatBaseline.txt

@ -1,15 +1,33 @@
Compat issues with assembly Avalonia.Visuals: Compat issues with assembly Avalonia.Visuals:
MembersMustExist : Member 'public void Avalonia.Media.DrawingContext.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.GetOrAddTypeface(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.GetOrAddTypeface(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.MatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.MatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.GlyphRun.Bounds.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<Avalonia.Point> Avalonia.StyledProperty<Avalonia.Point> Avalonia.Media.GlyphRunDrawing.BaselineOriginProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Point Avalonia.Media.GlyphRunDrawing.BaselineOrigin.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.GlyphRunDrawing.BaselineOrigin.set(Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
CannotSealType : Type 'Avalonia.Media.Typeface' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. CannotSealType : Type 'Avalonia.Media.Typeface' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
TypeCannotChangeClassification : Type 'Avalonia.Media.Typeface' is a 'struct' in the implementation but is a 'class' in the contract. TypeCannotChangeClassification : Type 'Avalonia.Media.Typeface' is a 'struct' in the implementation but is a 'class' in the contract.
CannotMakeMemberNonVirtual : Member 'public System.Boolean Avalonia.Media.Typeface.Equals(System.Object)' is non-virtual in the implementation but is virtual in the contract. CannotMakeMemberNonVirtual : Member 'public System.Boolean Avalonia.Media.Typeface.Equals(System.Object)' is non-virtual in the implementation but is virtual in the contract.
CannotMakeMemberNonVirtual : Member 'public System.Int32 Avalonia.Media.Typeface.GetHashCode()' is non-virtual in the implementation but is virtual in the contract. CannotMakeMemberNonVirtual : Member 'public System.Int32 Avalonia.Media.Typeface.GetHashCode()' is non-virtual in the implementation but is virtual in the contract.
TypesMustExist : Type 'Avalonia.Media.Fonts.FontKey' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Media.Fonts.FontKey' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.DrawableTextRun.Size' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.TextFormatting.DrawableTextRun.Bounds.get()' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.DrawableTextRun.Size.get()' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.TextFormatting.ShapedTextCharacters.Bounds.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextCharacters.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLayout.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak' is abstract in the implementation but is missing in the contract. CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak' is abstract in the implementation but is missing in the contract.
CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext)' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.LineBreak.get()' does not exist in the implementation but it does exist in the contract. 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. CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak.get()' is abstract in the implementation but is missing in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' is present in the contract but not in the implementation. 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. 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. InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract.
Total Issues: 13 Total Issues: 31

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

Loading…
Cancel
Save