[Android] Support multiple clipping settings in single layout (#3559)
authorE.Z. Hart <hartez@users.noreply.github.com>
Wed, 5 Sep 2018 18:15:30 +0000 (12:15 -0600)
committerJason Smith <jas@microsoft.com>
Wed, 5 Sep 2018 18:15:29 +0000 (11:15 -0700)
* Repro

* Fix for API 18+

Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/MultipleClipToBounds.cs [new file with mode: 0644]
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
Xamarin.Forms.Platform.Android/VisualElementTracker.cs

diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/MultipleClipToBounds.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/MultipleClipToBounds.cs
new file mode 100644 (file)
index 0000000..2426471
--- /dev/null
@@ -0,0 +1,101 @@
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+
+#if UITEST
+using Xamarin.Forms.Core.UITests;
+using Xamarin.UITest;
+using NUnit.Framework;
+#endif
+
+namespace Xamarin.Forms.Controls.Issues
+{
+#if UITEST
+       [Category(UITestCategories.ManualReview)]
+#endif
+       [Preserve(AllMembers = true)]
+       [Issue(IssueTracker.None, 8088, "Mixing IsClippedToBounds settings in a single layout", PlatformAffected.Android)]
+       public class MultipleClipToBounds : TestContentPage
+       {
+               Label _label1;
+               Label _label2;
+               ContentView _layout1;
+               ContentView _layout2;
+
+               Button _button1;
+               Button _button2;
+
+               protected override void Init()
+               {
+                       var layout = new StackLayout();
+
+                       var instructions = new Label
+                       {
+                               Text = "Toggle the IsClippedToBounds settings below. Toggling the settings "
+                                               + "for the first layout should not affect the second layout " 
+                                               + "(and vice versa). If toggling the settings for one layout affects" 
+                                               + " the other layout, this test has failed."
+                       };
+
+                       var box1 = new BoxView { BackgroundColor = Color.Coral, TranslationX = -10, TranslationY = -10 };
+                       var box2 = new BoxView { BackgroundColor = Color.LightGreen, TranslationX = -10, TranslationY = -10 };
+
+                       _layout1 = new ContentView
+                       {
+                               BackgroundColor = Color.RosyBrown,
+                               Margin = new Thickness(50),
+                               Content = box1
+                       };
+
+                       _label1 = new Label();
+
+                       _layout2 = new ContentView
+                       {
+                               BackgroundColor = Color.RosyBrown,
+                               Margin = new Thickness(50),
+                               Content = box2
+                       };
+
+                       _label2 = new Label();
+
+                       _button1 = new Button();
+
+                       _button2 = new Button();
+
+                       _button1.Clicked += (sender, args) =>
+                       {
+                               _layout1.IsClippedToBounds = !_layout1.IsClippedToBounds;
+                               UpdateLabels();
+                       };
+
+                       _button2.Clicked += (sender, args) =>
+                       {
+                               _layout2.IsClippedToBounds = !_layout2.IsClippedToBounds;
+                               UpdateLabels();
+                       };
+
+                       layout.Children.Add(instructions);
+                       layout.Children.Add(_button1);
+                       layout.Children.Add(_button2);
+                       layout.Children.Add(_layout1);
+                       layout.Children.Add(_label1);
+                       layout.Children.Add(_layout2);
+                       layout.Children.Add(_label2);
+
+                       UpdateLabels();
+
+                       Content = layout;
+               }
+
+               void UpdateLabels()
+               {
+                       _label1.Text =
+                               $"The coral Box above {(_layout1.IsClippedToBounds ? "should" : "should not")} be clipped by the brown container.";
+
+                       _label2.Text =
+                               $"The green Box above {(_layout2.IsClippedToBounds ? "should" : "should not")} be clipped by the brown container.";
+
+                       _button1.Text = $"Toggle L1 (currently {_layout1.IsClippedToBounds})";
+                       _button2.Text = $"Toggle L2 (currently {_layout2.IsClippedToBounds})";
+               }
+       }
+}
\ No newline at end of file
index 96f06ac..d93a699 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)Bugzilla39829.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Bugzilla39458.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Bugzilla39853.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)MultipleClipToBounds.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)PerformanceGallery\PerformanceDataManager.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)PerformanceGallery\PerformanceGallery.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)PerformanceGallery\PerformanceScenario.cs" />
index 66896d4..9ef374e 100644 (file)
@@ -2,6 +2,7 @@ using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using Android.Content;
+using Android.Graphics;
 using Android.OS;
 using Android.Views;
 using AView = Android.Views.View;
@@ -275,19 +276,39 @@ namespace Xamarin.Forms.Platform.Android
 
                void UpdateClipToBounds()
                {
-                       var layout = _renderer.Element as Layout;
-                       var parent = _renderer.View.Parent as ViewGroup;
-
-                       if (parent == null || layout == null)
+                       if (!(_renderer.Element is Layout layout))
+                       {
                                return;
+                       }
 
                        bool shouldClip = layout.IsClippedToBounds;
 
-                       if ((int)Build.VERSION.SdkInt >= 18 && parent.ClipChildren == shouldClip)
-                               return;
+                       // setClipBounds is only available in API 18 +
+                       if ((int)Build.VERSION.SdkInt >= 18)
+                       {
+                               if (!(_renderer.View is ViewGroup viewGroup))
+                               {
+                                       return;
+                               }
 
-                       parent.SetClipChildren(shouldClip);
-                       parent.Invalidate();
+                               // Forms layouts should not impose clipping on their children
+                               viewGroup.SetClipChildren(false);
+
+                               // But if IsClippedToBounds is true, they _should_ enforce clipping at their own edges
+                               viewGroup.ClipBounds = shouldClip ? new Rect(0, 0, viewGroup.Width, viewGroup.Height) : null;
+                       }
+                       else
+                       {
+                               // For everything in 17 and below, use the setClipChildren method
+                               if (!(_renderer.View.Parent is ViewGroup parent))
+                                       return;
+
+                               if ((int)Build.VERSION.SdkInt >= 18 && parent.ClipChildren == shouldClip)
+                                       return;
+
+                               parent.SetClipChildren(shouldClip);
+                               parent.Invalidate();
+                       }
                }
 
                void UpdateIsVisible()