Ver código fonte

Merge branch 'fixes/TypefaceLeak' of https://github.com/Gillibald/Avalonia into fixes/TypefaceLeak

Benedikt Schroeder 5 anos atrás
pai
commit
6c887fbfdd
40 arquivos alterados com 281 adições e 206 exclusões
  1. 0 7
      azure-pipelines.yml
  2. 8 2
      src/Avalonia.Controls/ColumnDefinitions.cs
  3. 1 1
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  4. 19 5
      src/Avalonia.Controls/Repeater/ItemsRepeater.cs
  5. 31 20
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs
  6. 10 12
      src/Avalonia.Input/AccessKeyHandler.cs
  7. 2 0
      src/Avalonia.Input/Avalonia.Input.csproj
  8. 3 3
      src/Avalonia.Input/DataObject.cs
  9. 3 3
      src/Avalonia.Input/DragDropDevice.cs
  10. 24 23
      src/Avalonia.Input/FocusManager.cs
  11. 7 9
      src/Avalonia.Input/GestureRecognizers/GestureRecognizerCollection.cs
  12. 7 8
      src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs
  13. 8 1
      src/Avalonia.Input/Gestures.cs
  14. 1 1
      src/Avalonia.Input/IAccessKeyHandler.cs
  15. 3 3
      src/Avalonia.Input/IDataObject.cs
  16. 3 3
      src/Avalonia.Input/IFocusManager.cs
  17. 1 1
      src/Avalonia.Input/IInputElement.cs
  18. 2 2
      src/Avalonia.Input/IInputRoot.cs
  19. 2 2
      src/Avalonia.Input/IKeyboardDevice.cs
  20. 2 2
      src/Avalonia.Input/IPointer.cs
  21. 2 2
      src/Avalonia.Input/IPointerDevice.cs
  22. 4 4
      src/Avalonia.Input/InputElement.cs
  23. 1 1
      src/Avalonia.Input/KeyEventArgs.cs
  24. 4 4
      src/Avalonia.Input/KeyboardDevice.cs
  25. 5 8
      src/Avalonia.Input/KeyboardNavigationHandler.cs
  26. 39 34
      src/Avalonia.Input/MouseDevice.cs
  27. 23 19
      src/Avalonia.Input/Navigation/TabNavigation.cs
  28. 4 5
      src/Avalonia.Input/Pointer.cs
  29. 7 7
      src/Avalonia.Input/PointerEventArgs.cs
  30. 1 1
      src/Avalonia.Input/Raw/RawDragEventType.cs
  31. 1 1
      src/Avalonia.Input/Raw/RawInputEventArgs.cs
  32. 2 2
      src/Avalonia.Input/TextInputEventArgs.cs
  33. 2 2
      src/Avalonia.Layout/Layoutable.cs
  34. 1 4
      src/Avalonia.OpenGL/AngleOptions.cs
  35. 3 0
      src/Avalonia.OpenGL/EglConsts.cs
  36. 5 1
      src/Avalonia.OpenGL/EglDisplay.cs
  37. 2 2
      src/Avalonia.Visuals/Visual.cs
  38. 5 1
      src/Skia/Avalonia.Skia/SkiaOptions.cs
  39. 2 0
      src/Windows/Avalonia.Win32/WindowImpl.cs
  40. 31 0
      tests/Avalonia.LeakTests/ControlTests.cs

+ 0 - 7
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:

+ 8 - 2
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
         /// <summary>
         /// Initializes a new instance of the <see cref="ColumnDefinitions"/> class.
         /// </summary>
-        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));
+        }
+
         /// <summary>
         /// Parses a string representation of column definitions collection.
         /// </summary>
@@ -34,4 +40,4 @@ namespace Avalonia.Controls
         /// <returns>The <see cref="ColumnDefinitions"/>.</returns>
         public static ColumnDefinitions Parse(string s) => new ColumnDefinitions(s);
     }
-}
+}

+ 1 - 1
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)

