Kaynağa Gözat

Merge branch 'master' into FixBindingStackOverflow

Steven Kirk 8 yıl önce
ebeveyn
işleme
b2886a5e8a

+ 5 - 0
.ncrunch/Avalonia.Designer.HostApp.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
+  </Settings>
+</ProjectConfiguration>

+ 0 - 1
.travis.yml

@@ -1,7 +1,6 @@
 language: csharp
 os:
   - linux
-  - osx
 dist: trusty
 osx_image: xcode8.3
 env:

+ 4 - 5
scripts/ReplaceNugetCache.ps1

@@ -1,5 +1,4 @@
-copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netcoreapp1.0\
-copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard1.3\
-copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard1.3\
-copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.skia.desktop\$args\lib\netstandard1.3\
-copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard1.3\
+copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netcoreapp2.0\
+copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard2.0\
+copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard2.0\
+copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard2.0\

+ 3 - 4
scripts/ReplaceNugetCache.sh

@@ -1,7 +1,6 @@
  #!/usr/bin/env bash
  
- cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netcoreapp1.0/
- cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netstandard1.1/
- cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia.gtk3/$1/lib/netstandard1.1/
- cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia.skia.desktop/$1/lib/netstandard1.3/
+ cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netcoreapp2.0/
+ cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netstandard2.0/
+ cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia.gtk3/$1/lib/netstandard2.0/
  

+ 4 - 5
scripts/ReplaceNugetCacheRelease.ps1

@@ -1,5 +1,4 @@
-copy ..\samples\ControlCatalog.NetCore\bin\Release\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netcoreapp1.0\
-copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard1.3\
-copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard1.3\
-copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.skia.desktop\$args\lib\netstandard1.3\
-copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard1.3\
+copy ..\samples\ControlCatalog.NetCore\bin\Release\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netcoreapp2.0\
+copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard2.0\
+copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard2.0\
+copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard2.0\

+ 4 - 6
src/Avalonia.Controls/Control.cs

@@ -656,7 +656,6 @@ namespace Avalonia.Controls
         /// <param name="e">The event args.</param>
         protected virtual void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
         {
-            AttachedToLogicalTree?.Invoke(this, e);
         }
 
         /// <summary>
@@ -665,7 +664,6 @@ namespace Avalonia.Controls
         /// <param name="e">The event args.</param>
         protected virtual void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
         {
-            DetachedFromLogicalTree?.Invoke(this, e);
         }
 
         /// <inheritdoc/>
