diff --git a/Avalonia.sln b/Avalonia.sln
index 66777f33eb..ddcd61408d 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -222,9 +222,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.Vnc", "sr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.Loader", "src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj", "{909A8CBD-7D0E-42FD-B841-022AD8925820}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.ReactiveUI.Events", "src\Avalonia.ReactiveUI.Events\Avalonia.ReactiveUI.Events.csproj", "{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI.Events", "src\Avalonia.ReactiveUI.Events\Avalonia.ReactiveUI.Events.csproj", "{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.ReactiveUI.Events.UnitTests", "tests\Avalonia.ReactiveUI.Events.UnitTests\Avalonia.ReactiveUI.Events.UnitTests.csproj", "{780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI.Events.UnitTests", "tests\Avalonia.ReactiveUI.Events.UnitTests\Avalonia.ReactiveUI.Events.UnitTests.csproj", "{780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
@@ -2040,6 +2040,30 @@ Global
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhone.Build.0 = Release|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Release|iPhone.Build.0 = Release|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 29d2c62caf..721a0415f4 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -56,13 +56,6 @@ jobs:
xcodeVersion: '10' # Options: 8, 9, default, specifyPath
args: '-derivedDataPath ./'
- - task: CmdLine@2
- displayName: 'Install CastXML'
- inputs:
- script: |
- brew update
- brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/8a004a91a7fcd3f6620d5b01b6541ff0a640ffba/Formula/castxml.rb
-
- task: CmdLine@2
displayName: 'Install Nuke'
inputs:
diff --git a/build/NetFX.props b/build/NetFX.props
index ed5cb6dd69..8ffc9ec561 100644
--- a/build/NetFX.props
+++ b/build/NetFX.props
@@ -1,7 +1,7 @@
-
+
diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt
index 11708b360f..af88c569a6 100644
--- a/src/Avalonia.Controls/ApiCompatBaseline.txt
+++ b/src/Avalonia.Controls/ApiCompatBaseline.txt
@@ -12,7 +12,9 @@ MembersMustExist : Member 'public Avalonia.DirectProperty Avalonia.Interactivity.RoutedEvent Avalonia.Controls.TreeView.SelectionChangedEvent' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Controls.ISelectionModel Avalonia.Controls.TreeView.Selection.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.TreeView.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.String[] Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.Args' is present in the implementation but not in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.String[] Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.Args.get()' is present in the implementation but not in the contract.
MembersMustExist : Member 'public Avalonia.DirectProperty Avalonia.DirectProperty Avalonia.Controls.Primitives.SelectingItemsControl.SelectionProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected Avalonia.Controls.ISelectionModel Avalonia.Controls.Primitives.SelectingItemsControl.Selection.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.Primitives.SelectingItemsControl.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract.
-Total Issues: 16
+Total Issues: 18
diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
index 6dd5b8cc81..e2c8e7e8e2 100644
--- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
+++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
@@ -47,6 +47,11 @@ namespace Avalonia.Controls.ApplicationLifetimes
///
public event EventHandler Exit;
+ ///
+ /// Gets the arguments passed to the AppBuilder Start method.
+ ///
+ public string[] Args { get; set; }
+
///
public ShutdownMode ShutdownMode { get; set; }
@@ -68,9 +73,6 @@ namespace Avalonia.Controls.ApplicationLifetimes
else if (ShutdownMode == ShutdownMode.OnMainWindowClose && window == MainWindow)
Shutdown();
}
-
-
-
public void Shutdown(int exitCode = 0)
{
@@ -123,7 +125,11 @@ namespace Avalonia
this T builder, string[] args, ShutdownMode shutdownMode = ShutdownMode.OnLastWindowClose)
where T : AppBuilderBase, new()
{
- var lifetime = new ClassicDesktopStyleApplicationLifetime() {ShutdownMode = shutdownMode};
+ var lifetime = new ClassicDesktopStyleApplicationLifetime()
+ {
+ Args = args,
+ ShutdownMode = shutdownMode
+ };
builder.SetupWithLifetime(lifetime);
return lifetime.Start(args);
}
diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs
index a1006d907b..212f0b8617 100644
--- a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs
+++ b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs
@@ -8,6 +8,13 @@ namespace Avalonia.Controls.ApplicationLifetimes
///
public interface IClassicDesktopStyleApplicationLifetime : IControlledApplicationLifetime
{
+ ///
+ /// Gets the arguments passed to the
+ ///
+ /// method.
+ ///
+ string[] Args { get; }
+
///
/// Gets or sets the . This property indicates whether the application is shutdown explicitly or implicitly.
/// If is set to OnExplicitShutdown the application is only closes if Shutdown is called.
diff --git a/src/Avalonia.Controls/ColumnDefinitions.cs b/src/Avalonia.Controls/ColumnDefinitions.cs
index ed4f9dbe99..7e355ab357 100644
--- a/src/Avalonia.Controls/ColumnDefinitions.cs
+++ b/src/Avalonia.Controls/ColumnDefinitions.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Specialized;
using System.Linq;
+using System.Text;
using Avalonia.Collections;
namespace Avalonia.Controls
@@ -13,7 +14,7 @@ namespace Avalonia.Controls
///
/// Initializes a new instance of the class.
///
- public ColumnDefinitions() : base ()
+ public ColumnDefinitions()
{
}
@@ -27,6 +28,11 @@ namespace Avalonia.Controls
AddRange(GridLength.ParseLengths(s).Select(x => new ColumnDefinition(x)));
}
+ public override string ToString()
+ {
+ return string.Join(",", this.Select(x => x.Width));
+ }
+
///
/// Parses a string representation of column definitions collection.
///
@@ -34,4 +40,4 @@ namespace Avalonia.Controls
/// The .
public static ColumnDefinitions Parse(string s) => new ColumnDefinitions(s);
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs
index 37490c3ef3..cb7bee1d33 100644
--- a/src/Avalonia.Controls/Presenters/TextPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs
@@ -282,7 +282,7 @@ namespace Avalonia.Controls.Presenters
return new FormattedText
{
Constraint = constraint,
- Typeface = FontManager.Current?.GetOrAddTypeface(FontFamily, FontStyle, FontWeight),
+ Typeface = new Typeface(FontFamily, FontStyle, FontWeight),
FontSize = FontSize,
Text = text ?? string.Empty,
TextAlignment = TextAlignment,
@@ -499,7 +499,7 @@ namespace Avalonia.Controls.Presenters
return new FormattedText
{
Text = "X",
- Typeface = FontManager.Current?.GetOrAddTypeface(FontFamily, FontStyle, FontWeight),
+ Typeface = new Typeface(FontFamily, FontStyle, FontWeight),
FontSize = FontSize,
TextAlignment = TextAlignment,
Constraint = availableSize,
diff --git a/src/Avalonia.Controls/Primitives/ScrollBar.cs b/src/Avalonia.Controls/Primitives/ScrollBar.cs
index fc82fcc7a7..477d24dc99 100644
--- a/src/Avalonia.Controls/Primitives/ScrollBar.cs
+++ b/src/Avalonia.Controls/Primitives/ScrollBar.cs
@@ -141,7 +141,7 @@ namespace Avalonia.Controls.Primitives
_ => throw new InvalidOperationException("Invalid value for ScrollBar.Visibility.")
};
- SetValue(IsVisibleProperty, isVisible, BindingPriority.Style);
+ SetValue(IsVisibleProperty, isVisible);
}
protected override void OnKeyDown(KeyEventArgs e)
diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs
index 8bc356bdec..40f1b8dbb9 100644
--- a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs
+++ b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs
@@ -7,10 +7,10 @@ using System;
using System.Collections;
using System.Collections.Specialized;
using Avalonia.Controls.Templates;
-using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Logging;
+using Avalonia.Utilities;
using Avalonia.VisualTree;
namespace Avalonia.Controls
@@ -681,8 +681,15 @@ namespace Avalonia.Controls
if (oldValue != null)
{
oldValue.UninitializeForContext(LayoutContext);
- oldValue.MeasureInvalidated -= InvalidateMeasureForLayout;
- oldValue.ArrangeInvalidated -= InvalidateArrangeForLayout;
+
+ WeakEventHandlerManager.Unsubscribe(
+ oldValue,
+ nameof(AttachedLayout.MeasureInvalidated),
+ InvalidateMeasureForLayout);
+ WeakEventHandlerManager.Unsubscribe(
+ oldValue,
+ nameof(AttachedLayout.ArrangeInvalidated),
+ InvalidateArrangeForLayout);
// Walk through all the elements and make sure they are cleared
foreach (var element in Children)
@@ -699,8 +706,15 @@ namespace Avalonia.Controls
if (newValue != null)
{
newValue.InitializeForContext(LayoutContext);
- newValue.MeasureInvalidated += InvalidateMeasureForLayout;
- newValue.ArrangeInvalidated += InvalidateArrangeForLayout;
+
+ WeakEventHandlerManager.Subscribe(
+ newValue,
+ nameof(AttachedLayout.MeasureInvalidated),
+ InvalidateMeasureForLayout);
+ WeakEventHandlerManager.Subscribe(
+ newValue,
+ nameof(AttachedLayout.ArrangeInvalidated),
+ InvalidateArrangeForLayout);
}
bool isVirtualizingLayout = newValue != null && newValue is VirtualizingLayout;
diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs
index 7e5287f81f..3b9e9c4751 100644
--- a/src/Avalonia.Controls/TextBlock.cs
+++ b/src/Avalonia.Controls/TextBlock.cs
@@ -452,7 +452,7 @@ namespace Avalonia.Controls
return new TextLayout(
text ?? string.Empty,
- FontManager.Current?.GetOrAddTypeface(FontFamily, FontStyle, FontWeight),
+ new Typeface(FontFamily, FontStyle, FontWeight),
FontSize,
Foreground,
TextAlignment,
diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs
index 55e2df8890..03dcf85b87 100644
--- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs
+++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs
@@ -38,11 +38,13 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport
public HtmlWebSocketTransport(IAvaloniaRemoteTransportConnection signalTransport, Uri listenUri)
{
if (listenUri.Scheme != "http")
- throw new ArgumentException("listenUri");
+ throw new ArgumentException("URI scheme is not HTTP.", nameof(listenUri));
var resourcePrefix = "Avalonia.DesignerSupport.Remote.HtmlTransport.webapp.build.";
_resources = typeof(HtmlWebSocketTransport).Assembly.GetManifestResourceNames()
- .Where(r => r.StartsWith(resourcePrefix) && r.EndsWith(".gz")).ToDictionary(
+ .Where(r => r.StartsWith(resourcePrefix, StringComparison.OrdinalIgnoreCase)
+ && r.EndsWith(".gz", StringComparison.OrdinalIgnoreCase))
+ .ToDictionary(
r => r.Substring(resourcePrefix.Length).Substring(0,r.Length-resourcePrefix.Length-3),
r =>
{
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs
index ddbdae7ed9..e23d6f1471 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs
@@ -8,8 +8,8 @@ namespace Avalonia.Diagnostics.ViewModels
internal abstract class PropertyViewModel : ViewModelBase
{
private const BindingFlags PublicStatic = BindingFlags.Public | BindingFlags.Static;
- private static readonly Type[] StringParameter = new[] { typeof(string) };
- private static readonly Type[] StringIFormatProviderParameters = new[] { typeof(string), typeof(IFormatProvider) };
+ private static readonly Type[] StringParameter = { typeof(string) };
+ private static readonly Type[] StringIFormatProviderParameters = { typeof(string), typeof(IFormatProvider) };
public abstract object Key { get; }
public abstract string Name { get; }
@@ -26,35 +26,46 @@ namespace Avalonia.Diagnostics.ViewModels
}
var converter = TypeDescriptor.GetConverter(value);
- return converter?.ConvertToString(value) ?? value.ToString();
+
+ //CollectionConverter does not deliver any important information. It just displays "(Collection)".
+ if (!converter.CanConvertTo(typeof(string)) ||
+ converter.GetType() == typeof(CollectionConverter))
+ {
+ return value.ToString();
+ }
+
+ return converter.ConvertToString(value);
}
- protected static object ConvertFromString(string s, Type targetType)
+ private static object InvokeParse(string s, Type targetType)
{
- var converter = TypeDescriptor.GetConverter(targetType);
-
- if (converter != null && converter.CanConvertFrom(typeof(string)))
+ var method = targetType.GetMethod("Parse", PublicStatic, null, StringIFormatProviderParameters, null);
+
+ if (method != null)
{
- return converter.ConvertFrom(null, CultureInfo.InvariantCulture, s);
+ return method.Invoke(null, new object[] { s, CultureInfo.InvariantCulture });
}
- else
+
+ method = targetType.GetMethod("Parse", PublicStatic, null, StringParameter, null);
+
+ if (method != null)
{
- var method = targetType.GetMethod("Parse", PublicStatic, null, StringIFormatProviderParameters, null);
+ return method.Invoke(null, new object[] { s });
+ }
- if (method != null)
- {
- return method.Invoke(null, new object[] { s, CultureInfo.InvariantCulture });
- }
+ throw new InvalidCastException("Unable to convert value.");
+ }
- method = targetType.GetMethod("Parse", PublicStatic, null, StringParameter, null);
+ protected static object ConvertFromString(string s, Type targetType)
+ {
+ var converter = TypeDescriptor.GetConverter(targetType);
- if (method != null)
- {
- return method.Invoke(null, new object[] { s });
- }
+ if (converter.CanConvertFrom(typeof(string)))
+ {
+ return converter.ConvertFrom(null, CultureInfo.InvariantCulture, s);
}
- throw new InvalidCastException("Unable to convert value.");
+ return InvokeParse(s, targetType);
}
}
}
diff --git a/src/Avalonia.Headless/HeadlessPlatformStubs.cs b/src/Avalonia.Headless/HeadlessPlatformStubs.cs
index 763d192693..4c0e2982f4 100644
--- a/src/Avalonia.Headless/HeadlessPlatformStubs.cs
+++ b/src/Avalonia.Headless/HeadlessPlatformStubs.cs
@@ -155,9 +155,9 @@ namespace Avalonia.Headless
return new List { "Arial" };
}
- public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily, CultureInfo culture, out FontKey fontKey)
+ public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily, CultureInfo culture, out Typeface typeface)
{
- fontKey = new FontKey("Arial", fontStyle, fontWeight);
+ typeface = new Typeface("Arial", fontStyle, fontWeight);
return true;
}
}
diff --git a/src/Avalonia.Input/AccessKeyHandler.cs b/src/Avalonia.Input/AccessKeyHandler.cs
index 96f0bb59b3..660584e2ed 100644
--- a/src/Avalonia.Input/AccessKeyHandler.cs
+++ b/src/Avalonia.Input/AccessKeyHandler.cs
@@ -28,7 +28,7 @@ namespace Avalonia.Input
///
/// The window to which the handler belongs.
///
- private IInputRoot _owner;
+ private IInputRoot? _owner;
///
/// Whether access keys are currently being shown;
@@ -48,17 +48,17 @@ namespace Avalonia.Input
///
/// Element to restore following AltKey taking focus.
///
- private IInputElement _restoreFocusElement;
+ private IInputElement? _restoreFocusElement;
///
/// The window's main menu.
///
- private IMainMenu _mainMenu;
+ private IMainMenu? _mainMenu;
///
/// Gets or sets the window's main menu.
///
- public IMainMenu MainMenu
+ public IMainMenu? MainMenu
{
get => _mainMenu;
set
@@ -86,14 +86,12 @@ namespace Avalonia.Input
///
public void SetOwner(IInputRoot owner)
{
- Contract.Requires(owner != null);
-
if (_owner != null)
{
throw new InvalidOperationException("AccessKeyHandler owner has already been set.");
}
- _owner = owner;
+ _owner = owner ?? throw new ArgumentNullException(nameof(owner));
_owner.AddHandler(InputElement.KeyDownEvent, OnPreviewKeyDown, RoutingStrategies.Tunnel);
_owner.AddHandler(InputElement.KeyDownEvent, OnKeyDown, RoutingStrategies.Bubble);
@@ -149,7 +147,7 @@ namespace Avalonia.Input
// When Alt is pressed without a main menu, or with a closed main menu, show
// access key markers in the window (i.e. "_File").
- _owner.ShowAccessKeys = _showingAccessKeys = true;
+ _owner!.ShowAccessKeys = _showingAccessKeys = true;
}
else
{
@@ -241,7 +239,7 @@ namespace Avalonia.Input
{
if (_showingAccessKeys)
{
- _owner.ShowAccessKeys = false;
+ _owner!.ShowAccessKeys = false;
}
}
@@ -250,13 +248,13 @@ namespace Avalonia.Input
///
private void CloseMenu()
{
- MainMenu.Close();
- _owner.ShowAccessKeys = _showingAccessKeys = false;
+ MainMenu!.Close();
+ _owner!.ShowAccessKeys = _showingAccessKeys = false;
}
private void MainMenuClosed(object sender, EventArgs e)
{
- _owner.ShowAccessKeys = false;
+ _owner!.ShowAccessKeys = false;
}
}
}
diff --git a/src/Avalonia.Input/Avalonia.Input.csproj b/src/Avalonia.Input/Avalonia.Input.csproj
index 2204778afe..c39c81a965 100644
--- a/src/Avalonia.Input/Avalonia.Input.csproj
+++ b/src/Avalonia.Input/Avalonia.Input.csproj
@@ -1,6 +1,8 @@
netstandard2.0
+ Enable
+ CS8600;CS8602;CS8603
diff --git a/src/Avalonia.Input/DataObject.cs b/src/Avalonia.Input/DataObject.cs
index 60d7d67606..688f5f9cc8 100644
--- a/src/Avalonia.Input/DataObject.cs
+++ b/src/Avalonia.Input/DataObject.cs
@@ -11,7 +11,7 @@ namespace Avalonia.Input
return _items.ContainsKey(dataFormat);
}
- public object Get(string dataFormat)
+ public object? Get(string dataFormat)
{
if (_items.ContainsKey(dataFormat))
return _items[dataFormat];
@@ -23,12 +23,12 @@ namespace Avalonia.Input
return _items.Keys;
}
- public IEnumerable GetFileNames()
+ public IEnumerable? GetFileNames()
{
return Get(DataFormats.FileNames) as IEnumerable;
}
- public string GetText()
+ public string? GetText()
{
return Get(DataFormats.Text) as string;
}
diff --git a/src/Avalonia.Input/DragDropDevice.cs b/src/Avalonia.Input/DragDropDevice.cs
index bcd962bc31..30a08eda17 100644
--- a/src/Avalonia.Input/DragDropDevice.cs
+++ b/src/Avalonia.Input/DragDropDevice.cs
@@ -9,9 +9,9 @@ namespace Avalonia.Input
{
public static readonly DragDropDevice Instance = new DragDropDevice();
- private Interactive _lastTarget = null;
+ private Interactive? _lastTarget = null;
- private Interactive GetTarget(IInputRoot root, Point local)
+ private Interactive? GetTarget(IInputRoot root, Point local)
{
var target = root.InputHitTest(local)?.GetSelfAndVisualAncestors()?.OfType()?.FirstOrDefault();
if (target != null && DragDrop.GetAllowDrop(target))
@@ -19,7 +19,7 @@ namespace Avalonia.Input
return null;
}
- private DragDropEffects RaiseDragEvent(Interactive target, IInputRoot inputRoot, Point point, RoutedEvent routedEvent, DragDropEffects operation, IDataObject data, KeyModifiers modifiers)
+ private DragDropEffects RaiseDragEvent(Interactive? target, IInputRoot inputRoot, Point point, RoutedEvent routedEvent, DragDropEffects operation, IDataObject data, KeyModifiers modifiers)
{
if (target == null)
return DragDropEffects.None;
diff --git a/src/Avalonia.Input/FocusManager.cs b/src/Avalonia.Input/FocusManager.cs
index 66355da8b9..a1f1478f51 100644
--- a/src/Avalonia.Input/FocusManager.cs
+++ b/src/Avalonia.Input/FocusManager.cs
@@ -15,8 +15,8 @@ namespace Avalonia.Input
///
/// The focus scopes in which the focus is currently defined.
///
- private readonly ConditionalWeakTable _focusScopes =
- new ConditionalWeakTable();
+ private readonly ConditionalWeakTable _focusScopes =
+ new ConditionalWeakTable();
///
/// Initializes a new instance of the class.
@@ -37,12 +37,12 @@ namespace Avalonia.Input
///
/// Gets the currently focused .
///
- public IInputElement Current => KeyboardDevice.Instance?.FocusedElement;
+ public IInputElement? Current => KeyboardDevice.Instance?.FocusedElement;
///
/// Gets the current focus scope.
///
- public IFocusScope Scope
+ public IFocusScope? Scope
{
get;
private set;
@@ -55,7 +55,7 @@ namespace Avalonia.Input
/// The method by which focus was changed.
/// Any key modifiers active at the time of focus.
public void Focus(
- IInputElement control,
+ IInputElement? control,
NavigationMethod method = NavigationMethod.Unspecified,
KeyModifiers keyModifiers = KeyModifiers.None)
{
@@ -75,17 +75,18 @@ namespace Avalonia.Input
// If control is null, set focus to the topmost focus scope.
foreach (var scope in GetFocusScopeAncestors(Current).Reverse().ToList())
{
- IInputElement element;
-
- if (_focusScopes.TryGetValue(scope, out element) && element != null)
+ if (_focusScopes.TryGetValue(scope, out var element) && element != null)
{
Focus(element, method);
return;
}
}
- // Couldn't find a focus scope, clear focus.
- SetFocusedElement(Scope, null);
+ if (Scope is object)
+ {
+ // Couldn't find a focus scope, clear focus.
+ SetFocusedElement(Scope, null);
+ }
}
}
@@ -102,13 +103,13 @@ namespace Avalonia.Input
///
public void SetFocusedElement(
IFocusScope scope,
- IInputElement element,
+ IInputElement? element,
NavigationMethod method = NavigationMethod.Unspecified,
KeyModifiers keyModifiers = KeyModifiers.None)
{
- Contract.Requires(scope != null);
+ scope = scope ?? throw new ArgumentNullException(nameof(scope));
- if (_focusScopes.TryGetValue(scope, out IInputElement existingElement))
+ if (_focusScopes.TryGetValue(scope, out var existingElement))
{
if (element != existingElement)
{
@@ -133,11 +134,9 @@ namespace Avalonia.Input
/// The new focus scope.
public void SetFocusScope(IFocusScope scope)
{
- Contract.Requires(scope != null);
+ scope = scope ?? throw new ArgumentNullException(nameof(scope));
- IInputElement e;
-
- if (!_focusScopes.TryGetValue(scope, out e))
+ if (!_focusScopes.TryGetValue(scope, out var e))
{
// TODO: Make this do something useful, i.e. select the first focusable
// control, select a control that the user has specified to have default
@@ -164,17 +163,19 @@ namespace Avalonia.Input
/// The focus scopes.
private static IEnumerable GetFocusScopeAncestors(IInputElement control)
{
- while (control != null)
+ IInputElement? c = control;
+
+ while (c != null)
{
- var scope = control as IFocusScope;
+ var scope = c as IFocusScope;
- if (scope != null && control.VisualRoot?.IsVisible == true)
+ if (scope != null && c.VisualRoot?.IsVisible == true)
{
yield return scope;
}
- control = control.GetVisualParent() ??
- ((control as IHostedVisualTreeRoot)?.Host as IInputElement);
+ c = c.GetVisualParent() ??
+ ((c as IHostedVisualTreeRoot)?.Host as IInputElement);
}
}
@@ -190,7 +191,7 @@ namespace Avalonia.Input
if (sender == e.Source && ev.GetCurrentPoint(visual).Properties.IsLeftButtonPressed)
{
- IVisual element = ev.Pointer?.Captured ?? e.Source as IInputElement;
+ IVisual? element = ev.Pointer?.Captured ?? e.Source as IInputElement;
while (element != null)
{
diff --git a/src/Avalonia.Input/GestureRecognizers/GestureRecognizerCollection.cs b/src/Avalonia.Input/GestureRecognizers/GestureRecognizerCollection.cs
index 112abb1a4e..54ef0b1a68 100644
--- a/src/Avalonia.Input/GestureRecognizers/GestureRecognizerCollection.cs
+++ b/src/Avalonia.Input/GestureRecognizers/GestureRecognizerCollection.cs
@@ -1,8 +1,6 @@
-using System;
using System.Collections;
using System.Collections.Generic;
using Avalonia.Controls;
-using Avalonia.Data;
using Avalonia.LogicalTree;
using Avalonia.Styling;
@@ -11,8 +9,8 @@ namespace Avalonia.Input.GestureRecognizers
public class GestureRecognizerCollection : IReadOnlyCollection, IGestureRecognizerActionsDispatcher
{
private readonly IInputElement _inputElement;
- private List _recognizers;
- private Dictionary _pointerGrabs;
+ private List? _recognizers;
+ private Dictionary? _pointerGrabs;
public GestureRecognizerCollection(IInputElement inputElement)
@@ -72,7 +70,7 @@ namespace Avalonia.Input.GestureRecognizers
{
if (_recognizers == null)
return false;
- if (_pointerGrabs.TryGetValue(e.Pointer, out var capture))
+ if (_pointerGrabs!.TryGetValue(e.Pointer, out var capture))
{
capture.PointerReleased(e);
}
@@ -90,7 +88,7 @@ namespace Avalonia.Input.GestureRecognizers
{
if (_recognizers == null)
return false;
- if (_pointerGrabs.TryGetValue(e.Pointer, out var capture))
+ if (_pointerGrabs!.TryGetValue(e.Pointer, out var capture))
{
capture.PointerMoved(e);
}
@@ -108,7 +106,7 @@ namespace Avalonia.Input.GestureRecognizers
{
if (_recognizers == null)
return;
- _pointerGrabs.Remove(e.Pointer);
+ _pointerGrabs!.Remove(e.Pointer);
foreach (var r in _recognizers)
{
r.PointerCaptureLost(e.Pointer);
@@ -118,8 +116,8 @@ namespace Avalonia.Input.GestureRecognizers
void IGestureRecognizerActionsDispatcher.Capture(IPointer pointer, IGestureRecognizer recognizer)
{
pointer.Capture(_inputElement);
- _pointerGrabs[pointer] = recognizer;
- foreach (var r in _recognizers)
+ _pointerGrabs![pointer] = recognizer;
+ foreach (var r in _recognizers!)
{
if (r != recognizer)
r.PointerCaptureLost(pointer);
diff --git a/src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs
index e022401c8e..3858cc04f2 100644
--- a/src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs
+++ b/src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs
@@ -1,6 +1,5 @@
using System;
using System.Diagnostics;
-using Avalonia.Interactivity;
using Avalonia.Threading;
namespace Avalonia.Input.GestureRecognizers
@@ -11,9 +10,9 @@ namespace Avalonia.Input.GestureRecognizers
{
private bool _scrolling;
private Point _trackedRootPoint;
- private IPointer _tracking;
- private IInputElement _target;
- private IGestureRecognizerActionsDispatcher _actions;
+ private IPointer? _tracking;
+ private IInputElement? _target;
+ private IGestureRecognizerActionsDispatcher? _actions;
private bool _canHorizontallyScroll;
private bool _canVerticallyScroll;
private int _gestureId;
@@ -95,7 +94,7 @@ namespace Avalonia.Input.GestureRecognizers
_scrolling = true;
if (_scrolling)
{
- _actions.Capture(e.Pointer, this);
+ _actions!.Capture(e.Pointer, this);
}
}
@@ -110,7 +109,7 @@ namespace Avalonia.Input.GestureRecognizers
_trackedRootPoint = rootPoint;
if (elapsed.TotalSeconds > 0)
_inertia = vector / elapsed.TotalSeconds;
- _target.RaiseEvent(new ScrollGestureEventArgs(_gestureId, vector));
+ _target!.RaiseEvent(new ScrollGestureEventArgs(_gestureId, vector));
e.Handled = true;
}
}
@@ -128,7 +127,7 @@ namespace Avalonia.Input.GestureRecognizers
{
_inertia = default;
_scrolling = false;
- _target.RaiseEvent(new ScrollGestureEndedEventArgs(_gestureId));
+ _target!.RaiseEvent(new ScrollGestureEndedEventArgs(_gestureId));
_gestureId = 0;
_lastMoveTimestamp = null;
}
@@ -165,7 +164,7 @@ namespace Avalonia.Input.GestureRecognizers
var speed = _inertia * Math.Pow(0.15, st.Elapsed.TotalSeconds);
var distance = speed * elapsedSinceLastTick.TotalSeconds;
- _target.RaiseEvent(new ScrollGestureEventArgs(_gestureId, distance));
+ _target!.RaiseEvent(new ScrollGestureEventArgs(_gestureId, distance));
diff --git a/src/Avalonia.Input/Gestures.cs b/src/Avalonia.Input/Gestures.cs
index cdfcd94692..1be2595ebe 100644
--- a/src/Avalonia.Input/Gestures.cs
+++ b/src/Avalonia.Input/Gestures.cs
@@ -29,7 +29,9 @@ namespace Avalonia.Input
RoutedEvent.Register(
"ScrollGestureEnded", RoutingStrategies.Bubble, typeof(Gestures));
+#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
private static WeakReference s_lastPress = new WeakReference(null);
+#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
static Gestures()
{
@@ -69,6 +71,11 @@ namespace Avalonia.Input
private static void PointerPressed(RoutedEventArgs ev)
{
+ if (ev.Source is null)
+ {
+ return;
+ }
+
if (ev.Route == RoutingStrategies.Bubble)
{
var e = (PointerPressedEventArgs)ev;
@@ -76,7 +83,7 @@ namespace Avalonia.Input
if (e.ClickCount <= 1)
{
- s_lastPress = new WeakReference(e.Source);
+ s_lastPress = new WeakReference(ev.Source);
}
else if (s_lastPress != null && e.ClickCount == 2 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed)
{
diff --git a/src/Avalonia.Input/IAccessKeyHandler.cs b/src/Avalonia.Input/IAccessKeyHandler.cs
index 3e6510320f..e484d003c7 100644
--- a/src/Avalonia.Input/IAccessKeyHandler.cs
+++ b/src/Avalonia.Input/IAccessKeyHandler.cs
@@ -8,7 +8,7 @@ namespace Avalonia.Input
///
/// Gets or sets the window's main menu.
///
- IMainMenu MainMenu { get; set; }
+ IMainMenu? MainMenu { get; set; }
///
/// Sets the owner of the access key handler.
diff --git a/src/Avalonia.Input/IDataObject.cs b/src/Avalonia.Input/IDataObject.cs
index 1aa8fd63d5..1db008aa3a 100644
--- a/src/Avalonia.Input/IDataObject.cs
+++ b/src/Avalonia.Input/IDataObject.cs
@@ -23,17 +23,17 @@ namespace Avalonia.Input
/// Returns the dragged text if the DataObject contains any text.
///
///
- string GetText();
+ string? GetText();
///
/// Returns a list of filenames if the DataObject contains filenames.
///
///
- IEnumerable GetFileNames();
+ IEnumerable? GetFileNames();
///
/// Tries to get the data of the given DataFormat.
///
- object Get(string dataFormat);
+ object? Get(string dataFormat);
}
}
diff --git a/src/Avalonia.Input/IFocusManager.cs b/src/Avalonia.Input/IFocusManager.cs
index 9122cc428d..e1b5087c3d 100644
--- a/src/Avalonia.Input/IFocusManager.cs
+++ b/src/Avalonia.Input/IFocusManager.cs
@@ -8,12 +8,12 @@ namespace Avalonia.Input
///
/// Gets the currently focused .
///
- IInputElement Current { get; }
+ IInputElement? Current { get; }
///
/// Gets the current focus scope.
///
- IFocusScope Scope { get; }
+ IFocusScope? Scope { get; }
///
/// Focuses a control.
@@ -22,7 +22,7 @@ namespace Avalonia.Input
/// The method by which focus was changed.
/// Any key modifiers active at the time of focus.
void Focus(
- IInputElement control,
+ IInputElement? control,
NavigationMethod method = NavigationMethod.Unspecified,
KeyModifiers keyModifiers = KeyModifiers.None);
diff --git a/src/Avalonia.Input/IInputElement.cs b/src/Avalonia.Input/IInputElement.cs
index c30d74c965..12fec82368 100644
--- a/src/Avalonia.Input/IInputElement.cs
+++ b/src/Avalonia.Input/IInputElement.cs
@@ -78,7 +78,7 @@ namespace Avalonia.Input
///
/// Gets or sets the associated mouse cursor.
///
- Cursor Cursor { get; }
+ Cursor? Cursor { get; }
///
/// Gets a value indicating whether this control and all its parents are enabled.
diff --git a/src/Avalonia.Input/IInputRoot.cs b/src/Avalonia.Input/IInputRoot.cs
index eeb7e4323e..3e2b8cc477 100644
--- a/src/Avalonia.Input/IInputRoot.cs
+++ b/src/Avalonia.Input/IInputRoot.cs
@@ -20,7 +20,7 @@ namespace Avalonia.Input
///
/// Gets or sets the input element that the pointer is currently over.
///
- IInputElement PointerOverElement { get; set; }
+ IInputElement? PointerOverElement { get; set; }
///
/// Gets or sets a value indicating whether access keys are shown in the window.
@@ -31,6 +31,6 @@ namespace Avalonia.Input
/// Gets associated mouse device
///
[CanBeNull]
- IMouseDevice MouseDevice { get; }
+ IMouseDevice? MouseDevice { get; }
}
}
diff --git a/src/Avalonia.Input/IKeyboardDevice.cs b/src/Avalonia.Input/IKeyboardDevice.cs
index ba7e0484ee..9506dc36fb 100644
--- a/src/Avalonia.Input/IKeyboardDevice.cs
+++ b/src/Avalonia.Input/IKeyboardDevice.cs
@@ -58,10 +58,10 @@ namespace Avalonia.Input
public interface IKeyboardDevice : IInputDevice, INotifyPropertyChanged
{
- IInputElement FocusedElement { get; }
+ IInputElement? FocusedElement { get; }
void SetFocusedElement(
- IInputElement element,
+ IInputElement? element,
NavigationMethod method,
KeyModifiers modifiers);
}
diff --git a/src/Avalonia.Input/IPointer.cs b/src/Avalonia.Input/IPointer.cs
index a3f051ce7f..7af48cef82 100644
--- a/src/Avalonia.Input/IPointer.cs
+++ b/src/Avalonia.Input/IPointer.cs
@@ -3,8 +3,8 @@ namespace Avalonia.Input
public interface IPointer
{
int Id { get; }
- void Capture(IInputElement control);
- IInputElement Captured { get; }
+ void Capture(IInputElement? control);
+ IInputElement? Captured { get; }
PointerType Type { get; }
bool IsPrimary { get; }
diff --git a/src/Avalonia.Input/IPointerDevice.cs b/src/Avalonia.Input/IPointerDevice.cs
index bf001dda15..1f82cb1ed7 100644
--- a/src/Avalonia.Input/IPointerDevice.cs
+++ b/src/Avalonia.Input/IPointerDevice.cs
@@ -6,10 +6,10 @@ namespace Avalonia.Input
public interface IPointerDevice : IInputDevice
{
[Obsolete("Use IPointer")]
- IInputElement Captured { get; }
+ IInputElement? Captured { get; }
[Obsolete("Use IPointer")]
- void Capture(IInputElement control);
+ void Capture(IInputElement? control);
[Obsolete("Use PointerEventArgs.GetPosition")]
Point GetPosition(IVisual relativeTo);
diff --git a/src/Avalonia.Input/InputElement.cs b/src/Avalonia.Input/InputElement.cs
index 0616c70d82..25f2d553d7 100644
--- a/src/Avalonia.Input/InputElement.cs
+++ b/src/Avalonia.Input/InputElement.cs
@@ -37,8 +37,8 @@ namespace Avalonia.Input
///
/// Gets or sets associated mouse cursor.
///
- public static readonly StyledProperty CursorProperty =
- AvaloniaProperty.Register(nameof(Cursor), null, true);
+ public static readonly StyledProperty CursorProperty =
+ AvaloniaProperty.Register(nameof(Cursor), null, true);
///
/// Defines the property.
@@ -160,7 +160,7 @@ namespace Avalonia.Input
private bool _isFocused;
private bool _isFocusVisible;
private bool _isPointerOver;
- private GestureRecognizerCollection _gestureRecognizers;
+ private GestureRecognizerCollection? _gestureRecognizers;
///
/// Initializes static members of the class.
@@ -336,7 +336,7 @@ namespace Avalonia.Input
///
/// Gets or sets associated mouse cursor.
///
- public Cursor Cursor
+ public Cursor? Cursor
{
get { return GetValue(CursorProperty); }
set { SetValue(CursorProperty, value); }
diff --git a/src/Avalonia.Input/KeyEventArgs.cs b/src/Avalonia.Input/KeyEventArgs.cs
index 267376262b..67cd5a520a 100644
--- a/src/Avalonia.Input/KeyEventArgs.cs
+++ b/src/Avalonia.Input/KeyEventArgs.cs
@@ -5,7 +5,7 @@ namespace Avalonia.Input
{
public class KeyEventArgs : RoutedEventArgs
{
- public IKeyboardDevice Device { get; set; }
+ public IKeyboardDevice? Device { get; set; }
public Key Key { get; set; }
diff --git a/src/Avalonia.Input/KeyboardDevice.cs b/src/Avalonia.Input/KeyboardDevice.cs
index 0321b0bdf3..187670a26b 100644
--- a/src/Avalonia.Input/KeyboardDevice.cs
+++ b/src/Avalonia.Input/KeyboardDevice.cs
@@ -8,9 +8,9 @@ namespace Avalonia.Input
{
public class KeyboardDevice : IKeyboardDevice, INotifyPropertyChanged
{
- private IInputElement _focusedElement;
+ private IInputElement? _focusedElement;
- public event PropertyChangedEventHandler PropertyChanged;
+ public event PropertyChangedEventHandler? PropertyChanged;
public static IKeyboardDevice Instance => AvaloniaLocator.Current.GetService();
@@ -18,7 +18,7 @@ namespace Avalonia.Input
public IFocusManager FocusManager => AvaloniaLocator.Current.GetService();
- public IInputElement FocusedElement
+ public IInputElement? FocusedElement
{
get
{
@@ -33,7 +33,7 @@ namespace Avalonia.Input
}
public void SetFocusedElement(
- IInputElement element,
+ IInputElement? element,
NavigationMethod method,
KeyModifiers keyModifiers)
{
diff --git a/src/Avalonia.Input/KeyboardNavigationHandler.cs b/src/Avalonia.Input/KeyboardNavigationHandler.cs
index c425eeeedb..dbefe63789 100644
--- a/src/Avalonia.Input/KeyboardNavigationHandler.cs
+++ b/src/Avalonia.Input/KeyboardNavigationHandler.cs
@@ -13,7 +13,7 @@ namespace Avalonia.Input
///
/// The window to which the handler belongs.
///
- private IInputRoot _owner;
+ private IInputRoot? _owner;
///
/// Sets the owner of the keyboard navigation handler.
@@ -24,15 +24,12 @@ namespace Avalonia.Input
///
public void SetOwner(IInputRoot owner)
{
- Contract.Requires(owner != null);
-
if (_owner != null)
{
throw new InvalidOperationException("AccessKeyHandler owner has already been set.");
}
- _owner = owner;
-
+ _owner = owner ?? throw new ArgumentNullException(nameof(owner));
_owner.AddHandler(InputElement.KeyDownEvent, OnKeyDown);
}
@@ -45,11 +42,11 @@ namespace Avalonia.Input
/// The next element in the specified direction, or null if
/// was the last in the requested direction.
///
- public static IInputElement GetNext(
+ public static IInputElement? GetNext(
IInputElement element,
NavigationDirection direction)
{
- Contract.Requires(element != null);
+ element = element ?? throw new ArgumentNullException(nameof(element));
var customHandler = element.GetSelfAndVisualAncestors()
.OfType()
@@ -97,7 +94,7 @@ namespace Avalonia.Input
NavigationDirection direction,
KeyModifiers keyModifiers = KeyModifiers.None)
{
- Contract.Requires(element != null);
+ element = element ?? throw new ArgumentNullException(nameof(element));
var next = GetNext(element, direction);
diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs
index 188ddd9835..cec5029c18 100644
--- a/src/Avalonia.Input/MouseDevice.cs
+++ b/src/Avalonia.Input/MouseDevice.cs
@@ -20,7 +20,7 @@ namespace Avalonia.Input
private readonly Pointer _pointer;
private bool _disposed;
- public MouseDevice(Pointer pointer = null)
+ public MouseDevice(Pointer? pointer = null)
{
_pointer = pointer ?? new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true);
}
@@ -34,7 +34,7 @@ namespace Avalonia.Input
/// method.
///
[Obsolete("Use IPointer instead")]
- public IInputElement Captured => _pointer.Captured;
+ public IInputElement? Captured => _pointer.Captured;
///
/// Gets the mouse position, in screen coordinates.
@@ -54,7 +54,7 @@ namespace Avalonia.Input
/// within the control's bounds or not. The current mouse capture control is exposed
/// by the property.
///
- public void Capture(IInputElement control)
+ public void Capture(IInputElement? control)
{
_pointer.Capture(control);
}
@@ -66,7 +66,7 @@ namespace Avalonia.Input
/// The mouse position in the control's coordinates.
public Point GetPosition(IVisual relativeTo)
{
- Contract.Requires(relativeTo != null);
+ relativeTo = relativeTo ?? throw new ArgumentNullException(nameof(relativeTo));
if (relativeTo.VisualRoot == null)
{
@@ -75,7 +75,7 @@ namespace Avalonia.Input
var rootPoint = relativeTo.VisualRoot.PointToClient(Position);
var transform = relativeTo.VisualRoot.TransformToVisual(relativeTo);
- return rootPoint * transform.Value;
+ return rootPoint * transform!.Value;
}
public void ProcessRawEvent(RawInputEventArgs e)
@@ -126,7 +126,7 @@ namespace Avalonia.Input
private void ProcessRawEvent(RawPointerEventArgs e)
{
- Contract.Requires(e != null);
+ e = e ?? throw new ArgumentNullException(nameof(e));
var mouse = (MouseDevice)e.Device;
if(mouse._disposed)
@@ -173,8 +173,8 @@ namespace Avalonia.Input
private void LeaveWindow(IMouseDevice device, ulong timestamp, IInputRoot root, PointerPointProperties properties,
KeyModifiers inputModifiers)
{
- Contract.Requires(device != null);
- Contract.Requires(root != null);
+ device = device ?? throw new ArgumentNullException(nameof(device));
+ root = root ?? throw new ArgumentNullException(nameof(root));
ClearPointerOver(this, timestamp, root, properties, inputModifiers);
}
@@ -214,8 +214,8 @@ namespace Avalonia.Input
PointerPointProperties properties,
KeyModifiers inputModifiers)
{
- Contract.Requires(device != null);
- Contract.Requires(root != null);
+ device = device ?? throw new ArgumentNullException(nameof(device));
+ root = root ?? throw new ArgumentNullException(nameof(root));
var hit = HitTest(root, p);
@@ -250,10 +250,10 @@ namespace Avalonia.Input
private bool MouseMove(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties properties,
KeyModifiers inputModifiers)
{
- Contract.Requires(device != null);
- Contract.Requires(root != null);
+ device = device ?? throw new ArgumentNullException(nameof(device));
+ root = root ?? throw new ArgumentNullException(nameof(root));
- IInputElement source;
+ IInputElement? source;
if (_pointer.Captured == null)
{
@@ -265,18 +265,23 @@ namespace Avalonia.Input
source = _pointer.Captured;
}
- var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, root,
- p, timestamp, properties, inputModifiers);
+ if (source is object)
+ {
+ var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, root,
+ p, timestamp, properties, inputModifiers);
- source?.RaiseEvent(e);
- return e.Handled;
+ source.RaiseEvent(e);
+ return e.Handled;
+ }
+
+ return false;
}
private bool MouseUp(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties props,
KeyModifiers inputModifiers)
{
- Contract.Requires(device != null);
- Contract.Requires(root != null);
+ device = device ?? throw new ArgumentNullException(nameof(device));
+ root = root ?? throw new ArgumentNullException(nameof(root));
var hit = HitTest(root, p);
@@ -298,8 +303,8 @@ namespace Avalonia.Input
PointerPointProperties props,
Vector delta, KeyModifiers inputModifiers)
{
- Contract.Requires(device != null);
- Contract.Requires(root != null);
+ device = device ?? throw new ArgumentNullException(nameof(device));
+ root = root ?? throw new ArgumentNullException(nameof(root));
var hit = HitTest(root, p);
@@ -317,21 +322,21 @@ namespace Avalonia.Input
private IInteractive GetSource(IVisual hit)
{
- Contract.Requires(hit != null);
+ hit = hit ?? throw new ArgumentNullException(nameof(hit));
return _pointer.Captured ??
(hit as IInteractive) ??
hit.GetSelfAndVisualAncestors().OfType().FirstOrDefault();
}
- private IInputElement HitTest(IInputElement root, Point p)
+ private IInputElement? HitTest(IInputElement root, Point p)
{
- Contract.Requires(root != null);
+ root = root ?? throw new ArgumentNullException(nameof(root));
return _pointer.Captured ?? root.InputHitTest(p);
}
- PointerEventArgs CreateSimpleEvent(RoutedEvent ev, ulong timestamp, IInteractive source,
+ PointerEventArgs CreateSimpleEvent(RoutedEvent ev, ulong timestamp, IInteractive? source,
PointerPointProperties properties,
KeyModifiers inputModifiers)
{
@@ -343,8 +348,8 @@ namespace Avalonia.Input
PointerPointProperties properties,
KeyModifiers inputModifiers)
{
- Contract.Requires(device != null);
- Contract.Requires(root != null);
+ device = device ?? throw new ArgumentNullException(nameof(device));
+ root = root ?? throw new ArgumentNullException(nameof(root));
var element = root.PointerOverElement;
var e = CreateSimpleEvent(InputElement.PointerLeaveEvent, timestamp, element, properties, inputModifiers);
@@ -384,12 +389,12 @@ namespace Avalonia.Input
}
}
- private IInputElement SetPointerOver(IPointerDevice device, ulong timestamp, IInputRoot root, Point p,
+ private IInputElement? SetPointerOver(IPointerDevice device, ulong timestamp, IInputRoot root, Point p,
PointerPointProperties properties,
KeyModifiers inputModifiers)
{
- Contract.Requires(device != null);
- Contract.Requires(root != null);
+ device = device ?? throw new ArgumentNullException(nameof(device));
+ root = root ?? throw new ArgumentNullException(nameof(root));
var element = root.InputHitTest(p);
@@ -412,11 +417,11 @@ namespace Avalonia.Input
PointerPointProperties properties,
KeyModifiers inputModifiers)
{
- Contract.Requires(device != null);
- Contract.Requires(root != null);
- Contract.Requires(element != null);
+ device = device ?? throw new ArgumentNullException(nameof(device));
+ root = root ?? throw new ArgumentNullException(nameof(root));
+ element = element ?? throw new ArgumentNullException(nameof(element));
- IInputElement branch = null;
+ IInputElement? branch = null;
var el = element;
diff --git a/src/Avalonia.Input/Navigation/TabNavigation.cs b/src/Avalonia.Input/Navigation/TabNavigation.cs
index cd377f1df6..6f6d68940b 100644
--- a/src/Avalonia.Input/Navigation/TabNavigation.cs
+++ b/src/Avalonia.Input/Navigation/TabNavigation.cs
@@ -22,15 +22,17 @@ namespace Avalonia.Input.Navigation
/// The next element in the specified direction, or null if
/// was the last in the requested direction.
///
- public static IInputElement GetNextInTabOrder(
+ public static IInputElement? GetNextInTabOrder(
IInputElement element,
NavigationDirection direction,
bool outsideElement = false)
{
- Contract.Requires(element != null);
- Contract.Requires(
- direction == NavigationDirection.Next ||
- direction == NavigationDirection.Previous);
+ element = element ?? throw new ArgumentNullException(nameof(element));
+
+ if (direction != NavigationDirection.Next && direction != NavigationDirection.Previous)
+ {
+ throw new ArgumentException("Invalid direction: must be Next or Previous.");
+ }
var container = element.GetVisualParent();
@@ -110,7 +112,7 @@ namespace Avalonia.Input.Navigation
if (customNext.handled)
{
- yield return customNext.next;
+ yield return customNext.next!;
}
else
{
@@ -143,12 +145,14 @@ namespace Avalonia.Input.Navigation
/// If true will not descend into to find next control.
///
/// The next element, or null if the element is the last.
- private static IInputElement GetNextInContainer(
+ private static IInputElement? GetNextInContainer(
IInputElement element,
IInputElement container,
NavigationDirection direction,
bool outsideElement)
{
+ IInputElement? e = element;
+
if (direction == NavigationDirection.Next && !outsideElement)
{
var descendant = GetFocusableDescendants(element, direction).FirstOrDefault();
@@ -167,13 +171,13 @@ namespace Avalonia.Input.Navigation
// INavigableContainer.
if (navigable != null)
{
- while (element != null)
+ while (e != null)
{
- element = navigable.GetControl(direction, element, false);
+ e = navigable.GetControl(direction, e, false);
- if (element != null &&
- element.CanFocus() &&
- KeyboardNavigation.GetIsTabStop((InputElement) element))
+ if (e != null &&
+ e.CanFocus() &&
+ KeyboardNavigation.GetIsTabStop((InputElement)e))
{
break;
}
@@ -183,12 +187,12 @@ namespace Avalonia.Input.Navigation
{
// TODO: Do a spatial search here if the container doesn't implement
// INavigableContainer.
- element = null;
+ e = null;
}
- if (element != null && direction == NavigationDirection.Previous)
+ if (e != null && direction == NavigationDirection.Previous)
{
- var descendant = GetFocusableDescendants(element, direction).LastOrDefault();
+ var descendant = GetFocusableDescendants(e, direction).LastOrDefault();
if (descendant != null)
{
@@ -196,7 +200,7 @@ namespace Avalonia.Input.Navigation
}
}
- return element;
+ return e;
}
return null;
@@ -209,13 +213,13 @@ namespace Avalonia.Input.Navigation
/// The container.
/// The direction of the search.
/// The first element, or null if there are no more elements.
- private static IInputElement GetFirstInNextContainer(
+ private static IInputElement? GetFirstInNextContainer(
IInputElement element,
IInputElement container,
NavigationDirection direction)
{
var parent = container.GetVisualParent();
- IInputElement next = null;
+ IInputElement? next = null;
if (parent != null)
{
@@ -268,7 +272,7 @@ namespace Avalonia.Input.Navigation
return next;
}
- private static (bool handled, IInputElement next) GetCustomNext(IInputElement element,
+ private static (bool handled, IInputElement? next) GetCustomNext(IInputElement element,
NavigationDirection direction)
{
if (element is ICustomKeyboardNavigation custom)
diff --git a/src/Avalonia.Input/Pointer.cs b/src/Avalonia.Input/Pointer.cs
index 00222e92cf..a477711584 100644
--- a/src/Avalonia.Input/Pointer.cs
+++ b/src/Avalonia.Input/Pointer.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using Avalonia.Interactivity;
using Avalonia.VisualTree;
namespace Avalonia.Input
@@ -20,7 +19,7 @@ namespace Avalonia.Input
public int Id { get; }
- IInputElement FindCommonParent(IInputElement control1, IInputElement control2)
+ IInputElement? FindCommonParent(IInputElement? control1, IInputElement? control2)
{
if (control1 == null || control2 == null)
return null;
@@ -28,12 +27,12 @@ namespace Avalonia.Input
return control2.GetSelfAndVisualAncestors().OfType().FirstOrDefault(seen.Contains);
}
- protected virtual void PlatformCapture(IInputElement element)
+ protected virtual void PlatformCapture(IInputElement? element)
{
}
- public void Capture(IInputElement control)
+ public void Capture(IInputElement? control)
{
if (Captured != null)
Captured.DetachedFromVisualTree -= OnCaptureDetached;
@@ -66,7 +65,7 @@ namespace Avalonia.Input
}
- public IInputElement Captured { get; private set; }
+ public IInputElement? Captured { get; private set; }
public PointerType Type { get; }
public bool IsPrimary { get; }
diff --git a/src/Avalonia.Input/PointerEventArgs.cs b/src/Avalonia.Input/PointerEventArgs.cs
index 9cc42ffa69..1cbddf89aa 100644
--- a/src/Avalonia.Input/PointerEventArgs.cs
+++ b/src/Avalonia.Input/PointerEventArgs.cs
@@ -7,14 +7,14 @@ namespace Avalonia.Input
{
public class PointerEventArgs : RoutedEventArgs
{
- private readonly IVisual _rootVisual;
+ private readonly IVisual? _rootVisual;
private readonly Point _rootVisualPosition;
private readonly PointerPointProperties _properties;
public PointerEventArgs(RoutedEvent routedEvent,
- IInteractive source,
+ IInteractive? source,
IPointer pointer,
- IVisual rootVisual, Point rootVisualPosition,
+ IVisual? rootVisual, Point rootVisualPosition,
ulong timestamp,
PointerPointProperties properties,
KeyModifiers modifiers)
@@ -40,8 +40,8 @@ namespace Avalonia.Input
public void ProcessRawEvent(RawInputEventArgs ev) => throw new NotSupportedException();
- public IInputElement Captured => _ev.Pointer.Captured;
- public void Capture(IInputElement control)
+ public IInputElement? Captured => _ev.Pointer.Captured;
+ public void Capture(IInputElement? control)
{
_ev.Pointer.Capture(control);
}
@@ -52,7 +52,7 @@ namespace Avalonia.Input
public IPointer Pointer { get; }
public ulong Timestamp { get; }
- private IPointerDevice _device;
+ private IPointerDevice? _device;
[Obsolete("Use Pointer to get pointer-specific information")]
public IPointerDevice Device => _device ?? (_device = new EmulatedDevice(this));
@@ -76,7 +76,7 @@ namespace Avalonia.Input
public KeyModifiers KeyModifiers { get; }
- public Point GetPosition(IVisual relativeTo)
+ public Point GetPosition(IVisual? relativeTo)
{
if (_rootVisual == null)
return default;
diff --git a/src/Avalonia.Input/Raw/RawDragEventType.cs b/src/Avalonia.Input/Raw/RawDragEventType.cs
index 9635f77467..77f17a5a41 100644
--- a/src/Avalonia.Input/Raw/RawDragEventType.cs
+++ b/src/Avalonia.Input/Raw/RawDragEventType.cs
@@ -7,4 +7,4 @@
DragLeave,
Drop
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Input/Raw/RawInputEventArgs.cs b/src/Avalonia.Input/Raw/RawInputEventArgs.cs
index b85563b24a..dcc5f27a79 100644
--- a/src/Avalonia.Input/Raw/RawInputEventArgs.cs
+++ b/src/Avalonia.Input/Raw/RawInputEventArgs.cs
@@ -21,7 +21,7 @@ namespace Avalonia.Input.Raw
/// The root from which the event originates.
public RawInputEventArgs(IInputDevice device, ulong timestamp, IInputRoot root)
{
- Contract.Requires(device != null);
+ device = device ?? throw new ArgumentNullException(nameof(device));
Device = device;
Timestamp = timestamp;
diff --git a/src/Avalonia.Input/TextInputEventArgs.cs b/src/Avalonia.Input/TextInputEventArgs.cs
index 6e763d3b56..cda0103749 100644
--- a/src/Avalonia.Input/TextInputEventArgs.cs
+++ b/src/Avalonia.Input/TextInputEventArgs.cs
@@ -4,8 +4,8 @@ namespace Avalonia.Input
{
public class TextInputEventArgs : RoutedEventArgs
{
- public IKeyboardDevice Device { get; set; }
+ public IKeyboardDevice? Device { get; set; }
- public string Text { get; set; }
+ public string? Text { get; set; }
}
}
diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs
index e62e22f8ec..aca2965ea6 100644
--- a/src/Avalonia.Layout/Layoutable.cs
+++ b/src/Avalonia.Layout/Layoutable.cs
@@ -758,8 +758,6 @@ namespace Avalonia.Layout
protected override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
{
- base.OnDetachedFromVisualTreeCore(e);
-
if (e.Root is ILayoutRoot r)
{
if (_layoutUpdated is object)
@@ -772,6 +770,8 @@ namespace Avalonia.Layout
r.LayoutManager.UnregisterEffectiveViewportListener(this);
}
}
+
+ base.OnDetachedFromVisualTreeCore(e);
}
///
diff --git a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
new file mode 100644
index 0000000000..8565d99b45
--- /dev/null
+++ b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Runtime.InteropServices;
+using Avalonia.Platform;
+using Avalonia.Platform.Interop;
+
+namespace Avalonia.OpenGL.Angle
+{
+ public class AngleEglInterface : EglInterface
+ {
+ [DllImport("libegl.dll", CharSet = CharSet.Ansi)]
+ static extern IntPtr eglGetProcAddress(string proc);
+
+ public AngleEglInterface() : base(LoadAngle())
+ {
+
+ }
+
+ static Func LoadAngle()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ var disp = eglGetProcAddress("eglGetPlatformDisplayEXT");
+
+ if (disp == IntPtr.Zero)
+ {
+ throw new OpenGlException("libegl.dll doesn't have eglGetPlatformDisplayEXT entry point");
+ }
+
+ return eglGetProcAddress;
+ }
+
+ throw new PlatformNotSupportedException();
+ }
+
+ }
+}
diff --git a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs
new file mode 100644
index 0000000000..1a42ed90c2
--- /dev/null
+++ b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+using static Avalonia.OpenGL.EglConsts;
+
+namespace Avalonia.OpenGL.Angle
+{
+ public class AngleWin32EglDisplay : EglDisplay
+ {
+ struct AngleInfo
+ {
+ public IntPtr Display { get; set; }
+ public AngleOptions.PlatformApi PlatformApi { get; set; }
+ }
+
+ static AngleInfo CreateAngleDisplay(EglInterface _egl)
+ {
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ throw new PlatformNotSupportedException();
+ var display = IntPtr.Zero;
+ AngleOptions.PlatformApi angleApi = default;
+ {
+ if (_egl.GetPlatformDisplayEXT == null)
+ throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl.dll");
+
+ var allowedApis = AvaloniaLocator.Current.GetService()?.AllowedPlatformApis
+ ?? new [] { AngleOptions.PlatformApi.DirectX11, AngleOptions.PlatformApi.DirectX9 };
+
+ foreach (var platformApi in allowedApis)
+ {
+ int dapi;
+ if (platformApi == AngleOptions.PlatformApi.DirectX9)
+ dapi = EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE;
+ else if (platformApi == AngleOptions.PlatformApi.DirectX11)
+ dapi = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE;
+ else
+ continue;
+
+ display = _egl.GetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, IntPtr.Zero,
+ new[] { EGL_PLATFORM_ANGLE_TYPE_ANGLE, dapi, EGL_NONE });
+ if (display != IntPtr.Zero)
+ {
+ angleApi = platformApi;
+ break;
+ }
+ }
+
+ if (display == IntPtr.Zero)
+ throw new OpenGlException("Unable to create ANGLE display");
+ return new AngleInfo { Display = display, PlatformApi = angleApi };
+ }
+ }
+
+ private AngleWin32EglDisplay(EglInterface egl, AngleInfo info) : base(egl, info.Display)
+ {
+ PlatformApi = info.PlatformApi;
+ }
+
+ public AngleWin32EglDisplay(EglInterface egl) : this(egl, CreateAngleDisplay(egl))
+ {
+
+ }
+
+ public AngleWin32EglDisplay() : this(new AngleEglInterface())
+ {
+
+ }
+
+ public AngleOptions.PlatformApi PlatformApi { get; }
+
+ public IntPtr GetDirect3DDevice()
+ {
+ if (!EglInterface.QueryDisplayAttribExt(Handle, EglConsts.EGL_DEVICE_EXT, out var eglDevice))
+ throw new OpenGlException("Unable to get EGL_DEVICE_EXT");
+ if (!EglInterface.QueryDeviceAttribExt(eglDevice, PlatformApi == AngleOptions.PlatformApi.DirectX9 ? EGL_D3D9_DEVICE_ANGLE : EGL_D3D11_DEVICE_ANGLE, out var d3dDeviceHandle))
+ throw new OpenGlException("Unable to get EGL_D3D9_DEVICE_ANGLE");
+ return d3dDeviceHandle;
+ }
+
+ public EglSurface WrapDirect3D11Texture(IntPtr handle)
+ {
+ if (PlatformApi != AngleOptions.PlatformApi.DirectX11)
+ throw new InvalidOperationException("Current platform API is " + PlatformApi);
+ return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, EGL_NONE });
+ }
+ }
+}
diff --git a/src/Avalonia.OpenGL/AngleOptions.cs b/src/Avalonia.OpenGL/AngleOptions.cs
index 4b9c04f4e6..84744288ed 100644
--- a/src/Avalonia.OpenGL/AngleOptions.cs
+++ b/src/Avalonia.OpenGL/AngleOptions.cs
@@ -10,9 +10,6 @@ namespace Avalonia.OpenGL
DirectX11
}
- public List AllowedPlatformApis = new List
- {
- PlatformApi.DirectX9
- };
+ public IList AllowedPlatformApis { get; set; } = null;
}
}
diff --git a/src/Avalonia.OpenGL/EglConsts.cs b/src/Avalonia.OpenGL/EglConsts.cs
index 62fb3faef6..8e44004f2d 100644
--- a/src/Avalonia.OpenGL/EglConsts.cs
+++ b/src/Avalonia.OpenGL/EglConsts.cs
@@ -186,11 +186,24 @@ namespace Avalonia.OpenGL
public const int EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE = 0x3206;
public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE = 0x320A;
public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE = 0x345E;
+
+ public const int EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE = 0x320D;
+ public const int EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE = 0x320E;
//EGL_ANGLE_platform_angle_d3d
public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE = 0x3209;
public const int EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE = 0x320F;
public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE = 0x320B;
public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_REFERENCE_ANGLE = 0x320C;
+
+ //EXT_device_query
+ public const int EGL_DEVICE_EXT = 0x322C;
+
+ //ANGLE_device_d3d
+ public const int EGL_D3D9_DEVICE_ANGLE = 0x33A0;
+ public const int EGL_D3D11_DEVICE_ANGLE = 0x33A1;
+
+ public const int EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE = 0x3200;
+ public const int EGL_D3D_TEXTURE_ANGLE = 0x33A3;
}
}
diff --git a/src/Avalonia.OpenGL/EglDisplay.cs b/src/Avalonia.OpenGL/EglDisplay.cs
index 0436f6ac52..7b194e4346 100644
--- a/src/Avalonia.OpenGL/EglDisplay.cs
+++ b/src/Avalonia.OpenGL/EglDisplay.cs
@@ -15,7 +15,6 @@ namespace Avalonia.OpenGL
private readonly int _surfaceType;
public IntPtr Handle => _display;
- private AngleOptions.PlatformApi? _angleApi;
private int _sampleCount;
private int _stencilSize;
private GlVersion _version;
@@ -24,56 +23,41 @@ namespace Avalonia.OpenGL
{
}
- public EglDisplay(EglInterface egl, int platformType, IntPtr platformDisplay, int[] attrs)
- {
- _egl = egl;
+ static IntPtr CreateDisplay(EglInterface egl, int platformType, IntPtr platformDisplay, int[] attrs)
+ {
+ var display = IntPtr.Zero;
if (platformType == -1 && platformDisplay == IntPtr.Zero)
{
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- if (_egl.GetPlatformDisplayEXT == null)
- throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl.dll");
-
- var allowedApis = AvaloniaLocator.Current.GetService()?.AllowedPlatformApis
- ?? new List {AngleOptions.PlatformApi.DirectX9};
-
- foreach (var platformApi in allowedApis)
- {
- int dapi;
- if (platformApi == AngleOptions.PlatformApi.DirectX9)
- dapi = EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE;
- else if (platformApi == AngleOptions.PlatformApi.DirectX11)
- dapi = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE;
- else
- continue;
-
- _display = _egl.GetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, IntPtr.Zero,
- new[] {EGL_PLATFORM_ANGLE_TYPE_ANGLE, dapi, EGL_NONE});
- if (_display != IntPtr.Zero)
- {
- _angleApi = platformApi;
- break;
- }
- }
-
- if (_display == IntPtr.Zero)
- throw new OpenGlException("Unable to create ANGLE display");
- }
-
- if (_display == IntPtr.Zero)
- _display = _egl.GetDisplay(IntPtr.Zero);
+ if (display == IntPtr.Zero)
+ display = egl.GetDisplay(IntPtr.Zero);
}
else
{
- if (_egl.GetPlatformDisplayEXT == null)
+ if (egl.GetPlatformDisplayEXT == null)
throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl");
- _display = _egl.GetPlatformDisplayEXT(platformType, platformDisplay, attrs);
+ display = egl.GetPlatformDisplayEXT(platformType, platformDisplay, attrs);
}
+
+ if (display == IntPtr.Zero)
+ throw OpenGlException.GetFormattedException("eglGetDisplay", egl);
+ return display;
+ }
- if (_display == IntPtr.Zero)
- throw OpenGlException.GetFormattedException("eglGetDisplay", _egl);
+ public EglDisplay(EglInterface egl, int platformType, IntPtr platformDisplay, int[] attrs)
+ : this(egl, CreateDisplay(egl, platformType, platformDisplay, attrs))
+ {
+
+ }
+ public EglDisplay(EglInterface egl, IntPtr display)
+ {
+ _egl = egl;
+ _display = display;
+ if(_display == IntPtr.Zero)
+ throw new ArgumentException();
+
+
if (!_egl.Initialize(_display, out var major, out var minor))
throw OpenGlException.GetFormattedException("eglInitialize", _egl);
@@ -172,5 +156,15 @@ namespace Avalonia.OpenGL
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);
+ }
}
}
diff --git a/src/Avalonia.OpenGL/EglGlPlatformFeature.cs b/src/Avalonia.OpenGL/EglGlPlatformFeature.cs
index f59c6b7751..7e9383432c 100644
--- a/src/Avalonia.OpenGL/EglGlPlatformFeature.cs
+++ b/src/Avalonia.OpenGL/EglGlPlatformFeature.cs
@@ -20,12 +20,13 @@ namespace Avalonia.OpenGL
if (feature != null)
AvaloniaLocator.CurrentMutable.Bind().ToConstant(feature);
}
-
- public static EglGlPlatformFeature TryCreate()
+
+ public static EglGlPlatformFeature TryCreate() => TryCreate(() => new EglDisplay());
+ public static EglGlPlatformFeature TryCreate(Func displayFactory)
{
try
{
- var disp = new EglDisplay();
+ var disp = displayFactory();
return new EglGlPlatformFeature
{
_display = disp,
diff --git a/src/Avalonia.OpenGL/EglGlPlatformSurface.cs b/src/Avalonia.OpenGL/EglGlPlatformSurface.cs
index 3e4befe2c6..21fadff19e 100644
--- a/src/Avalonia.OpenGL/EglGlPlatformSurface.cs
+++ b/src/Avalonia.OpenGL/EglGlPlatformSurface.cs
@@ -3,33 +3,26 @@ using System.Threading;
namespace Avalonia.OpenGL
{
- public class EglGlPlatformSurface : IGlPlatformSurface
+ public class EglGlPlatformSurface : EglGlPlatformSurfaceBase
{
- public interface IEglWindowGlPlatformSurfaceInfo
- {
- IntPtr Handle { get; }
- PixelSize Size { get; }
- double Scaling { get; }
- }
-
private readonly EglDisplay _display;
private readonly EglContext _context;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
- public EglGlPlatformSurface(EglContext context, IEglWindowGlPlatformSurfaceInfo info)
+ public EglGlPlatformSurface(EglContext context, IEglWindowGlPlatformSurfaceInfo info) : base()
{
_display = context.Display;
_context = context;
_info = info;
}
-
- public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
+
+ public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
{
var glSurface = _display.CreateWindowSurface(_info.Handle);
return new RenderTarget(_display, _context, glSurface, _info);
}
- class RenderTarget : IGlPlatformSurfaceRenderTargetWithCorruptionInfo
+ class RenderTarget : EglPlatformSurfaceRenderTargetBase
{
private readonly EglDisplay _display;
private readonly EglContext _context;
@@ -38,7 +31,7 @@ namespace Avalonia.OpenGL
private PixelSize _initialSize;
public RenderTarget(EglDisplay display, EglContext context,
- EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info)
+ EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info) : base(display, context)
{
_display = display;
_context = context;
@@ -47,70 +40,11 @@ namespace Avalonia.OpenGL
_initialSize = info.Size;
}
- public void Dispose() => _glSurface.Dispose();
+ public override void Dispose() => _glSurface.Dispose();
- public bool IsCorrupted => _initialSize != _info.Size;
-
- public IGlPlatformSurfaceRenderingSession BeginDraw()
- {
- var l = _context.Lock();
- try
- {
- if (IsCorrupted)
- throw new RenderTargetCorruptedException();
- var restoreContext = _context.MakeCurrent(_glSurface);
- _display.EglInterface.WaitClient();
- _display.EglInterface.WaitGL();
- _display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
-
- return new Session(_display, _context, _glSurface, _info, l, restoreContext);
- }
- catch
- {
- l.Dispose();
- throw;
- }
- }
+ public override bool IsCorrupted => _initialSize != _info.Size;
- class Session : IGlPlatformSurfaceRenderingSession
- {
- private readonly EglContext _context;
- private readonly EglSurface _glSurface;
- private readonly IEglWindowGlPlatformSurfaceInfo _info;
- private readonly EglDisplay _display;
- private IDisposable _lock;
- private readonly IDisposable _restoreContext;
-
-
- public Session(EglDisplay display, EglContext context,
- EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info,
- IDisposable @lock, IDisposable restoreContext)
- {
- _context = context;
- _display = display;
- _glSurface = glSurface;
- _info = info;
- _lock = @lock;
- _restoreContext = restoreContext;
- }
-
- public void Dispose()
- {
- _context.GlInterface.Flush();
- _display.EglInterface.WaitGL();
- _glSurface.SwapBuffers();
- _display.EglInterface.WaitClient();
- _display.EglInterface.WaitGL();
- _display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
- _restoreContext.Dispose();
- _lock.Dispose();
- }
-
- public IGlContext Context => _context;
- public PixelSize Size => _info.Size;
- public double Scaling => _info.Scaling;
- public bool IsYFlipped { get; }
- }
+ public override IGlPlatformSurfaceRenderingSession BeginDraw() => base.BeginDraw(_glSurface, _info);
}
}
}
diff --git a/src/Avalonia.OpenGL/EglGlPlatformSurfaceBase.cs b/src/Avalonia.OpenGL/EglGlPlatformSurfaceBase.cs
new file mode 100644
index 0000000000..00c7c4796c
--- /dev/null
+++ b/src/Avalonia.OpenGL/EglGlPlatformSurfaceBase.cs
@@ -0,0 +1,103 @@
+using System;
+
+namespace Avalonia.OpenGL
+{
+ public abstract class EglGlPlatformSurfaceBase : IGlPlatformSurface
+ {
+ public interface IEglWindowGlPlatformSurfaceInfo
+ {
+ IntPtr Handle { get; }
+ PixelSize Size { get; }
+ double Scaling { get; }
+ }
+
+ public abstract IGlPlatformSurfaceRenderTarget CreateGlRenderTarget();
+ }
+
+ public abstract class EglPlatformSurfaceRenderTargetBase : IGlPlatformSurfaceRenderTargetWithCorruptionInfo
+ {
+ private readonly EglDisplay _display;
+ private readonly EglContext _context;
+
+ protected EglPlatformSurfaceRenderTargetBase(EglDisplay display, EglContext context)
+ {
+ _display = display;
+ _context = context;
+ }
+
+ public abstract bool IsCorrupted { get; }
+
+ public virtual void Dispose()
+ {
+
+ }
+
+ public abstract IGlPlatformSurfaceRenderingSession BeginDraw();
+
+ protected IGlPlatformSurfaceRenderingSession BeginDraw(EglSurface surface,
+ EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info, Action onFinish = null, bool isYFlipped = false)
+ {
+ var l = _context.Lock();
+ try
+ {
+ if (IsCorrupted)
+ throw new RenderTargetCorruptedException();
+ var restoreContext = _context.MakeCurrent(surface);
+ _display.EglInterface.WaitClient();
+ _display.EglInterface.WaitGL();
+ _display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
+
+ return new Session(_display, _context, surface, info, l, restoreContext, onFinish, isYFlipped);
+ }
+ catch
+ {
+ l.Dispose();
+ throw;
+ }
+ }
+
+ class Session : IGlPlatformSurfaceRenderingSession
+ {
+ private readonly EglContext _context;
+ private readonly EglSurface _glSurface;
+ private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info;
+ private readonly EglDisplay _display;
+ private readonly IDisposable _lock;
+ private readonly IDisposable _restoreContext;
+ private readonly Action _onFinish;
+
+
+ public Session(EglDisplay display, EglContext context,
+ EglSurface glSurface, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info,
+ IDisposable @lock, IDisposable restoreContext, Action onFinish, bool isYFlipped)
+ {
+ IsYFlipped = isYFlipped;
+ _context = context;
+ _display = display;
+ _glSurface = glSurface;
+ _info = info;
+ _lock = @lock;
+ _restoreContext = restoreContext;
+ _onFinish = onFinish;
+ }
+
+ public void Dispose()
+ {
+ _context.GlInterface.Flush();
+ _display.EglInterface.WaitGL();
+ _glSurface.SwapBuffers();
+ _display.EglInterface.WaitClient();
+ _display.EglInterface.WaitGL();
+ _display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
+ _restoreContext.Dispose();
+ _lock.Dispose();
+ _onFinish?.Invoke();
+ }
+
+ public IGlContext Context => _context;
+ public PixelSize Size => _info.Size;
+ public double Scaling => _info.Scaling;
+ public bool IsYFlipped { get; }
+ }
+ }
+}
diff --git a/src/Avalonia.OpenGL/EglInterface.cs b/src/Avalonia.OpenGL/EglInterface.cs
index c0665a1ea1..666c0d8351 100644
--- a/src/Avalonia.OpenGL/EglInterface.cs
+++ b/src/Avalonia.OpenGL/EglInterface.cs
@@ -17,25 +17,21 @@ namespace Avalonia.OpenGL
}
+ public EglInterface(Func getProcAddress) : base(getProcAddress)
+ {
+
+ }
+
public EglInterface(string library) : base(Load(library))
{
}
- [DllImport("libegl.dll", CharSet = CharSet.Ansi)]
- static extern IntPtr eglGetProcAddress(string proc);
static Func Load()
{
var os = AvaloniaLocator.Current.GetService().GetRuntimeInfo().OperatingSystem;
if(os == OperatingSystemType.Linux || os == OperatingSystemType.Android)
return Load("libEGL.so.1");
- if (os == OperatingSystemType.WinNT)
- {
- var disp = eglGetProcAddress("eglGetPlatformDisplayEXT");
- if (disp == IntPtr.Zero)
- throw new OpenGlException("libegl.dll doesn't have eglGetPlatformDisplayEXT entry point");
- return eglGetProcAddress;
- }
throw new PlatformNotSupportedException();
}
@@ -147,6 +143,21 @@ namespace Avalonia.OpenGL
return null;
return Marshal.PtrToStringAnsi(rv);
}
+
+ public delegate IntPtr EglCreatePbufferFromClientBuffer(IntPtr display, int buftype, IntPtr buffer, IntPtr config, int[] attrib_list);
+ [GlEntryPoint("eglCreatePbufferFromClientBuffer")]
+
+ public EglCreatePbufferFromClientBuffer CreatePbufferFromClientBuffer { get; }
+
+ public delegate bool EglQueryDisplayAttribEXT(IntPtr display, int attr, out IntPtr res);
+
+ [GlEntryPoint("eglQueryDisplayAttribEXT"), GlOptionalEntryPoint]
+ public EglQueryDisplayAttribEXT QueryDisplayAttribExt { get; }
+
+ public delegate bool EglQueryDeviceAttribEXT(IntPtr display, int attr, out IntPtr res);
+
+ [GlEntryPoint("eglQueryDeviceAttribEXT"), GlOptionalEntryPoint]
+ public EglQueryDisplayAttribEXT QueryDeviceAttribExt { get; }
// ReSharper restore UnassignedGetOnlyAutoProperty
}
diff --git a/src/Avalonia.Themes.Fluent/CheckBox.xaml b/src/Avalonia.Themes.Fluent/CheckBox.xaml
index 5f82fed08c..73c44c02ab 100644
--- a/src/Avalonia.Themes.Fluent/CheckBox.xaml
+++ b/src/Avalonia.Themes.Fluent/CheckBox.xaml
@@ -8,11 +8,11 @@
-
+
-
+
diff --git a/src/Avalonia.Themes.Fluent/RadioButton.xaml b/src/Avalonia.Themes.Fluent/RadioButton.xaml
index acde4ea0be..078f51c87a 100644
--- a/src/Avalonia.Themes.Fluent/RadioButton.xaml
+++ b/src/Avalonia.Themes.Fluent/RadioButton.xaml
@@ -13,11 +13,11 @@
-
+
-
+
diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt
index 5058cff26d..5aa497861d 100644
--- a/src/Avalonia.Visuals/ApiCompatBaseline.txt
+++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt
@@ -1,5 +1,15 @@
Compat issues with assembly Avalonia.Visuals:
+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.
+CannotSealType : Type 'Avalonia.Media.Typeface' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
+TypeCannotChangeClassification : Type 'Avalonia.Media.Typeface' is a 'struct' in the implementation but is a 'class' in the contract.
+CannotMakeMemberNonVirtual : Member 'public System.Boolean Avalonia.Media.Typeface.Equals(System.Object)' is non-virtual in the implementation but is virtual in the contract.
+CannotMakeMemberNonVirtual : Member 'public System.Int32 Avalonia.Media.Typeface.GetHashCode()' is non-virtual in the implementation but is virtual in the contract.
+TypesMustExist : Type 'Avalonia.Media.Fonts.FontKey' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak' is abstract in the implementation but is missing 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.
-Total Issues: 3
+InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' is present in the contract but not in the implementation.
+MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' does not exist in the implementation but it does exist in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract.
+Total Issues: 13
diff --git a/src/Avalonia.Visuals/Media/FontManager.cs b/src/Avalonia.Visuals/Media/FontManager.cs
index ad3fee7eb7..db87c7c6c4 100644
--- a/src/Avalonia.Visuals/Media/FontManager.cs
+++ b/src/Avalonia.Visuals/Media/FontManager.cs
@@ -13,8 +13,8 @@ namespace Avalonia.Media
///
public sealed class FontManager
{
- private readonly ConcurrentDictionary _typefaceCache =
- new ConcurrentDictionary();
+ private readonly ConcurrentDictionary _glyphTypefaceCache =
+ new ConcurrentDictionary();
private readonly FontFamily _defaultFontFamily;
public FontManager(IFontManagerImpl platformImpl)
@@ -76,79 +76,52 @@ namespace Avalonia.Media
PlatformImpl.GetInstalledFontFamilyNames(checkForUpdates);
///
- /// Returns a new typeface, or an existing one if a matching typeface exists.
+ /// Returns a new , or an existing one if a matching exists.
///
- /// The font family.
- /// The font style.
- /// The font weight.
+ /// The typeface.
///
- /// The typeface.
+ /// The .
///
- public Typeface GetOrAddTypeface(FontFamily fontFamily, FontStyle fontStyle = FontStyle.Normal,
- FontWeight fontWeight = FontWeight.Normal)
+ public GlyphTypeface GetOrAddGlyphTypeface(Typeface typeface)
{
while (true)
{
- if (fontFamily.IsDefault)
+ if (_glyphTypefaceCache.TryGetValue(typeface, out var glyphTypeface))
{
- fontFamily = _defaultFontFamily;
+ return glyphTypeface;
}
- var key = new FontKey(fontFamily.Name, fontStyle, fontWeight);
+ glyphTypeface = new GlyphTypeface(typeface);
- if (_typefaceCache.TryGetValue(key, out var typeface))
+ if (_glyphTypefaceCache.TryAdd(typeface, glyphTypeface))
{
- return typeface;
+ return glyphTypeface;
}
- typeface = new Typeface(fontFamily, fontStyle, fontWeight);
-
- if (_typefaceCache.TryAdd(key, typeface))
- {
- return typeface;
- }
-
- if (fontFamily == _defaultFontFamily)
+ if (typeface.FontFamily == _defaultFontFamily)
{
return null;
}
- fontFamily = _defaultFontFamily;
+ typeface = new Typeface(_defaultFontFamily, typeface.Style, typeface.Weight);
}
}
///
- /// Tries to match a specified character to a typeface that supports specified font properties.
- /// Returns null if no fallback was found.
+ /// Tries to match a specified character to a that supports specified font properties.
///
/// The codepoint to match against.
/// The font style.
/// The font weight.
/// The font family. This is optional and used for fallback lookup.
/// The culture.
+ /// The matching .
///
- /// The matched typeface.
+ /// True, if the could match the character to specified parameters, False otherwise.
///
- public Typeface MatchCharacter(int codepoint,
- FontStyle fontStyle = FontStyle.Normal,
- FontWeight fontWeight = FontWeight.Normal,
- FontFamily fontFamily = null, CultureInfo culture = null)
- {
- foreach (var cachedTypeface in _typefaceCache.Values)
- {
- // First try to find a cached typeface by style and weight to avoid redundant glyph index lookup.
- if (cachedTypeface.Style == fontStyle && cachedTypeface.Weight == fontWeight
- && cachedTypeface.GlyphTypeface.GetGlyph((uint)codepoint) != 0)
- {
- return cachedTypeface;
- }
- }
-
- var matchedTypeface = PlatformImpl.TryMatchCharacter(codepoint, fontStyle, fontWeight, fontFamily, culture, out var key) ?
- _typefaceCache.GetOrAdd(key, new Typeface(key.FamilyName, key.Style, key.Weight)) :
- null;
-
- return matchedTypeface;
- }
+ public bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
+ FontWeight fontWeight,
+ FontFamily fontFamily, CultureInfo culture, out Typeface typeface) =>
+ PlatformImpl.TryMatchCharacter(codepoint, fontStyle, fontWeight, fontFamily, culture, out typeface);
}
}
diff --git a/src/Avalonia.Visuals/Media/Fonts/FontKey.cs b/src/Avalonia.Visuals/Media/Fonts/FontKey.cs
deleted file mode 100644
index b330db8462..0000000000
--- a/src/Avalonia.Visuals/Media/Fonts/FontKey.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System;
-
-namespace Avalonia.Media.Fonts
-{
- public readonly struct FontKey : IEquatable
- {
- public FontKey(string familyName, FontStyle style, FontWeight weight)
- {
- FamilyName = familyName;
- Style = style;
- Weight = weight;
- }
-
- public string FamilyName { get; }
- public FontStyle Style { get; }
- public FontWeight Weight { get; }
-
- public override int GetHashCode()
- {
- var hash = FamilyName.GetHashCode();
-
- hash = hash * 31 + (int)Style;
- hash = hash * 31 + (int)Weight;
-
- return hash;
- }
-
- public override bool Equals(object other)
- {
- return other is FontKey key && Equals(key);
- }
-
- public bool Equals(FontKey other)
- {
- return FamilyName == other.FamilyName &&
- Style == other.Style &&
- Weight == other.Weight;
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs
index 47e716982c..b91a50a27c 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs
@@ -70,10 +70,11 @@ namespace Avalonia.Media.TextFormatting
var codepoint = Codepoint.ReadAt(text, count, out _);
//ToDo: Fix FontFamily fallback
- currentTypeface =
- FontManager.Current.MatchCharacter(codepoint, defaultTypeface.Style, defaultTypeface.Weight, defaultTypeface.FontFamily);
+ var matchFound =
+ FontManager.Current.TryMatchCharacter(codepoint, defaultTypeface.Style, defaultTypeface.Weight,
+ defaultTypeface.FontFamily, defaultProperties.CultureInfo, out currentTypeface);
- if (currentTypeface != null && TryGetRunProperties(text, currentTypeface, defaultTypeface, out count))
+ if (matchFound && TryGetRunProperties(text, currentTypeface, defaultTypeface, out count))
{
//Fallback found
return new ShapeableTextCharacters(text.Take(count),
diff --git a/src/Avalonia.Visuals/Media/Typeface.cs b/src/Avalonia.Visuals/Media/Typeface.cs
index 677e930804..17824c3c5e 100644
--- a/src/Avalonia.Visuals/Media/Typeface.cs
+++ b/src/Avalonia.Visuals/Media/Typeface.cs
@@ -8,17 +8,15 @@ namespace Avalonia.Media
/// Represents a typeface.
///
[DebuggerDisplay("Name = {FontFamily.Name}, Weight = {Weight}, Style = {Style}")]
- public class Typeface : IEquatable
+ public readonly struct Typeface : IEquatable
{
- private GlyphTypeface _glyphTypeface;
-
///
/// Initializes a new instance of the class.
///
/// The font family.
/// The font style.
/// The font weight.
- public Typeface([NotNull]FontFamily fontFamily,
+ public Typeface([NotNull] FontFamily fontFamily,
FontStyle style = FontStyle.Normal,
FontWeight weight = FontWeight.Normal)
{
@@ -45,7 +43,7 @@ namespace Avalonia.Media
{
}
- public static Typeface Default => FontManager.Current?.GetOrAddTypeface(FontFamily.Default);
+ public static Typeface Default { get; } = new Typeface(FontFamily.Default);
///
/// Gets the font family.
@@ -68,7 +66,7 @@ namespace Avalonia.Media
///
/// The glyph typeface.
///
- public GlyphTypeface GlyphTypeface => _glyphTypeface ?? (_glyphTypeface = new GlyphTypeface(this));
+ public GlyphTypeface GlyphTypeface => FontManager.Current.GetOrAddGlyphTypeface(this);
public static bool operator !=(Typeface a, Typeface b)
{
@@ -77,32 +75,17 @@ namespace Avalonia.Media
public static bool operator ==(Typeface a, Typeface b)
{
- if (ReferenceEquals(a, b))
- {
- return true;
- }
-
- return !(a is null) && a.Equals(b);
+ return a.Equals(b);
}
public override bool Equals(object obj)
{
- if (obj is Typeface typeface)
- {
- return Equals(typeface);
- }
-
- return false;
+ return obj is Typeface typeface && Equals(typeface);
}
public bool Equals(Typeface other)
{
- if (other is null)
- {
- return false;
- }
-
- return FontFamily.Equals(other.FontFamily) && Style == other.Style && Weight == other.Weight;
+ return FontFamily == other.FontFamily && Style == other.Style && Weight == other.Weight;
}
public override int GetHashCode()
diff --git a/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs b/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs
index 59b08aae0a..e562b45ca8 100644
--- a/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs
+++ b/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs
@@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Globalization;
using Avalonia.Media;
-using Avalonia.Media.Fonts;
namespace Avalonia.Platform
{
@@ -26,13 +25,13 @@ namespace Avalonia.Platform
/// The font weight.
/// The font family. This is optional and used for fallback lookup.
/// The culture.
- /// The matching font key.
+ /// The matching typeface.
///
/// True, if the could match the character to specified parameters, False otherwise.
///
bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
FontWeight fontWeight,
- FontFamily fontFamily, CultureInfo culture, out FontKey fontKey);
+ FontFamily fontFamily, CultureInfo culture, out Typeface typeface);
///
/// Creates a glyph typeface.
diff --git a/src/Avalonia.Visuals/Rendering/RenderLayer.cs b/src/Avalonia.Visuals/Rendering/RenderLayer.cs
index d6676e25ff..ddf5f4e5cf 100644
--- a/src/Avalonia.Visuals/Rendering/RenderLayer.cs
+++ b/src/Avalonia.Visuals/Rendering/RenderLayer.cs
@@ -30,12 +30,13 @@ namespace Avalonia.Rendering
{
if (Size != size || Scaling != scaling)
{
+ Bitmap.Dispose();
var resized = RefCountable.Create(drawingContext.CreateLayer(size));
using (var context = resized.Item.CreateDrawingContext(null))
{
context.Clear(Colors.Transparent);
- Bitmap.Dispose();
+
Bitmap = resized;
Scaling = scaling;
Size = size;
diff --git a/src/Avalonia.Visuals/Rendering/RendererBase.cs b/src/Avalonia.Visuals/Rendering/RendererBase.cs
index b37d5d660b..5c9cace4cd 100644
--- a/src/Avalonia.Visuals/Rendering/RendererBase.cs
+++ b/src/Avalonia.Visuals/Rendering/RendererBase.cs
@@ -20,7 +20,7 @@ namespace Avalonia.Rendering
_useManualFpsCounting = useManualFpsCounting;
_fpsText = new FormattedText
{
- Typeface = FontManager.Current?.GetOrAddTypeface(FontFamily.Default),
+ Typeface = new Typeface(FontFamily.Default),
FontSize = s_fontSize
};
}
diff --git a/src/Skia/Avalonia.Skia/FontManagerImpl.cs b/src/Skia/Avalonia.Skia/FontManagerImpl.cs
index 91bc937475..62ec39346a 100644
--- a/src/Skia/Avalonia.Skia/FontManagerImpl.cs
+++ b/src/Skia/Avalonia.Skia/FontManagerImpl.cs
@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Globalization;
using Avalonia.Media;
-using Avalonia.Media.Fonts;
using Avalonia.Platform;
using SkiaSharp;
@@ -31,7 +30,7 @@ namespace Avalonia.Skia
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
FontWeight fontWeight,
- FontFamily fontFamily, CultureInfo culture, out FontKey fontKey)
+ FontFamily fontFamily, CultureInfo culture, out Typeface fontKey)
{
SKFontStyle skFontStyle;
@@ -81,7 +80,7 @@ namespace Avalonia.Skia
continue;
}
- fontKey = new FontKey(skTypeface.FamilyName, fontStyle, fontWeight);
+ fontKey = new Typeface(skTypeface.FamilyName, fontStyle, fontWeight);
return true;
}
@@ -92,7 +91,7 @@ namespace Avalonia.Skia
if (skTypeface != null)
{
- fontKey = new FontKey(skTypeface.FamilyName, fontStyle, fontWeight);
+ fontKey = new Typeface(skTypeface.FamilyName, fontStyle, fontWeight);
return true;
}
diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs
index 6c2ac17923..71deb1235f 100644
--- a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs
+++ b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs
@@ -2,31 +2,28 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Media;
-using Avalonia.Media.Fonts;
using SkiaSharp;
namespace Avalonia.Skia
{
internal class SKTypefaceCollection
{
- private readonly ConcurrentDictionary _typefaces =
- new ConcurrentDictionary();
+ private readonly ConcurrentDictionary _typefaces =
+ new ConcurrentDictionary();
- public void AddTypeface(FontKey key, SKTypeface typeface)
+ public void AddTypeface(Typeface key, SKTypeface typeface)
{
_typefaces.TryAdd(key, typeface);
}
public SKTypeface Get(Typeface typeface)
{
- var key = new FontKey(typeface.FontFamily.Name, typeface.Style, typeface.Weight);
-
- return GetNearestMatch(_typefaces, key);
+ return GetNearestMatch(_typefaces, typeface);
}
- private static SKTypeface GetNearestMatch(IDictionary typefaces, FontKey key)
+ private static SKTypeface GetNearestMatch(IDictionary typefaces, Typeface key)
{
- if (typefaces.TryGetValue(new FontKey(key.FamilyName, key.Style, key.Weight), out var typeface))
+ if (typefaces.TryGetValue(key, out var typeface))
{
return typeface;
}
@@ -42,7 +39,7 @@ namespace Avalonia.Skia
{
if (weight - j >= 100)
{
- if (typefaces.TryGetValue(new FontKey(key.FamilyName, (FontStyle)i, (FontWeight)(weight - j)), out typeface))
+ if (typefaces.TryGetValue(new Typeface(key.FontFamily, (FontStyle)i, (FontWeight)(weight - j)), out typeface))
{
return typeface;
}
@@ -53,7 +50,7 @@ namespace Avalonia.Skia
continue;
}
- if (typefaces.TryGetValue(new FontKey(key.FamilyName, (FontStyle)i, (FontWeight)(weight + j)), out typeface))
+ if (typefaces.TryGetValue(new Typeface(key.FontFamily, (FontStyle)i, (FontWeight)(weight + j)), out typeface))
{
return typeface;
}
diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs
index 7ca44e7282..de77c9186e 100644
--- a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs
+++ b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs
@@ -56,7 +56,7 @@ namespace Avalonia.Skia
continue;
}
- var key = new FontKey(fontFamily.Name, typeface.FontSlant.ToAvalonia(),
+ var key = new Typeface(fontFamily, typeface.FontSlant.ToAvalonia(),
(FontWeight)typeface.FontWeight);
typeFaceCollection.AddTypeface(key, typeface);
diff --git a/src/Skia/Avalonia.Skia/SkiaOptions.cs b/src/Skia/Avalonia.Skia/SkiaOptions.cs
index cbe0b5ef42..493263677d 100644
--- a/src/Skia/Avalonia.Skia/SkiaOptions.cs
+++ b/src/Skia/Avalonia.Skia/SkiaOptions.cs
@@ -16,6 +16,10 @@ namespace Avalonia
///
/// The maximum number of bytes for video memory to store textures and resources.
///
- public long? MaxGpuResourceSizeBytes { get; set; }
+ ///
+ /// This is set by default to the recommended value for Avalonia.
+ /// Setting this to null will give you the default Skia value.
+ ///
+ public long? MaxGpuResourceSizeBytes { get; set; } = 1024 * 600 * 4 * 12; // ~28mb 12x 1024 x 600 textures.
}
}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs
index 33af15076d..6d95d759ec 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs
@@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Globalization;
using Avalonia.Media;
-using Avalonia.Media.Fonts;
using Avalonia.Platform;
using SharpDX.DirectWrite;
using FontFamily = Avalonia.Media.FontFamily;
@@ -34,7 +33,7 @@ namespace Avalonia.Direct2D1.Media
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
FontWeight fontWeight,
- FontFamily fontFamily, CultureInfo culture, out FontKey fontKey)
+ FontFamily fontFamily, CultureInfo culture, out Typeface typeface)
{
var familyCount = Direct2D1FontCollectionCache.InstalledFontCollection.FontFamilyCount;
@@ -51,12 +50,12 @@ namespace Avalonia.Direct2D1.Media
var fontFamilyName = font.FontFamily.FamilyNames.GetString(0);
- fontKey = new FontKey(fontFamilyName, fontStyle, fontWeight);
+ typeface = new Typeface(fontFamilyName, fontStyle, fontWeight);
return true;
}
- fontKey = default;
+ typeface = default;
return false;
}
diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs
index 585e68056b..bd188ad53a 100644
--- a/src/Windows/Avalonia.Win32/Win32GlManager.cs
+++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs
@@ -1,4 +1,5 @@
using Avalonia.OpenGL;
+using Avalonia.OpenGL.Angle;
namespace Avalonia.Win32
{
@@ -15,7 +16,7 @@ namespace Avalonia.Win32
{
if (!s_attemptedToInitialize)
{
- EglFeature = EglGlPlatformFeature.TryCreate();
+ EglFeature = EglGlPlatformFeature.TryCreate(() => new AngleWin32EglDisplay());
s_attemptedToInitialize = true;
}
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index ddc0cc4e42..9c6bce1c90 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -853,7 +853,7 @@ namespace Avalonia.Win32
private void ShowWindow(WindowState state)
{
- ShowWindowCommand command;
+ ShowWindowCommand? command;
var newWindowProperties = _windowProperties;
@@ -875,8 +875,8 @@ namespace Avalonia.Win32
case WindowState.FullScreen:
newWindowProperties.IsFullScreen = true;
- UpdateWindowProperties(newWindowProperties);
- return;
+ command = IsWindowVisible(_hwnd) ? (ShowWindowCommand?)null : ShowWindowCommand.Restore;
+ break;
default:
throw new ArgumentException("Invalid WindowState.");
@@ -884,7 +884,10 @@ namespace Avalonia.Win32
UpdateWindowProperties(newWindowProperties);
- UnmanagedMethods.ShowWindow(_hwnd, command);
+ if (command.HasValue)
+ {
+ UnmanagedMethods.ShowWindow(_hwnd, command.Value);
+ }
if (state == WindowState.Maximized)
{
@@ -1007,10 +1010,12 @@ namespace Avalonia.Win32
if (newProperties.IsResizable)
{
style |= WindowStyles.WS_SIZEFRAME;
+ style |= WindowStyles.WS_MAXIMIZEBOX;
}
else
{
style &= ~WindowStyles.WS_SIZEFRAME;
+ style &= ~WindowStyles.WS_MAXIMIZEBOX;
}
SetStyle(style);
diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs
index 0b81276240..0c7b966f29 100644
--- a/tests/Avalonia.LeakTests/ControlTests.cs
+++ b/tests/Avalonia.LeakTests/ControlTests.cs
@@ -552,6 +552,37 @@ namespace Avalonia.LeakTests
}
}
+ [Fact]
+ public void ItemsRepeater_Is_Freed()
+ {
+ using (Start())
+ {
+ Func run = () =>
+ {
+ var window = new Window
+ {
+ Content = new ItemsRepeater(),
+ };
+
+ window.Show();
+
+ window.LayoutManager.ExecuteInitialLayoutPass();
+ Assert.IsType(window.Presenter.Child);
+
+ window.Content = null;
+ window.LayoutManager.ExecuteLayoutPass();
+ Assert.Null(window.Presenter.Child);
+
+ return window;
+ };
+
+ var result = run();
+
+ dotMemory.Check(memory =>
+ Assert.Equal(0, memory.GetObjects(where => where.Type.Is()).ObjectsCount));
+ }
+ }
+
private IDisposable Start()
{
return UnitTestApplication.Start(TestServices.StyledWindow.With(
diff --git a/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs b/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs
index 8683da9a01..7528424521 100644
--- a/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs
+++ b/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs
@@ -51,7 +51,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
{
var r = AvaloniaLocator.Current.GetService();
return r.CreateFormattedText(text,
- FontManager.Current.GetOrAddTypeface(fontFamily, fontStyle, fontWeight),
+ new Typeface(fontFamily, fontStyle, fontWeight),
fontSize,
textAlignment,
wrapping,
diff --git a/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs b/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs
index f36d6d9e4a..a0fe348166 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs
@@ -39,7 +39,7 @@ namespace Avalonia.Skia.UnitTests.Media
private readonly string[] _bcp47 = { CultureInfo.CurrentCulture.ThreeLetterISOLanguageName, CultureInfo.CurrentCulture.TwoLetterISOLanguageName };
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily,
- CultureInfo culture, out FontKey fontKey)
+ CultureInfo culture, out Typeface typeface)
{
foreach (var customTypeface in _customTypefaces)
{
@@ -48,7 +48,7 @@ namespace Avalonia.Skia.UnitTests.Media
continue;
}
- fontKey = new FontKey(customTypeface.FontFamily.Name, fontStyle, fontWeight);
+ typeface = new Typeface(customTypeface.FontFamily.Name, fontStyle, fontWeight);
return true;
}
@@ -56,7 +56,7 @@ namespace Avalonia.Skia.UnitTests.Media
var fallback = SKFontManager.Default.MatchCharacter(fontFamily?.Name, (SKFontStyleWeight)fontWeight,
SKFontStyleWidth.Normal, (SKFontStyleSlant)fontStyle, _bcp47, codepoint);
- fontKey = new FontKey(fallback?.FamilyName ?? _defaultFamilyName, fontStyle, fontWeight);
+ typeface = new Typeface(fallback?.FamilyName ?? _defaultFamilyName, fontStyle, fontWeight);
return true;
}
@@ -73,13 +73,13 @@ namespace Avalonia.Skia.UnitTests.Media
skTypeface = typefaceCollection.Get(typeface);
break;
}
-
case "Noto Sans":
{
var typefaceCollection = SKTypefaceCollectionCache.GetOrAddTypefaceCollection(_italicTypeface.FontFamily);
skTypeface = typefaceCollection.Get(typeface);
break;
}
+ case FontFamily.DefaultFontFamilyName:
case "Noto Mono":
{
var typefaceCollection = SKTypefaceCollectionCache.GetOrAddTypefaceCollection(_defaultTypeface.FontFamily);
diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs
index 4a88b259bc..adcc79e029 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs
@@ -130,7 +130,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
{
using (Start())
{
- const string text = "1234الدولي";
+ const string text = "ABCDالدولي";
var defaultProperties = new GenericTextRunProperties(Typeface.Default);
diff --git a/tests/Avalonia.UnitTests/MockFontManagerImpl.cs b/tests/Avalonia.UnitTests/MockFontManagerImpl.cs
index e614c60310..ba3b346f1b 100644
--- a/tests/Avalonia.UnitTests/MockFontManagerImpl.cs
+++ b/tests/Avalonia.UnitTests/MockFontManagerImpl.cs
@@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Globalization;
using Avalonia.Media;
-using Avalonia.Media.Fonts;
using Avalonia.Platform;
namespace Avalonia.UnitTests
@@ -26,9 +25,9 @@ namespace Avalonia.UnitTests
}
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily,
- CultureInfo culture, out FontKey fontKey)
+ CultureInfo culture, out Typeface fontKey)
{
- fontKey = default;
+ fontKey = new Typeface(_defaultFamilyName);
return false;
}
diff --git a/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs
index 81a4ca6495..2b0ffa4ed6 100644
--- a/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs
@@ -1,6 +1,5 @@
using System;
using Avalonia.Media;
-using Avalonia.Platform;
using Avalonia.UnitTests;
using Xunit;
@@ -15,9 +14,11 @@ namespace Avalonia.Visuals.UnitTests.Media
{
var fontFamily = new FontFamily("MyFont");
- var typeface = FontManager.Current.GetOrAddTypeface(fontFamily);
+ var typeface = new Typeface(fontFamily);
- Assert.Same(typeface, FontManager.Current.GetOrAddTypeface(fontFamily));
+ var glyphTypeface = FontManager.Current.GetOrAddGlyphTypeface(typeface);
+
+ Assert.Same(glyphTypeface, FontManager.Current.GetOrAddGlyphTypeface(typeface));
}
}