+ 19 - 5
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<EventArgs, ItemsRepeater>(
+                    oldValue,
+                    nameof(AttachedLayout.MeasureInvalidated),
+                    InvalidateMeasureForLayout);
+                WeakEventHandlerManager.Unsubscribe<EventArgs, ItemsRepeater>(
+                    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<AttachedLayout, EventArgs, ItemsRepeater>(
+                    newValue,
+                    nameof(AttachedLayout.MeasureInvalidated),
+                    InvalidateMeasureForLayout);
+                WeakEventHandlerManager.Subscribe<AttachedLayout, EventArgs, ItemsRepeater>(
+                    newValue,
+                    nameof(AttachedLayout.ArrangeInvalidated),
+                    InvalidateArrangeForLayout);
             }
 
             bool isVirtualizingLayout = newValue != null && newValue is VirtualizingLayout;

+ 31 - 20
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);
         }
     }
 }

+ 10 - 12
src/Avalonia.Input/AccessKeyHandler.cs

@@ -28,7 +28,7 @@ namespace Avalonia.Input
         /// <summary>
         /// The window to which the handler belongs.
         /// </summary>
-        private IInputRoot _owner;
+        private IInputRoot? _owner;
 
         /// <summary>
         /// Whether access keys are currently being shown;
@@ -48,17 +48,17 @@ namespace Avalonia.Input
         /// <summary>
         /// Element to restore following AltKey taking focus.
         /// </summary>
-        private IInputElement _restoreFocusElement;
+        private IInputElement? _restoreFocusElement;
 
         /// <summary>
         /// The window's main menu.
         /// </summary>
-        private IMainMenu _mainMenu;
+        private IMainMenu? _mainMenu;
 
         /// <summary>
         /// Gets or sets the window's main menu.
         /// </summary>
-        public IMainMenu MainMenu
+        public IMainMenu? MainMenu
         {
             get => _mainMenu;
             set
@@ -86,14 +86,12 @@ namespace Avalonia.Input
         /// </remarks>
         public void SetOwner(IInputRoot owner)
         {
-            Contract.Requires<ArgumentNullException>(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
         /// </summary>
         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;
         }
     }
 }

+ 2 - 0
src/Avalonia.Input/Avalonia.Input.csproj

@@ -1,6 +1,8 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <TargetFramework>netstandard2.0</TargetFramework>
+    <Nullable>Enable</Nullable>
+    <WarningsAsErrors>CS8600;CS8602;CS8603</WarningsAsErrors>
   </PropertyGroup>
   <ItemGroup>
     <ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />

+ 3 - 3
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<string> GetFileNames()
+        public IEnumerable<string>? GetFileNames()
         {
             return Get(DataFormats.FileNames) as IEnumerable<string>;
         }
 
-        public string GetText()
+        public string? GetText()
         {
             return Get(DataFormats.Text) as string;
         }