@@ -773,11 +771,9 @@ namespace Avalonia.Controls
         {
             while (e != null)
             {
-                var root = e as IStyleRoot;
-
-                if (root != null && root.StylingParent == null)
+                if (e is IRenderRoot root)
                 {
-                    return root;
+                    return root as IStyleRoot;
                 }
 
                 e = e.StylingParent;
@@ -844,6 +840,7 @@ namespace Avalonia.Controls
                 InitializeStylesIfNeeded(true);
 
                 OnAttachedToLogicalTree(e);
+                AttachedToLogicalTree?.Invoke(this, e);
             }
 
             foreach (var child in LogicalChildren.OfType<Control>())
@@ -864,6 +861,7 @@ namespace Avalonia.Controls
                 _isAttachedToLogicalTree = false;
                 _styleDetach.OnNext(this);
                 OnDetachedFromLogicalTree(e);
+                DetachedFromLogicalTree?.Invoke(this, e);
 
                 foreach (var child in LogicalChildren.OfType<Control>())
                 {

+ 10 - 10
src/Avalonia.Controls/DropDown.cs

@@ -120,21 +120,21 @@ namespace Avalonia.Controls
         /// <inheritdoc/>
         protected override void OnPointerPressed(PointerPressedEventArgs e)
         {
-            if (!IsDropDownOpen && ((IVisual)e.Source).GetVisualRoot() is PopupRoot)
-            {
-                IsDropDownOpen = true;
-                e.Handled = true;
-            }
-
             if (!e.Handled)
             {
-                if (UpdateSelectionFromEventSource(e.Source))
+                if (((IVisual)e.Source).GetVisualRoot() is PopupRoot)
                 {
-                    _popup?.Close();
-                    e.Handled = true;
+                    if (UpdateSelectionFromEventSource(e.Source))
+                    {
+                        _popup?.Close();
+                        e.Handled = true;
+                    }
+                }
+                else
+                {
+                    IsDropDownOpen = !IsDropDownOpen;
                 }
             }
-
             base.OnPointerPressed(e);
         }
 

+ 20 - 2
src/Avalonia.Input/MouseDevice.cs

@@ -20,6 +20,8 @@ namespace Avalonia.Input
         private int _clickCount;
         private Rect _lastClickRect;
         private uint _lastClickTime;
+        private IInputElement _captured;
+        private IDisposable _capturedSubscription;
        
         /// <summary>
         /// Gets the control that is currently capturing by the mouse, if any.
@@ -31,8 +33,23 @@ namespace Avalonia.Input
         /// </remarks>
         public IInputElement Captured
         {
-            get;
-            protected set;
+            get => _captured;
+            protected set
+            {
+                _capturedSubscription?.Dispose();
+                _capturedSubscription = null;
+
+                if (value != null)
+                {
+                    _capturedSubscription = Observable.FromEventPattern<VisualTreeAttachmentEventArgs>(
+                        x => value.DetachedFromVisualTree += x,
+                        x => value.DetachedFromVisualTree -= x)
+                        .Take(1)
+                        .Subscribe(_ => Captured = null);
+                }
+
+                _captured = value;
+            }
         }
         
         /// <summary>
@@ -55,6 +72,7 @@ namespace Avalonia.Input
         /// </remarks>
         public virtual void Capture(IInputElement control)
         {
+            // TODO: Check visibility and enabled state before setting capture.
             Captured = control;
         }
 

+ 1 - 1
src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs

@@ -173,7 +173,7 @@ namespace Avalonia.Rendering.SceneGraph
                         }
                     }
 
-                    if (node.HitTest(p))
+                    if (node.HitTest(p) && node.Visual.IsAttachedToVisualTree)
                     {
                         yield return node.Visual;
                     }

+ 5 - 2
src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs

@@ -168,11 +168,14 @@ namespace Avalonia.Rendering.SceneGraph
                 using (context.PushTransformContainer())
                 {
                     var startLayer = opacity < 1 || visual.OpacityMask != null;
+                    var clipBounds = bounds.TransformToAABB(contextImpl.Transform).Intersect(clip);
 
-                    forceRecurse = forceRecurse || node.Transform != contextImpl.Transform;
+                    forceRecurse = forceRecurse ||
+                        node.Transform != contextImpl.Transform ||
+                        node.ClipBounds != clipBounds;
 
                     node.Transform = contextImpl.Transform;
-                    node.ClipBounds = bounds.TransformToAABB(node.Transform).Intersect(clip);
+                    node.ClipBounds = clipBounds;
                     node.ClipToBounds = clipToBounds;
                     node.GeometryClip = visual.Clip?.PlatformImpl;
                     node.Opacity = opacity;

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

@@ -329,6 +329,7 @@ namespace Avalonia
             }
 
             OnAttachedToVisualTree(e);
+            AttachedToVisualTree?.Invoke(this, e);
             InvalidateVisual();
 
             if (VisualChildren != null)
@@ -357,6 +358,7 @@ namespace Avalonia
             }
 
             OnDetachedFromVisualTree(e);
+            DetachedFromVisualTree?.Invoke(this, e);
             e.Root?.Renderer?.AddDirty(this);
 
             if (VisualChildren != null)
