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/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/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.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.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/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..5cc732ef6a 100644 --- a/src/Avalonia.OpenGL/EglConsts.cs +++ b/src/Avalonia.OpenGL/EglConsts.cs @@ -186,6 +186,9 @@ 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; diff --git a/src/Avalonia.OpenGL/EglDisplay.cs b/src/Avalonia.OpenGL/EglDisplay.cs index 0436f6ac52..ce5fd50a07 100644 --- a/src/Avalonia.OpenGL/EglDisplay.cs +++ b/src/Avalonia.OpenGL/EglDisplay.cs @@ -36,7 +36,11 @@ namespace Avalonia.OpenGL throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl.dll"); var allowedApis = AvaloniaLocator.Current.GetService()?.AllowedPlatformApis - ?? new List {AngleOptions.PlatformApi.DirectX9}; + ?? new [] + { + AngleOptions.PlatformApi.DirectX11, + AngleOptions.PlatformApi.DirectX9 + }; foreach (var platformApi in allowedApis) { 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.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index ddc0cc4e42..4929283874 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -1007,10 +1007,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(