+ 3 - 3
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<Interactive>()?.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<DragEventArgs> routedEvent, DragDropEffects operation, IDataObject data, KeyModifiers modifiers)
+        private DragDropEffects RaiseDragEvent(Interactive? target, IInputRoot inputRoot, Point point, RoutedEvent<DragEventArgs> routedEvent, DragDropEffects operation, IDataObject data, KeyModifiers modifiers)
         {
             if (target == null)
                 return DragDropEffects.None;

+ 24 - 23
src/Avalonia.Input/FocusManager.cs

@@ -15,8 +15,8 @@ namespace Avalonia.Input
         /// <summary>
         /// The focus scopes in which the focus is currently defined.
         /// </summary>
-        private readonly ConditionalWeakTable<IFocusScope, IInputElement> _focusScopes =
-            new ConditionalWeakTable<IFocusScope, IInputElement>();
+        private readonly ConditionalWeakTable<IFocusScope, IInputElement?> _focusScopes =
+            new ConditionalWeakTable<IFocusScope, IInputElement?>();
 
         /// <summary>
         /// Initializes a new instance of the <see cref="FocusManager"/> class.
@@ -37,12 +37,12 @@ namespace Avalonia.Input
         /// <summary>
         /// Gets the currently focused <see cref="IInputElement"/>.
         /// </summary>
-        public IInputElement Current => KeyboardDevice.Instance?.FocusedElement;
+        public IInputElement? Current => KeyboardDevice.Instance?.FocusedElement;
 
         /// <summary>
         /// Gets the current focus scope.
         /// </summary>
-        public IFocusScope Scope
+        public IFocusScope? Scope
         {
             get;
             private set;
@@ -55,7 +55,7 @@ namespace Avalonia.Input
         /// <param name="method">The method by which focus was changed.</param>
         /// <param name="keyModifiers">Any key modifiers active at the time of focus.</param>
         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
         /// </remarks>
         public void SetFocusedElement(
             IFocusScope scope,
-            IInputElement element,
+            IInputElement? element,
             NavigationMethod method = NavigationMethod.Unspecified,
             KeyModifiers keyModifiers = KeyModifiers.None)
         {
-            Contract.Requires<ArgumentNullException>(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
         /// <param name="scope">The new focus scope.</param>
         public void SetFocusScope(IFocusScope scope)
         {
-            Contract.Requires<ArgumentNullException>(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
         /// <returns>The focus scopes.</returns>
         private static IEnumerable<IFocusScope> 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<IInputElement>() ??
-                    ((control as IHostedVisualTreeRoot)?.Host as IInputElement);
+                c = c.GetVisualParent<IInputElement>() ??
+                    ((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)
                 {

+ 7 - 9
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<IGestureRecognizer>, IGestureRecognizerActionsDispatcher
     {
         private readonly IInputElement _inputElement;
-        private List<IGestureRecognizer> _recognizers;
-        private Dictionary<IPointer, IGestureRecognizer> _pointerGrabs;
+        private List<IGestureRecognizer>? _recognizers;
+        private Dictionary<IPointer, IGestureRecognizer>? _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);

+ 7 - 8
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));
 
 
 

+ 8 - 1
src/Avalonia.Input/Gestures.cs

@@ -29,7 +29,9 @@ namespace Avalonia.Input
             RoutedEvent.Register<ScrollGestureEventArgs>(
                 "ScrollGestureEnded", RoutingStrategies.Bubble, typeof(Gestures));
 
+#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
         private static WeakReference<IInteractive> s_lastPress = new WeakReference<IInteractive>(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<IInteractive>(e.Source);
+                    s_lastPress = new WeakReference<IInteractive>(ev.Source);
                 }
                 else if (s_lastPress != null && e.ClickCount == 2 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed)
                 {

+ 1 - 1
src/Avalonia.Input/IAccessKeyHandler.cs

@@ -8,7 +8,7 @@ namespace Avalonia.Input
         /// <summary>
         /// Gets or sets the window's main menu.
         /// </summary>
-        IMainMenu MainMenu { get; set; }
+        IMainMenu? MainMenu { get; set; }
 
         /// <summary>
         /// Sets the owner of the access key handler.

+ 3 - 3
src/Avalonia.Input/IDataObject.cs

@@ -23,17 +23,17 @@ namespace Avalonia.Input
         /// Returns the dragged text if the DataObject contains any text.
         /// <seealso cref="DataFormats.Text"/>
         /// </summary>
-        string GetText();
+        string? GetText();
 
         /// <summary>
         /// Returns a list of filenames if the DataObject contains filenames.
         /// <seealso cref="DataFormats.FileNames"/>
         /// </summary>
-        IEnumerable<string> GetFileNames();
+        IEnumerable<string>? GetFileNames();
         
         /// <summary>
         /// Tries to get the data of the given DataFormat.
         /// </summary>
-        object Get(string dataFormat);
+        object? Get(string dataFormat);
     }
 }

+ 3 - 3
src/Avalonia.Input/IFocusManager.cs

@@ -8,12 +8,12 @@ namespace Avalonia.Input
         /// <summary>
         /// Gets the currently focused <see cref="IInputElement"/>.
         /// </summary>
-        IInputElement Current { get; }
+        IInputElement? Current { get; }
 
         /// <summary>
         /// Gets the current focus scope.
         /// </summary>
-        IFocusScope Scope { get; }
+        IFocusScope? Scope { get; }
 
         /// <summary>
         /// Focuses a control.
@@ -22,7 +22,7 @@ namespace Avalonia.Input
         /// <param name="method">The method by which focus was changed.</param>
         /// <param name="keyModifiers">Any key modifiers active at the time of focus.</param>
         void Focus(
-            IInputElement control,
+            IInputElement? control,
             NavigationMethod method = NavigationMethod.Unspecified,
             KeyModifiers keyModifiers = KeyModifiers.None);
 

+ 1 - 1
src/Avalonia.Input/IInputElement.cs

@@ -78,7 +78,7 @@ namespace Avalonia.Input
         /// <summary>
         /// Gets or sets the associated mouse cursor.
         /// </summary>
-        Cursor Cursor { get; }
+        Cursor? Cursor { get; }
 
         /// <summary>
         /// Gets a value indicating whether this control and all its parents are enabled.

+ 2 - 2
src/Avalonia.Input/IInputRoot.cs

@@ -20,7 +20,7 @@ namespace Avalonia.Input
         /// <summary>
         /// Gets or sets the input element that the pointer is currently over.
         /// </summary>
-        IInputElement PointerOverElement { get; set; }
+        IInputElement? PointerOverElement { get; set; }
 
         /// <summary>
         /// 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
         /// </summary>
         [CanBeNull]
-        IMouseDevice MouseDevice { get; }
+        IMouseDevice? MouseDevice { get; }
     }
 }

+ 2 - 2
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);
     }

+ 2 - 2
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; }
         