@@ -374,7 +376,6 @@ namespace Avalonia
         /// <param name="e">The event args.</param>
         protected virtual void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
         {
-            AttachedToVisualTree?.Invoke(this, e);
         }
 
         /// <summary>
@@ -383,7 +384,6 @@ namespace Avalonia
         /// <param name="e">The event args.</param>
         protected virtual void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
         {
-            DetachedFromVisualTree?.Invoke(this, e);
         }
 
         /// <summary>

+ 40 - 1
tests/Avalonia.Controls.UnitTests/ControlTests.cs

@@ -123,7 +123,26 @@ namespace Avalonia.Controls.UnitTests
         }
 
         [Fact]
-        public void DetachedToLogicalParent_Should_Be_Called_When_Removed_From_Tree()
+        public void AttachedToLogicalParent_Should_Not_Be_Called_With_GlobalStyles_As_Root()
+        {
+            var globalStyles = Mock.Of<IGlobalStyles>();
+            var root = new TestRoot { StylingParent = globalStyles };
+            var child = new Border();
+            var raised = false;
+
+            child.AttachedToLogicalTree += (s, e) =>
+            {
+                Assert.Equal(root, e.Root);
+                raised = true;
+            };
+
+            root.Child = child;
+
+            Assert.True(raised);
+        }
+
+        [Fact]
+        public void DetachedFromLogicalParent_Should_Be_Called_When_Removed_From_Tree()
         {
             var root = new TestRoot();
             var parent = new Border();
@@ -148,6 +167,26 @@ namespace Avalonia.Controls.UnitTests
             Assert.True(grandchildRaised);
         }
 