+ 2 - 2
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);

+ 4 - 4
src/Avalonia.Input/InputElement.cs

@@ -37,8 +37,8 @@ namespace Avalonia.Input
         /// <summary>
         /// Gets or sets associated mouse cursor.
         /// </summary>
-        public static readonly StyledProperty<Cursor> CursorProperty =
-            AvaloniaProperty.Register<InputElement, Cursor>(nameof(Cursor), null, true);
+        public static readonly StyledProperty<Cursor?> CursorProperty =
+            AvaloniaProperty.Register<InputElement, Cursor?>(nameof(Cursor), null, true);
 
         /// <summary>
         /// Defines the <see cref="IsFocused"/> property.
@@ -160,7 +160,7 @@ namespace Avalonia.Input
         private bool _isFocused;
         private bool _isFocusVisible;
         private bool _isPointerOver;
-        private GestureRecognizerCollection _gestureRecognizers;
+        private GestureRecognizerCollection? _gestureRecognizers;
 
         /// <summary>
         /// Initializes static members of the <see cref="InputElement"/> class.
@@ -336,7 +336,7 @@ namespace Avalonia.Input
         /// <summary>
         /// Gets or sets associated mouse cursor.
         /// </summary>
-        public Cursor Cursor
+        public Cursor? Cursor
         {
             get { return GetValue(CursorProperty); }
             set { SetValue(CursorProperty, value); }

+ 1 - 1
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; }
 

+ 4 - 4
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<IKeyboardDevice>();
 
@@ -18,7 +18,7 @@ namespace Avalonia.Input
 
         public IFocusManager FocusManager => AvaloniaLocator.Current.GetService<IFocusManager>();
 
-        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)
         {

+ 5 - 8
src/Avalonia.Input/KeyboardNavigationHandler.cs

@@ -13,7 +13,7 @@ namespace Avalonia.Input
         /// <summary>
         /// The window to which the handler belongs.
         /// </summary>
-        private IInputRoot _owner;
+        private IInputRoot? _owner;
 
         /// <summary>
         /// Sets the owner of the keyboard navigation handler.
@@ -24,15 +24,12 @@ namespace Avalonia.Input
         /// </remarks>
         public void SetOwner(IInputRoot owner)
         {
-            Contract.Requires<ArgumentNullException>(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 <paramref name="element"/>
         /// was the last in the requested direction.
         /// </returns>
-        public static IInputElement GetNext(
+        public static IInputElement? GetNext(
             IInputElement element,
             NavigationDirection direction)
         {
-            Contract.Requires<ArgumentNullException>(element != null);
+            element = element ?? throw new ArgumentNullException(nameof(element));
 
             var customHandler = element.GetSelfAndVisualAncestors()
                 .OfType<ICustomKeyboardNavigation>()
@@ -97,7 +94,7 @@ namespace Avalonia.Input
             NavigationDirection direction,
             KeyModifiers keyModifiers = KeyModifiers.None)
         {
-            Contract.Requires<ArgumentNullException>(element != null);
+            element = element ?? throw new ArgumentNullException(nameof(element));
 
             var next = GetNext(element, direction);
 

+ 39 - 34
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
         /// <see cref="Capture"/> method.
         /// </remarks>
         [Obsolete("Use IPointer instead")]
-        public IInputElement Captured => _pointer.Captured;
+        public IInputElement? Captured => _pointer.Captured;
 
         /// <summary>
         /// 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 <see cref="Captured"/> property.
         /// </remarks>
-        public void Capture(IInputElement control)
+        public void Capture(IInputElement? control)
         {
             _pointer.Capture(control);
         }
@@ -66,7 +66,7 @@ namespace Avalonia.Input
         /// <returns>The mouse position in the control's coordinates.</returns>
         public Point GetPosition(IVisual relativeTo)
         {
-            Contract.Requires<ArgumentNullException>(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<ArgumentNullException>(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<ArgumentNullException>(device != null);
-            Contract.Requires<ArgumentNullException>(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<ArgumentNullException>(device != null);
-            Contract.Requires<ArgumentNullException>(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<ArgumentNullException>(device != null);
-            Contract.Requires<ArgumentNullException>(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<ArgumentNullException>(device != null);
-            Contract.Requires<ArgumentNullException>(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<ArgumentNullException>(device != null);
-            Contract.Requires<ArgumentNullException>(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<ArgumentNullException>(hit != null);
+            hit = hit ?? throw new ArgumentNullException(nameof(hit));
 
             return _pointer.Captured ??
                 (hit as IInteractive) ??
                 hit.GetSelfAndVisualAncestors().OfType<IInteractive>().FirstOrDefault();
         }
 
-        private IInputElement HitTest(IInputElement root, Point p)
+        private IInputElement? HitTest(IInputElement root, Point p)
         {
-            Contract.Requires<ArgumentNullException>(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<ArgumentNullException>(device != null);
-            Contract.Requires<ArgumentNullException>(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<ArgumentNullException>(device != null);
-            Contract.Requires<ArgumentNullException>(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<ArgumentNullException>(device != null);
-            Contract.Requires<ArgumentNullException>(root != null);
-            Contract.Requires<ArgumentNullException>(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;
 

+ 23 - 19
src/Avalonia.Input/Navigation/TabNavigation.cs

@@ -22,15 +22,17 @@ namespace Avalonia.Input.Navigation
         /// The next element in the specified direction, or null if <paramref name="element"/>
         /// was the last in the requested direction.
         /// </returns>
-        public static IInputElement GetNextInTabOrder(
+        public static IInputElement? GetNextInTabOrder(
             IInputElement element,
             NavigationDirection direction,
             bool outsideElement = false)
         {
-            Contract.Requires<ArgumentNullException>(element != null);
-            Contract.Requires<ArgumentException>(
-                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<IInputElement>();
 
@@ -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 <paramref name="element"/> to find next control.
         /// </param>
         /// <returns>The next element, or null if the element is the last.</returns>
-        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
         /// <param name="container">The container.</param>
         /// <param name="direction">The direction of the search.</param>
         /// <returns>The first element, or null if there are no more elements.</returns>
-        private static IInputElement GetFirstInNextContainer(
+        private static IInputElement? GetFirstInNextContainer(
             IInputElement element,
             IInputElement container,
             NavigationDirection direction)
         {
             var parent = container.GetVisualParent<IInputElement>();
-            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)

+ 4 - 5
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<IInputElement>().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; }

+ 7 - 7
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;

+ 1 - 1
src/Avalonia.Input/Raw/RawDragEventType.cs

@@ -7,4 +7,4 @@
         DragLeave,
         Drop
     }
-}
+}

+ 1 - 1
src/Avalonia.Input/Raw/RawInputEventArgs.cs

@@ -21,7 +21,7 @@ namespace Avalonia.Input.Raw
         /// <param name="root">The root from which the event originates.</param>
         public RawInputEventArgs(IInputDevice device, ulong timestamp, IInputRoot root)
         {
-            Contract.Requires<ArgumentNullException>(device != null);
+            device = device ?? throw new ArgumentNullException(nameof(device));
 
             Device = device;
             Timestamp = timestamp;

+ 2 - 2
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; }
     }
 }

+ 2 - 2
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);
         }
 
         /// <summary>

+ 1 - 4
src/Avalonia.OpenGL/AngleOptions.cs

@@ -10,9 +10,6 @@ namespace Avalonia.OpenGL
 			DirectX11
         }
 
-        public List<PlatformApi> AllowedPlatformApis = new List<PlatformApi>
-        {
-            PlatformApi.DirectX9
-        };
+        public IList<PlatformApi> AllowedPlatformApis { get; set; } = null;
     }
 }

+ 3 - 0
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;

+ 5 - 1
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<AngleOptions>()?.AllowedPlatformApis
-                                      ?? new List<AngleOptions.PlatformApi> {AngleOptions.PlatformApi.DirectX9};
+                                      ?? new []
+                                      {
+                                          AngleOptions.PlatformApi.DirectX11, 
+                                          AngleOptions.PlatformApi.DirectX9
+                                      };
 
                     foreach (var platformApi in allowedApis)
                     {

+ 2 - 2
src/Avalonia.Visuals/Visual.cs

@@ -455,7 +455,7 @@ namespace Avalonia
         }
 
         /// <summary>
-        /// Called when the control is added to a visual tree.
+        /// Called when the control is added to a rooted visual tree.
         /// </summary>
         /// <param name="e">The event args.</param>
         protected virtual void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
@@ -463,7 +463,7 @@ namespace Avalonia
         }
 
         /// <summary>
-        /// Called when the control is removed from a visual tree.
+        /// Called when the control is removed from a rooted visual tree.
         /// </summary>
         /// <param name="e">The event args.</param>
         protected virtual void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)

+ 5 - 1
src/Skia/Avalonia.Skia/SkiaOptions.cs

@@ -16,6 +16,10 @@ namespace Avalonia
         /// <summary>
         /// The maximum number of bytes for video memory to store textures and resources.
         /// </summary>
-        public long? MaxGpuResourceSizeBytes { get; set; }
+        /// <remarks>
+        /// This is set by default to the recommended value for Avalonia.
+        /// Setting this to null will give you the default Skia value.
+        /// </remarks>
+        public long? MaxGpuResourceSizeBytes { get; set; } = 1024 * 600 * 4 * 12; // ~28mb 12x 1024 x 600 textures. 
     }
 }

+ 2 - 0
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);

+ 31 - 0
tests/Avalonia.LeakTests/ControlTests.cs

@@ -552,6 +552,37 @@ namespace Avalonia.LeakTests
             }
         }
 
+        [Fact]
+        public void ItemsRepeater_Is_Freed()
+        {
+            using (Start())
+            {
+                Func<Window> run = () =>
+                {
+                    var window = new Window
+                    {
+                        Content = new ItemsRepeater(),
+                    };
+
+                    window.Show();
+
+                    window.LayoutManager.ExecuteInitialLayoutPass();
+                    Assert.IsType<ItemsRepeater>(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<ItemsRepeater>()).ObjectsCount));
+            }
+        }
+
         private IDisposable Start()
         {
             return UnitTestApplication.Start(TestServices.StyledWindow.With(