+        [Fact]
+        public void DetachedFromLogicalParent_Should_Not_Be_Called_With_GlobalStyles_As_Root()
+        {
+            var globalStyles = Mock.Of<IGlobalStyles>();
+            var root = new TestRoot { StylingParent = globalStyles };
+            var child = new Border();
+            var raised = false;
+
+            child.DetachedFromLogicalTree += (s, e) =>
+            {
+                Assert.Equal(root, e.Root);
+                raised = true;
+            };
+
+            root.Child = child;
+            root.Child = null;
+
+            Assert.True(raised);
+        }
+
         [Fact]
         public void Adding_Tree_To_IStyleRoot_Should_Style_Controls()
         {

+ 24 - 2
tests/Avalonia.Controls.UnitTests/DropDownTests.cs

@@ -1,21 +1,43 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
-using System.Linq;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Shapes;
 using Avalonia.Controls.Templates;
+using Avalonia.Input;
 using Avalonia.LogicalTree;
 using Avalonia.Media;
 using Avalonia.UnitTests;
-using Avalonia.VisualTree;
 using Xunit;
 
 namespace Avalonia.Controls.UnitTests
 {
     public class DropDownTests
     {
+        [Fact]
+        public void Clicking_On_Control_Toggles_IsDropDownOpen()
+        {
+            var target = new DropDown
+            {
+                Items = new[] { "Foo", "Bar" },
+            };
+
+            target.RaiseEvent(new PointerPressedEventArgs
+            {
+                RoutedEvent = InputElement.PointerPressedEvent,
+            });
+
+            Assert.True(target.IsDropDownOpen);
+
+            target.RaiseEvent(new PointerPressedEventArgs
+            {
+                RoutedEvent = InputElement.PointerPressedEvent,
+            });
+
+            Assert.False(target.IsDropDownOpen);
+        }
+
         [Fact]
         public void SelectionBoxItem_Is_Rectangle_With_VisualBrush_When_Selection_Is_Control()
         {

+ 4 - 0
tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs

@@ -12,6 +12,7 @@ using Moq;
 using System;
 using System.Linq;
 using Xunit;
+using Avalonia.Rendering;
 
 namespace Avalonia.Controls.UnitTests.Presenters
 {
@@ -56,6 +57,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
 
             var parentMock = new Mock<Control>();
             parentMock.As<IContentPresenterHost>();
+            parentMock.As<IRenderRoot>();
             parentMock.As<IStyleRoot>();
 
             (target as ISetLogicalParent).SetParent(parentMock.Object);
@@ -100,6 +102,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
             };
 
             var parentMock = new Mock<Control>();
+            parentMock.As<IRenderRoot>();
             parentMock.As<IStyleRoot>();
             parentMock.As<ILogical>().SetupGet(l => l.IsAttachedToLogicalTree).Returns(true);
 
@@ -144,6 +147,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
 
             var parentMock = new Mock<Control>();
             parentMock.As<IContentPresenterHost>();
+            parentMock.As<IRenderRoot>();
             parentMock.As<IStyleRoot>();
 
             (target as ISetLogicalParent).SetParent(parentMock.Object);

+ 18 - 1
tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs

@@ -1,6 +1,5 @@
 using Avalonia.Controls;
 using Avalonia.Input.Raw;
-using Avalonia.Layout;
 using Avalonia.Rendering;
 using Avalonia.UnitTests;
 using Avalonia.VisualTree;
@@ -12,6 +11,24 @@ namespace Avalonia.Input.UnitTests
 {
     public class MouseDeviceTests
     {
+        [Fact]
+        public void Capture_Is_Cleared_When_Control_Removed()
+        {
+            Canvas control;
+            var root = new TestRoot
+            {
+                Child = control = new Canvas(),
+            };
+            var target = new MouseDevice();
+
+            target.Capture(control);
+            Assert.Same(control, target.Captured);
+
+            root.Child = null;
+
+            Assert.Null(target.Captured);
+        }
+
         [Fact]
         public void MouseMove_Should_Update_PointerOver()
         {

+ 51 - 3
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs

@@ -143,13 +143,61 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
 
                 var borderNode = scene.FindNode(border);
                 Assert.Equal(new Rect(50, 50, 50, 50), borderNode.ClipBounds);
+            }
+        }
+
+        [Fact]
+        public void Should_Update_Descendent_ClipBounds_When_Margin_Changed()
+        {
+            using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
+            {
+                Border border;
+                Canvas canvas;
+                var tree = new TestRoot
+                {
+                    Width = 200,
+                    Height = 300,
+                    Child = canvas = new Canvas
+                    {
+                        ClipToBounds = true,
+                        Width = 100,
+                        Height = 100,
+                        HorizontalAlignment = HorizontalAlignment.Left,
+                        VerticalAlignment = VerticalAlignment.Top,
+                        Children =
+                        {
+                            (border = new Border
+                            {
+                                Background = Brushes.AliceBlue,
+                                Width = 100,
+                                Height = 100,
+                                [Canvas.LeftProperty] = 50,
+                                [Canvas.TopProperty] = 50,
+                            })
+                        }
+                    }
+                };
+
+                tree.Measure(Size.Infinity);
+                tree.Arrange(new Rect(tree.DesiredSize));
 
-                // Initial ClipBounds are correct, make sure they're still correct after updating border.
+                var scene = new Scene(tree);
+                var sceneBuilder = new SceneBuilder();
+                sceneBuilder.UpdateAll(scene);
+
+                var borderNode = scene.FindNode(border);
+                Assert.Equal(new Rect(50, 50, 50, 50), borderNode.ClipBounds);
+
+                canvas.Width = canvas.Height = 125;
+                canvas.Measure(Size.Infinity);
+                canvas.Arrange(new Rect(tree.DesiredSize));
+
+                // Initial ClipBounds are correct, make sure they're still correct after updating canvas.
                 scene = scene.Clone();
-                Assert.True(sceneBuilder.Update(scene, border));
+                Assert.True(sceneBuilder.Update(scene, canvas));
 
                 borderNode = scene.FindNode(border);
-                Assert.Equal(new Rect(50, 50, 50, 50), borderNode.ClipBounds);
+                Assert.Equal(new Rect(50, 50, 75, 75), borderNode.ClipBounds);
             }
         }