From 2ce285c465676c1a224d51e742ae1f6228778e00 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Sat, 29 Apr 2023 20:46:25 +0800 Subject: [PATCH 01/72] Fix InputTransparent and CascadeInputTransparent --- .../Core/HandlerImpl/Layout/Layout.Android.cs | 27 +-------- .../Core/HandlerImpl/Layout/Layout.Windows.cs | 22 +------- src/Controls/src/Core/Layout/Layout.cs | 26 ++++----- src/Controls/src/Core/VisualElement.cs | 56 ++++++++++++++++++- 4 files changed, 70 insertions(+), 61 deletions(-) diff --git a/src/Controls/src/Core/HandlerImpl/Layout/Layout.Android.cs b/src/Controls/src/Core/HandlerImpl/Layout/Layout.Android.cs index 9b0727a51a6c..9051d3efff17 100644 --- a/src/Controls/src/Core/HandlerImpl/Layout/Layout.Android.cs +++ b/src/Controls/src/Core/HandlerImpl/Layout/Layout.Android.cs @@ -7,31 +7,8 @@ namespace Microsoft.Maui.Controls { public partial class Layout { - public static void MapInputTransparent(LayoutHandler handler, Layout layout) => - UpdateInputTransparent(handler, layout); + public static void MapInputTransparent(LayoutHandler handler, Layout layout) { } - public static void MapInputTransparent(ILayoutHandler handler, Layout layout) => - UpdateInputTransparent(handler, layout); - - static void MapInputTransparent(IViewHandler handler, IView layout) => - UpdateInputTransparent(handler, layout); - - static void UpdateInputTransparent(IViewHandler handler, IView layout) - { - if (handler is ILayoutHandler layoutHandler && layout is Layout controlsLayout) - { - if (layoutHandler.PlatformView is LayoutViewGroup layoutViewGroup) - { - // Handle input transparent for this view - layoutViewGroup.InputTransparent = layout.InputTransparent; - } - - controlsLayout.UpdateDescendantInputTransparent(); - } - else - { - ControlsVisualElementMapper.UpdateProperty(handler, layout, nameof(IView.InputTransparent)); - } - } + public static void MapInputTransparent(ILayoutHandler handler, Layout layout) { } } } diff --git a/src/Controls/src/Core/HandlerImpl/Layout/Layout.Windows.cs b/src/Controls/src/Core/HandlerImpl/Layout/Layout.Windows.cs index b118ff2ef221..1c2001468d15 100644 --- a/src/Controls/src/Core/HandlerImpl/Layout/Layout.Windows.cs +++ b/src/Controls/src/Core/HandlerImpl/Layout/Layout.Windows.cs @@ -5,26 +5,8 @@ namespace Microsoft.Maui.Controls { public partial class Layout { - public static void MapInputTransparent(LayoutHandler handler, Layout layout) => - UpdateInputTransparent(handler, layout); + public static void MapInputTransparent(LayoutHandler handler, Layout layout) { } - public static void MapInputTransparent(ILayoutHandler handler, Layout layout) => - UpdateInputTransparent(handler, layout); - - static void MapInputTransparent(IViewHandler handler, IView layout) => - UpdateInputTransparent(handler, layout); - - static void UpdateInputTransparent(IViewHandler handler, IView layout) - { - if (handler is ILayoutHandler layoutHandler && layout is Layout controlsLayout) - { - layoutHandler.PlatformView?.UpdateInputTransparent(layoutHandler, controlsLayout); - controlsLayout.UpdateDescendantInputTransparent(); - } - else - { - ControlsVisualElementMapper.UpdateProperty(handler, layout, nameof(IView.InputTransparent)); - } - } + public static void MapInputTransparent(ILayoutHandler handler, Layout layout) { } } } \ No newline at end of file diff --git a/src/Controls/src/Core/Layout/Layout.cs b/src/Controls/src/Core/Layout/Layout.cs index b010a77a565d..8f9ce71e64da 100644 --- a/src/Controls/src/Core/Layout/Layout.cs +++ b/src/Controls/src/Core/Layout/Layout.cs @@ -293,7 +293,8 @@ public Graphics.Size CrossPlatformArrange(Graphics.Rect bounds) /// Bindable property for . public static readonly BindableProperty CascadeInputTransparentProperty = - BindableProperty.Create(nameof(CascadeInputTransparent), typeof(bool), typeof(Layout), true); + BindableProperty.Create(nameof(CascadeInputTransparent), typeof(bool), typeof(Layout), true, + propertyChanged: OnCascadeInputTransparentPropertyChanged); public bool CascadeInputTransparent { @@ -303,25 +304,20 @@ public bool CascadeInputTransparent public static IPropertyMapper ControlsLayoutMapper = new PropertyMapper(ControlsVisualElementMapper) { - [nameof(CascadeInputTransparent)] = MapInputTransparent, - [nameof(IView.InputTransparent)] = MapInputTransparent, }; - void UpdateDescendantInputTransparent() + + static void OnCascadeInputTransparentPropertyChanged(BindableObject bindable, object oldValue, object newValue) => + (bindable as Layout)?.RefreshInputTransparentProperty(); + + private protected override bool InputTransparentCore { - if (!InputTransparent || !CascadeInputTransparent) + get { - // We only need to propagate values if the layout is InputTransparent AND Cascade is true - return; - } + if (Parent is Layout parent && parent.CascadeInputTransparent && parent.InputTransparent) + return true; - // Set all the child InputTransparent values to match this one - for (int n = 0; n < Count; n++) - { - if (this[n] is VisualElement visualElement) - { - visualElement.InputTransparent = true; - } + return base.InputTransparentCore; } } } diff --git a/src/Controls/src/Core/VisualElement.cs b/src/Controls/src/Core/VisualElement.cs index 191d08f10cec..d0ddb0dd199d 100644 --- a/src/Controls/src/Core/VisualElement.cs +++ b/src/Controls/src/Core/VisualElement.cs @@ -20,8 +20,12 @@ public partial class VisualElement : NavigableElement, IAnimatable, IVisualEleme /// public new static readonly BindableProperty StyleProperty = NavigableElement.StyleProperty; + bool _inputTransparentExplicit = (bool)InputTransparentProperty.DefaultValue; + /// Bindable property for . - public static readonly BindableProperty InputTransparentProperty = BindableProperty.Create("InputTransparent", typeof(bool), typeof(VisualElement), default(bool)); + public static readonly BindableProperty InputTransparentProperty = BindableProperty.Create( + "InputTransparent", typeof(bool), typeof(VisualElement), default(bool), + propertyChanged: OnInputTransparentPropertyChanged, coerceValue: CoerceInputTransparentProperty); bool _isEnabledExplicit = (bool)IsEnabledProperty.DefaultValue; @@ -585,6 +589,30 @@ protected virtual bool IsEnabledCore } } + /// + /// This value represents the cumulative InputTransparent value. + /// All types that override this property need to also invoke + /// the RefreshInputTransparentProperty() method if the value will change. + /// + private protected virtual bool InputTransparentCore + { + get + { + if (_inputTransparentExplicit == true) + { + // If the explicitly set value is true, then nothing else matters + // And we can save the effort of a Parent check + return true; + } + + var parent = Parent as VisualElement; + if (parent is not null && parent.InputTransparent) + return true; + + return _inputTransparentExplicit; + } + } + /// public bool IsFocused => (bool)GetValue(IsFocusedProperty); @@ -1275,6 +1303,22 @@ static void OnIsEnabledPropertyChanged(BindableObject bindable, object oldValue, (bindable as IPropertyPropagationController)?.PropagatePropertyChanged(VisualElement.IsEnabledProperty.PropertyName); } + static object CoerceInputTransparentProperty(BindableObject bindable, object value) + { + if (bindable is VisualElement visualElement) + { + visualElement._inputTransparentExplicit = (bool)value; + return visualElement.InputTransparentCore; + } + + return false; + } + + static void OnInputTransparentPropertyChanged(BindableObject bindable, object oldValue, object newValue) + { + (bindable as IPropertyPropagationController)?.PropagatePropertyChanged(VisualElement.InputTransparentProperty.PropertyName); + } + static void OnIsFocusedPropertyChanged(BindableObject bindable, object oldvalue, object newvalue) { var element = (VisualElement)bindable; @@ -1334,6 +1378,9 @@ void IPropertyPropagationController.PropagatePropertyChanged(string propertyName if (propertyName == null || propertyName == IsEnabledProperty.PropertyName) this.RefreshPropertyValue(IsEnabledProperty, _isEnabledExplicit); + if (propertyName == null || propertyName == InputTransparentProperty.PropertyName) + this.RefreshPropertyValue(InputTransparentProperty, _inputTransparentExplicit); + PropertyPropagationExtensions.PropagatePropertyChanged(propertyName, this, ((IVisualTreeElement)this).GetVisualChildren()); } @@ -1344,6 +1391,13 @@ void IPropertyPropagationController.PropagatePropertyChanged(string propertyName protected void RefreshIsEnabledProperty() => this.RefreshPropertyValue(IsEnabledProperty, _isEnabledExplicit); + /// + /// This method must always be called if some event occurs and the value of + /// the InputTransparentCore property will change. + /// + private protected void RefreshInputTransparentProperty() => + this.RefreshPropertyValue(InputTransparentProperty, _inputTransparentExplicit); + void UpdateBoundsComponents(Rect bounds) { _frame = bounds; From 1895907e0fb3f20fc9edbb87a48e30a0681a53d3 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Sat, 29 Apr 2023 21:31:29 +0800 Subject: [PATCH 02/72] Adding some tests for the baseline --- .../VisualElementInputTransparentTests.cs | 560 ++++++++++++++++++ 1 file changed, 560 insertions(+) create mode 100644 src/Controls/tests/Core.UnitTests/VisualElementInputTransparentTests.cs diff --git a/src/Controls/tests/Core.UnitTests/VisualElementInputTransparentTests.cs b/src/Controls/tests/Core.UnitTests/VisualElementInputTransparentTests.cs new file mode 100644 index 000000000000..4ef588000840 --- /dev/null +++ b/src/Controls/tests/Core.UnitTests/VisualElementInputTransparentTests.cs @@ -0,0 +1,560 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Maui.Controls.Shapes; +using Microsoft.Maui.Graphics; +using Microsoft.Maui.Primitives; +using Xunit; +using Xunit.Sdk; + +namespace Microsoft.Maui.Controls.Core.UnitTests +{ + public class VisualElementInputTransparentTests + { + // this is both for color diff and cols + const bool truee = true; + + static IReadOnlyDictionary<(bool, bool, bool, bool, bool), (bool, bool)> States = new Dictionary<(bool, bool, bool, bool, bool), (bool, bool)> + { + [(truee, truee, truee, truee, truee)] = (truee, truee), + [(truee, truee, truee, truee, false)] = (truee, truee), + [(truee, truee, truee, false, truee)] = (truee, truee), + [(truee, truee, truee, false, false)] = (truee, truee), + [(truee, truee, false, truee, truee)] = (truee, truee), + [(truee, truee, false, truee, false)] = (truee, truee), + [(truee, truee, false, false, truee)] = (truee, truee), + [(truee, truee, false, false, false)] = (truee, truee), + [(truee, false, truee, truee, truee)] = (truee, truee), + [(truee, false, truee, truee, false)] = (truee, truee), + [(truee, false, truee, false, truee)] = (truee, truee), + [(truee, false, truee, false, false)] = (truee, false), + [(truee, false, false, truee, truee)] = (false, truee), + [(truee, false, false, truee, false)] = (false, false), + [(truee, false, false, false, truee)] = (false, truee), + [(truee, false, false, false, false)] = (false, false), + [(false, truee, truee, truee, truee)] = (truee, truee), + [(false, truee, truee, truee, false)] = (truee, truee), + [(false, truee, truee, false, truee)] = (truee, truee), + [(false, truee, truee, false, false)] = (truee, false), + [(false, truee, false, truee, truee)] = (false, truee), + [(false, truee, false, truee, false)] = (false, false), + [(false, truee, false, false, truee)] = (false, truee), + [(false, truee, false, false, false)] = (false, false), + [(false, false, truee, truee, truee)] = (truee, truee), + [(false, false, truee, truee, false)] = (truee, truee), + [(false, false, truee, false, truee)] = (truee, truee), + [(false, false, truee, false, false)] = (truee, false), + [(false, false, false, truee, truee)] = (false, truee), + [(false, false, false, truee, false)] = (false, false), + [(false, false, false, false, truee)] = (false, truee), + [(false, false, false, false, false)] = (false, false), + }; + + public static IEnumerable TransparencyStates() + { + foreach (var pair in States) + { + var (rT, rC, nT, nC, t) = pair.Key; + + yield return new object[] { rT, rC, nT, nC, t }; + } + } + + static (Layout Root, Layout Nested, VisualElement Child) CreateViews(bool rootTrans, bool rootCascade, bool nestedTrans, bool nestedCascade, bool trans) + { + Layout root; + Layout nested; + VisualElement child; + + root = new Grid + { + InputTransparent = rootTrans, + CascadeInputTransparent = rootCascade, + Children = + { + (nested = new Grid + { + InputTransparent = nestedTrans, + CascadeInputTransparent = nestedCascade, + Children = + { + (child = new Button + { + InputTransparent = trans + }) + } + }) + } + }; + + return (root, nested, child); + } + + static void AssertState(bool rootTrans, bool rootCascade, bool nestedTrans, bool nestedCascade, bool trans, Layout nested, VisualElement child) + { + var (finalNestedTrans, finalTrans) = States[(rootTrans, rootCascade, nestedTrans, nestedCascade, trans)]; + + if (finalNestedTrans) + Assert.True(nested.InputTransparent, "Nested layout did not match"); + else + Assert.False(nested.InputTransparent, "Nested layout did not match"); + + if (finalTrans) + Assert.True(child.InputTransparent, "Child element did not match"); + else + Assert.False(child.InputTransparent, "Child element did not match"); + } + + [Theory] + [MemberData(nameof(TransparencyStates))] + public void TestMethodsAreValid(bool rootTrans, bool rootCascade, bool nestedTrans, bool nestedCascade, bool trans) + { + var (rootLayout, nestedLayout, element) = CreateViews(rootTrans, rootCascade, nestedTrans, nestedCascade, trans); + + AssertState(rootTrans, rootCascade, nestedTrans, nestedCascade, trans, nestedLayout, element); + } + + [Theory] + [MemberData(nameof(TransparencyStates))] + public void InitialStateIsCorrectWhenOrderIsPropsRootChildNestedChild(bool rootTrans, bool rootCascade, bool nestedTrans, bool nestedCascade, bool trans) + { + var rootLayout = new Grid + { + InputTransparent = rootTrans, + CascadeInputTransparent = rootCascade + }; + var nestedLayout = new Grid + { + InputTransparent = nestedTrans, + CascadeInputTransparent = nestedCascade + }; + var element = new Button + { + InputTransparent = trans + }; + + rootLayout.Add(nestedLayout); + nestedLayout.Add(element); + + AssertState(rootTrans, rootCascade, nestedTrans, nestedCascade, trans, nestedLayout, element); + } + + [Theory] + [MemberData(nameof(TransparencyStates))] + public void InitialStateIsCorrectWhenOrderIsPropsNestedChildRootChild(bool rootTrans, bool rootCascade, bool nestedTrans, bool nestedCascade, bool trans) + { + var rootLayout = new Grid + { + InputTransparent = rootTrans, + CascadeInputTransparent = rootCascade + }; + var nestedLayout = new Grid + { + InputTransparent = nestedTrans, + CascadeInputTransparent = nestedCascade + }; + var element = new Button + { + InputTransparent = trans + }; + + nestedLayout.Add(element); + rootLayout.Add(nestedLayout); + + AssertState(rootTrans, rootCascade, nestedTrans, nestedCascade, trans, nestedLayout, element); + } + + [Theory] + [MemberData(nameof(TransparencyStates))] + public void StateIsCorrectWhenSettingChildNestedRoot(bool rootTrans, bool rootCascade, bool nestedTrans, bool nestedCascade, bool trans) + { + var element = new Button(); + var nestedLayout = new Grid { element }; + var rootLayout = new Grid { nestedLayout }; + + element.InputTransparent = trans; + + nestedLayout.InputTransparent = nestedTrans; + nestedLayout.CascadeInputTransparent = nestedCascade; + + rootLayout.InputTransparent = rootTrans; + rootLayout.CascadeInputTransparent = rootCascade; + + AssertState(rootTrans, rootCascade, nestedTrans, nestedCascade, trans, nestedLayout, element); + } + + [Theory] + [MemberData(nameof(TransparencyStates))] + public void StateIsCorrectWhenSettingRootNestedChild(bool rootTrans, bool rootCascade, bool nestedTrans, bool nestedCascade, bool trans) + { + var element = new Button(); + var nestedLayout = new Grid { element }; + var rootLayout = new Grid { nestedLayout }; + + rootLayout.InputTransparent = rootTrans; + rootLayout.CascadeInputTransparent = rootCascade; + + nestedLayout.InputTransparent = nestedTrans; + nestedLayout.CascadeInputTransparent = nestedCascade; + + element.InputTransparent = trans; + + AssertState(rootTrans, rootCascade, nestedTrans, nestedCascade, trans, nestedLayout, element); + } + + [Theory] + [MemberData(nameof(TransparencyStates))] + public void InvertingRootLayoutInputTransparentIsCorrect(bool rootTrans, bool rootCascade, bool nestedTrans, bool nestedCascade, bool trans) + { + var (rootLayout, nestedLayout, element) = CreateViews(rootTrans, rootCascade, nestedTrans, nestedCascade, trans); + + rootLayout.InputTransparent = !rootTrans; + + AssertState(!rootTrans, rootCascade, nestedTrans, nestedCascade, trans, nestedLayout, element); + } + + [Theory] + [MemberData(nameof(TransparencyStates))] + public void InvertingRootLayoutCascadeInputTransparentIsCorrect(bool rootTrans, bool rootCascade, bool nestedTrans, bool nestedCascade, bool trans) + { + var (rootLayout, nestedLayout, element) = CreateViews(rootTrans, rootCascade, nestedTrans, nestedCascade, trans); + + rootLayout.CascadeInputTransparent = !rootCascade; + + AssertState(rootTrans, !rootCascade, nestedTrans, nestedCascade, trans, nestedLayout, element); + } + + [Theory] + [MemberData(nameof(TransparencyStates))] + public void InvertingNestedLayoutInputTransparentIsCorrect(bool rootTrans, bool rootCascade, bool nestedTrans, bool nestedCascade, bool trans) + { + var (rootLayout, nestedLayout, element) = CreateViews(rootTrans, rootCascade, nestedTrans, nestedCascade, trans); + + nestedLayout.InputTransparent = !nestedTrans; + + AssertState(rootTrans, rootCascade, !nestedTrans, nestedCascade, trans, nestedLayout, element); + } + + [Theory] + [MemberData(nameof(TransparencyStates))] + public void InvertingNestedLayoutCascadeInputTransparentIsCorrect(bool rootTrans, bool rootCascade, bool nestedTrans, bool nestedCascade, bool trans) + { + var (rootLayout, nestedLayout, element) = CreateViews(rootTrans, rootCascade, nestedTrans, nestedCascade, trans); + + nestedLayout.CascadeInputTransparent = !nestedCascade; + + AssertState(rootTrans, rootCascade, nestedTrans, !nestedCascade, trans, nestedLayout, element); + } + + [Theory] + [MemberData(nameof(TransparencyStates))] + public void InvertingChildInputTransparentIsCorrect(bool rootTrans, bool rootCascade, bool nestedTrans, bool nestedCascade, bool trans) + { + var (rootLayout, nestedLayout, element) = CreateViews(rootTrans, rootCascade, nestedTrans, nestedCascade, trans); + + element.InputTransparent = !trans; + + AssertState(rootTrans, rootCascade, nestedTrans, nestedCascade, !trans, nestedLayout, element); + } + + [Theory] + [InlineData(typeof(Button))] + [InlineData(typeof(VerticalStackLayout))] + [InlineData(typeof(Editor))] + [InlineData(typeof(Entry))] + public void VisualElementsCanToggleInputTransparency(Type type) + { + var element = (VisualElement)Activator.CreateInstance(type); + + Assert.False(element.InputTransparent); + + element.InputTransparent = true; + + Assert.True(element.InputTransparent); + + element.InputTransparent = false; + + Assert.False(element.InputTransparent); + } + + // TODO: the tests below may be duplicates of the tests above + + [Theory] + [InlineData(typeof(Button), true)] + [InlineData(typeof(VerticalStackLayout), true)] + [InlineData(typeof(Editor), true)] + [InlineData(typeof(Entry), true)] + [InlineData(typeof(Button), false)] + [InlineData(typeof(VerticalStackLayout), false)] + [InlineData(typeof(Editor), false)] + [InlineData(typeof(Entry), false)] + public void InputTransparencyOnLayoutDoesNotAffectChildWhenNotCascadeInputTransparent(Type type, bool initialInputTransparent) + { + var element = (View)Activator.CreateInstance(type); + var layout = new Grid + { + InputTransparent = initialInputTransparent, + CascadeInputTransparent = false, + Children = + { + element + } + }; + + Assert.Equal(initialInputTransparent, layout.InputTransparent); + Assert.False(element.InputTransparent); + + layout.InputTransparent = !initialInputTransparent; + + Assert.NotEqual(initialInputTransparent, layout.InputTransparent); + Assert.False(element.InputTransparent); + + layout.InputTransparent = initialInputTransparent; + + Assert.Equal(initialInputTransparent, layout.InputTransparent); + Assert.False(element.InputTransparent); + } + + [Theory] + [InlineData(typeof(Button), true)] + [InlineData(typeof(VerticalStackLayout), true)] + [InlineData(typeof(Editor), true)] + [InlineData(typeof(Entry), true)] + [InlineData(typeof(Button), false)] + [InlineData(typeof(VerticalStackLayout), false)] + [InlineData(typeof(Editor), false)] + [InlineData(typeof(Entry), false)] + public void InputTransparencyOnLayoutAffectsChild(Type type, bool initialInputTransparent) + { + var element = (View)Activator.CreateInstance(type); + var layout = new Grid + { + InputTransparent = initialInputTransparent, + CascadeInputTransparent = true, // default + Children = + { + element + } + }; + + Assert.Equal(initialInputTransparent, layout.InputTransparent); + Assert.Equal(initialInputTransparent, element.InputTransparent); + + layout.InputTransparent = !initialInputTransparent; + + Assert.NotEqual(initialInputTransparent, layout.InputTransparent); + Assert.NotEqual(initialInputTransparent, element.InputTransparent); + + layout.InputTransparent = initialInputTransparent; + + Assert.Equal(initialInputTransparent, layout.InputTransparent); + Assert.Equal(initialInputTransparent, element.InputTransparent); + } + + [Theory] + [InlineData(typeof(Button), true)] + [InlineData(typeof(VerticalStackLayout), true)] + [InlineData(typeof(Editor), true)] + [InlineData(typeof(Entry), true)] + [InlineData(typeof(Button), false)] + [InlineData(typeof(VerticalStackLayout), false)] + [InlineData(typeof(Editor), false)] + [InlineData(typeof(Entry), false)] + public void InputTransparencyOnLayoutDoesNotAffectDeeplyNestedChildWhenNotCascadeInputTransparent(Type type, bool initialInputTransparent) + { + var element = (View)Activator.CreateInstance(type); + var layout = new Grid + { + InputTransparent = initialInputTransparent, + CascadeInputTransparent = false, + Children = + { + new Grid { new Grid { new Grid { element } } } + } + }; + + Assert.Equal(initialInputTransparent, layout.InputTransparent); + Assert.False(element.InputTransparent); + + layout.InputTransparent = !initialInputTransparent; + + Assert.NotEqual(initialInputTransparent, layout.InputTransparent); + Assert.False(element.InputTransparent); + + layout.InputTransparent = initialInputTransparent; + + Assert.Equal(initialInputTransparent, layout.InputTransparent); + Assert.False(element.InputTransparent); + } + + [Theory] + [InlineData(typeof(Button), true)] + [InlineData(typeof(VerticalStackLayout), true)] + [InlineData(typeof(Editor), true)] + [InlineData(typeof(Entry), true)] + [InlineData(typeof(Button), false)] + [InlineData(typeof(VerticalStackLayout), false)] + [InlineData(typeof(Editor), false)] + [InlineData(typeof(Entry), false)] + public void InputTransparencyOnLayoutAffectsDeeplyNestedChild(Type type, bool initialInputTransparent) + { + var element = (View)Activator.CreateInstance(type); + var layout = new Grid + { + InputTransparent = initialInputTransparent, + CascadeInputTransparent = true, // default + Children = + { + new Grid { new Grid { new Grid { element } } } + } + }; + + Assert.Equal(initialInputTransparent, layout.InputTransparent); + Assert.Equal(initialInputTransparent, element.InputTransparent); + + layout.InputTransparent = !initialInputTransparent; + + Assert.NotEqual(initialInputTransparent, layout.InputTransparent); + Assert.NotEqual(initialInputTransparent, element.InputTransparent); + + layout.InputTransparent = initialInputTransparent; + + Assert.Equal(initialInputTransparent, layout.InputTransparent); + Assert.Equal(initialInputTransparent, element.InputTransparent); + } + + [Theory] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public void InputTransparencyOnLayoutDoesNotOverrideNestedLayoutWhenNotCascadeInputTransparentButCascadeOnNested(bool parent, bool nested) + { + var element = new Button(); + var layout = new Grid + { + InputTransparent = parent, + CascadeInputTransparent = false, + Children = + { + new Grid + { + new Grid + { + InputTransparent = nested, + CascadeInputTransparent = true, // default + Children = + { + new Grid { element } + } + } + } + } + }; + + Assert.Equal(parent, layout.InputTransparent); + Assert.Equal(nested, element.InputTransparent); + + layout.InputTransparent = !parent; + + Assert.NotEqual(parent, layout.InputTransparent); + Assert.Equal(nested, element.InputTransparent); + + layout.InputTransparent = parent; + + Assert.Equal(parent, layout.InputTransparent); + Assert.Equal(nested, element.InputTransparent); + } + + [Theory] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public void InputTransparencyOnLayoutDoesNotOverrideNestedLayoutWhenNotCascadeInputTransparent(bool parent, bool nested) + { + var element = new Button(); + var layout = new Grid + { + InputTransparent = parent, + CascadeInputTransparent = false, + Children = + { + new Grid + { + new Grid + { + InputTransparent = nested, + CascadeInputTransparent = false, + Children = + { + new Grid { element } + } + } + } + } + }; + + Assert.Equal(parent, layout.InputTransparent); + Assert.False(element.InputTransparent); + + layout.InputTransparent = !parent; + + Assert.NotEqual(parent, layout.InputTransparent); + Assert.False(element.InputTransparent); + + layout.InputTransparent = parent; + + Assert.Equal(parent, layout.InputTransparent); + Assert.False(element.InputTransparent); + } + + [Theory] + [InlineData(true, true, true)] + [InlineData(true, true, false)] + [InlineData(true, false, true)] + [InlineData(true, false, false)] + [InlineData(false, true, true)] + [InlineData(false, true, false)] + [InlineData(false, false, true)] + [InlineData(false, false, false)] + public void InputTransparencyOnLayoutOverridesNestedLayout(bool parent, bool nested, bool cascadeNested) + { + var element = new Button(); + var layout = new Grid + { + InputTransparent = parent, + CascadeInputTransparent = true, // default + Children = + { + new Grid + { + new Grid + { + InputTransparent = nested, + CascadeInputTransparent = cascadeNested, + Children = + { + new Grid { element } + } + } + } + } + }; + + Assert.Equal(parent, layout.InputTransparent); + Assert.Equal(parent, element.InputTransparent); + + layout.InputTransparent = !parent; + + Assert.NotEqual(parent, layout.InputTransparent); + Assert.NotEqual(parent, element.InputTransparent); + + layout.InputTransparent = parent; + + Assert.Equal(parent, layout.InputTransparent); + Assert.Equal(parent, element.InputTransparent); + } + } +} From 802dc6e4d9117ee9076671da31c066b8d38504cd Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Sun, 30 Apr 2023 13:40:23 +0800 Subject: [PATCH 03/72] This works better --- src/Controls/src/Core/Layout/Layout.cs | 11 ----------- src/Controls/src/Core/VisualElement.cs | 10 +++++++--- .../VisualElementInputTransparentTests.cs | 8 ++++---- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/Controls/src/Core/Layout/Layout.cs b/src/Controls/src/Core/Layout/Layout.cs index 8f9ce71e64da..7da028b6ee80 100644 --- a/src/Controls/src/Core/Layout/Layout.cs +++ b/src/Controls/src/Core/Layout/Layout.cs @@ -309,16 +309,5 @@ public bool CascadeInputTransparent static void OnCascadeInputTransparentPropertyChanged(BindableObject bindable, object oldValue, object newValue) => (bindable as Layout)?.RefreshInputTransparentProperty(); - - private protected override bool InputTransparentCore - { - get - { - if (Parent is Layout parent && parent.CascadeInputTransparent && parent.InputTransparent) - return true; - - return base.InputTransparentCore; - } - } } } diff --git a/src/Controls/src/Core/VisualElement.cs b/src/Controls/src/Core/VisualElement.cs index d0ddb0dd199d..4d51a4997c80 100644 --- a/src/Controls/src/Core/VisualElement.cs +++ b/src/Controls/src/Core/VisualElement.cs @@ -605,9 +605,13 @@ private protected virtual bool InputTransparentCore return true; } - var parent = Parent as VisualElement; - if (parent is not null && parent.InputTransparent) - return true; + var parent = Parent as Layout; + while (parent is not null) + { + if (parent.CascadeInputTransparent && parent.InputTransparent) + return true; + parent = parent.Parent as Layout; + } return _inputTransparentExplicit; } diff --git a/src/Controls/tests/Core.UnitTests/VisualElementInputTransparentTests.cs b/src/Controls/tests/Core.UnitTests/VisualElementInputTransparentTests.cs index 4ef588000840..48d16cb6068e 100644 --- a/src/Controls/tests/Core.UnitTests/VisualElementInputTransparentTests.cs +++ b/src/Controls/tests/Core.UnitTests/VisualElementInputTransparentTests.cs @@ -96,14 +96,14 @@ static void AssertState(bool rootTrans, bool rootCascade, bool nestedTrans, bool var (finalNestedTrans, finalTrans) = States[(rootTrans, rootCascade, nestedTrans, nestedCascade, trans)]; if (finalNestedTrans) - Assert.True(nested.InputTransparent, "Nested layout did not match"); + Assert.True(nested.InputTransparent, "Nested layout was not input transparent when it should have been."); else - Assert.False(nested.InputTransparent, "Nested layout did not match"); + Assert.False(nested.InputTransparent, "Nested layout was input transparent when it should not have been."); if (finalTrans) - Assert.True(child.InputTransparent, "Child element did not match"); + Assert.True(child.InputTransparent, "Child element was not input transparent when it should have been."); else - Assert.False(child.InputTransparent, "Child element did not match"); + Assert.False(child.InputTransparent, "Child element was input transparent when it should not have been."); } [Theory] From 063147c7af24cc22f83760eccfeefa5f6189b033 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Mon, 1 May 2023 10:21:43 +0800 Subject: [PATCH 04/72] Fix this --- src/Controls/src/Core/Layout/Layout.cs | 11 ++- src/Controls/src/Core/VisualElement.cs | 19 +++- .../VisualElementInputTransparentTests.cs | 93 +++++++++---------- 3 files changed, 70 insertions(+), 53 deletions(-) diff --git a/src/Controls/src/Core/Layout/Layout.cs b/src/Controls/src/Core/Layout/Layout.cs index 7da028b6ee80..fc8edd1ccfd9 100644 --- a/src/Controls/src/Core/Layout/Layout.cs +++ b/src/Controls/src/Core/Layout/Layout.cs @@ -307,7 +307,14 @@ public bool CascadeInputTransparent }; - static void OnCascadeInputTransparentPropertyChanged(BindableObject bindable, object oldValue, object newValue) => - (bindable as Layout)?.RefreshInputTransparentProperty(); + static void OnCascadeInputTransparentPropertyChanged(BindableObject bindable, object oldValue, object newValue) + { + // We only need to update if the cascade changes anything, namely when InputTransparent=true. + // When InputTransparent=false, then the cascade property has no effect. + if (bindable is Layout layout && layout.InputTransparent) + { + layout.RefreshInputTransparentProperty(); + } + } } } diff --git a/src/Controls/src/Core/VisualElement.cs b/src/Controls/src/Core/VisualElement.cs index 4d51a4997c80..39975d680d61 100644 --- a/src/Controls/src/Core/VisualElement.cs +++ b/src/Controls/src/Core/VisualElement.cs @@ -593,8 +593,14 @@ protected virtual bool IsEnabledCore /// This value represents the cumulative InputTransparent value. /// All types that override this property need to also invoke /// the RefreshInputTransparentProperty() method if the value will change. + /// + /// This method is not virtual as none of the derived types actually need + /// to change the calculation. If this ever needs to change, then the + /// RefreshInputTransparentProperty() method should also call the + /// RefreshPropertyValue() method - just like how the + /// RefreshIsEnabledProperty() method does. /// - private protected virtual bool InputTransparentCore + private protected bool InputTransparentCore { get { @@ -1399,8 +1405,15 @@ protected void RefreshIsEnabledProperty() => /// This method must always be called if some event occurs and the value of /// the InputTransparentCore property will change. /// - private protected void RefreshInputTransparentProperty() => - this.RefreshPropertyValue(InputTransparentProperty, _inputTransparentExplicit); + private protected void RefreshInputTransparentProperty() + { + // This method does not need to call the + // this.RefreshPropertyValue(InputTransparentProperty, _inputTransparentExplicit); + // method because none of the derived types will affect this view. All we + // need to do is propagate the new value to all the children. + + (this as IPropertyPropagationController)?.PropagatePropertyChanged(VisualElement.InputTransparentProperty.PropertyName); + } void UpdateBoundsComponents(Rect bounds) { diff --git a/src/Controls/tests/Core.UnitTests/VisualElementInputTransparentTests.cs b/src/Controls/tests/Core.UnitTests/VisualElementInputTransparentTests.cs index 48d16cb6068e..b4f840fdd071 100644 --- a/src/Controls/tests/Core.UnitTests/VisualElementInputTransparentTests.cs +++ b/src/Controls/tests/Core.UnitTests/VisualElementInputTransparentTests.cs @@ -106,6 +106,21 @@ static void AssertState(bool rootTrans, bool rootCascade, bool nestedTrans, bool Assert.False(child.InputTransparent, "Child element was input transparent when it should not have been."); } + static void AssertState(bool layoutTrans, bool layoutCascade, bool trans, Layout layout, VisualElement child) + { + var (finalLayoutTrans, finalTrans) = States[(false, false, layoutTrans, layoutCascade, trans)]; + + if (finalLayoutTrans) + Assert.True(layout.InputTransparent, "Layout was not input transparent when it should have been."); + else + Assert.False(layout.InputTransparent, "Layout was input transparent when it should not have been."); + + if (finalTrans) + Assert.True(child.InputTransparent, "Child element was not input transparent when it should have been."); + else + Assert.False(child.InputTransparent, "Child element was input transparent when it should not have been."); + } + [Theory] [MemberData(nameof(TransparencyStates))] public void TestMethodsAreValid(bool rootTrans, bool rootCascade, bool nestedTrans, bool nestedCascade, bool trans) @@ -302,18 +317,15 @@ public void InputTransparencyOnLayoutDoesNotAffectChildWhenNotCascadeInputTransp } }; - Assert.Equal(initialInputTransparent, layout.InputTransparent); - Assert.False(element.InputTransparent); + AssertState(initialInputTransparent, false, false, layout, element); layout.InputTransparent = !initialInputTransparent; - Assert.NotEqual(initialInputTransparent, layout.InputTransparent); - Assert.False(element.InputTransparent); + AssertState(!initialInputTransparent, false, false, layout, element); layout.InputTransparent = initialInputTransparent; - Assert.Equal(initialInputTransparent, layout.InputTransparent); - Assert.False(element.InputTransparent); + AssertState(initialInputTransparent, false, false, layout, element); } [Theory] @@ -338,18 +350,15 @@ public void InputTransparencyOnLayoutAffectsChild(Type type, bool initialInputTr } }; - Assert.Equal(initialInputTransparent, layout.InputTransparent); - Assert.Equal(initialInputTransparent, element.InputTransparent); + AssertState(initialInputTransparent, true, false, layout, element); layout.InputTransparent = !initialInputTransparent; - Assert.NotEqual(initialInputTransparent, layout.InputTransparent); - Assert.NotEqual(initialInputTransparent, element.InputTransparent); + AssertState(!initialInputTransparent, true, false, layout, element); layout.InputTransparent = initialInputTransparent; - Assert.Equal(initialInputTransparent, layout.InputTransparent); - Assert.Equal(initialInputTransparent, element.InputTransparent); + AssertState(initialInputTransparent, true, false, layout, element); } [Theory] @@ -374,18 +383,15 @@ public void InputTransparencyOnLayoutDoesNotAffectDeeplyNestedChildWhenNotCascad } }; - Assert.Equal(initialInputTransparent, layout.InputTransparent); - Assert.False(element.InputTransparent); + AssertState(initialInputTransparent, false, false, layout, element); layout.InputTransparent = !initialInputTransparent; - Assert.NotEqual(initialInputTransparent, layout.InputTransparent); - Assert.False(element.InputTransparent); + AssertState(!initialInputTransparent, false, false, layout, element); layout.InputTransparent = initialInputTransparent; - Assert.Equal(initialInputTransparent, layout.InputTransparent); - Assert.False(element.InputTransparent); + AssertState(initialInputTransparent, false, false, layout, element); } [Theory] @@ -410,18 +416,15 @@ public void InputTransparencyOnLayoutAffectsDeeplyNestedChild(Type type, bool in } }; - Assert.Equal(initialInputTransparent, layout.InputTransparent); - Assert.Equal(initialInputTransparent, element.InputTransparent); + AssertState(initialInputTransparent, true, false, layout, element); layout.InputTransparent = !initialInputTransparent; - Assert.NotEqual(initialInputTransparent, layout.InputTransparent); - Assert.NotEqual(initialInputTransparent, element.InputTransparent); + AssertState(!initialInputTransparent, true, false, layout, element); layout.InputTransparent = initialInputTransparent; - Assert.Equal(initialInputTransparent, layout.InputTransparent); - Assert.Equal(initialInputTransparent, element.InputTransparent); + AssertState(initialInputTransparent, true, false, layout, element); } [Theory] @@ -432,6 +435,7 @@ public void InputTransparencyOnLayoutAffectsDeeplyNestedChild(Type type, bool in public void InputTransparencyOnLayoutDoesNotOverrideNestedLayoutWhenNotCascadeInputTransparentButCascadeOnNested(bool parent, bool nested) { var element = new Button(); + Grid nestedLayout; var layout = new Grid { InputTransparent = parent, @@ -440,7 +444,7 @@ public void InputTransparencyOnLayoutDoesNotOverrideNestedLayoutWhenNotCascadeIn { new Grid { - new Grid + (nestedLayout = new Grid { InputTransparent = nested, CascadeInputTransparent = true, // default @@ -448,23 +452,20 @@ public void InputTransparencyOnLayoutDoesNotOverrideNestedLayoutWhenNotCascadeIn { new Grid { element } } - } + }) } } }; - Assert.Equal(parent, layout.InputTransparent); - Assert.Equal(nested, element.InputTransparent); + AssertState(parent, false, nested, true, false, nestedLayout, element); layout.InputTransparent = !parent; - Assert.NotEqual(parent, layout.InputTransparent); - Assert.Equal(nested, element.InputTransparent); + AssertState(!parent, false, nested, true, false, nestedLayout, element); layout.InputTransparent = parent; - Assert.Equal(parent, layout.InputTransparent); - Assert.Equal(nested, element.InputTransparent); + AssertState(parent, false, nested, true, false, nestedLayout, element); } [Theory] @@ -475,6 +476,7 @@ public void InputTransparencyOnLayoutDoesNotOverrideNestedLayoutWhenNotCascadeIn public void InputTransparencyOnLayoutDoesNotOverrideNestedLayoutWhenNotCascadeInputTransparent(bool parent, bool nested) { var element = new Button(); + Grid nestedLayout; var layout = new Grid { InputTransparent = parent, @@ -483,7 +485,7 @@ public void InputTransparencyOnLayoutDoesNotOverrideNestedLayoutWhenNotCascadeIn { new Grid { - new Grid + (nestedLayout = new Grid { InputTransparent = nested, CascadeInputTransparent = false, @@ -491,23 +493,20 @@ public void InputTransparencyOnLayoutDoesNotOverrideNestedLayoutWhenNotCascadeIn { new Grid { element } } - } + }) } } }; - Assert.Equal(parent, layout.InputTransparent); - Assert.False(element.InputTransparent); + AssertState(parent, false, nested, false, false, nestedLayout, element); layout.InputTransparent = !parent; - Assert.NotEqual(parent, layout.InputTransparent); - Assert.False(element.InputTransparent); + AssertState(!parent, false, nested, false, false, nestedLayout, element); layout.InputTransparent = parent; - Assert.Equal(parent, layout.InputTransparent); - Assert.False(element.InputTransparent); + AssertState(parent, false, nested, false, false, nestedLayout, element); } [Theory] @@ -522,6 +521,7 @@ public void InputTransparencyOnLayoutDoesNotOverrideNestedLayoutWhenNotCascadeIn public void InputTransparencyOnLayoutOverridesNestedLayout(bool parent, bool nested, bool cascadeNested) { var element = new Button(); + Grid nestedLayout; var layout = new Grid { InputTransparent = parent, @@ -530,7 +530,7 @@ public void InputTransparencyOnLayoutOverridesNestedLayout(bool parent, bool nes { new Grid { - new Grid + (nestedLayout = new Grid { InputTransparent = nested, CascadeInputTransparent = cascadeNested, @@ -538,23 +538,20 @@ public void InputTransparencyOnLayoutOverridesNestedLayout(bool parent, bool nes { new Grid { element } } - } + }) } } }; - Assert.Equal(parent, layout.InputTransparent); - Assert.Equal(parent, element.InputTransparent); + AssertState(parent, true, nested, cascadeNested, false, nestedLayout, element); layout.InputTransparent = !parent; - Assert.NotEqual(parent, layout.InputTransparent); - Assert.NotEqual(parent, element.InputTransparent); + AssertState(!parent, true, nested, cascadeNested, false, nestedLayout, element); layout.InputTransparent = parent; - Assert.Equal(parent, layout.InputTransparent); - Assert.Equal(parent, element.InputTransparent); + AssertState(parent, true, nested, cascadeNested, false, nestedLayout, element); } } } From 77de113dbcbc7fbcd6a05443b69e1138fc0a63c8 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Mon, 1 May 2023 10:35:41 +0800 Subject: [PATCH 05/72] Also these platforms --- .../Core/HandlerImpl/Layout/Layout.Tizen.cs | 32 ++----------------- .../src/Core/HandlerImpl/Layout/Layout.iOS.cs | 22 ++----------- 2 files changed, 4 insertions(+), 50 deletions(-) diff --git a/src/Controls/src/Core/HandlerImpl/Layout/Layout.Tizen.cs b/src/Controls/src/Core/HandlerImpl/Layout/Layout.Tizen.cs index 5a54a70be665..079d21ba8bda 100644 --- a/src/Controls/src/Core/HandlerImpl/Layout/Layout.Tizen.cs +++ b/src/Controls/src/Core/HandlerImpl/Layout/Layout.Tizen.cs @@ -3,36 +3,8 @@ namespace Microsoft.Maui.Controls { public partial class Layout { - public static void MapInputTransparent(LayoutHandler handler, Layout layout) => - UpdateInputTransparent(handler, layout); + public static void MapInputTransparent(LayoutHandler handler, Layout layout) { } - public static void MapInputTransparent(ILayoutHandler handler, Layout layout) => - UpdateInputTransparent(handler, layout); - - static void MapInputTransparent(IViewHandler handler, IView layout) => - UpdateInputTransparent(handler, layout); - - static void UpdateInputTransparent(IViewHandler handler, IView view) - { - if (handler.PlatformView is not Microsoft.Maui.Platform.LayoutViewGroup platformView || - view is not Layout layout) - { - return; - } - - if (layout.CascadeInputTransparent) - { - // Sensitive property on NUI View was false, disabled all touch event including children - platformView.Sensitive = !layout.InputTransparent; - platformView.InputTransparent = false; - } - else - { - // InputTransparent property on LayoutViewGroup was false, - // Only LayoutViewGroup event was disabled but children are allowed - platformView.InputTransparent = layout.InputTransparent; - platformView.Sensitive = true; - } - } + public static void MapInputTransparent(ILayoutHandler handler, Layout layout) { } } } diff --git a/src/Controls/src/Core/HandlerImpl/Layout/Layout.iOS.cs b/src/Controls/src/Core/HandlerImpl/Layout/Layout.iOS.cs index 2f69361182f5..079d21ba8bda 100644 --- a/src/Controls/src/Core/HandlerImpl/Layout/Layout.iOS.cs +++ b/src/Controls/src/Core/HandlerImpl/Layout/Layout.iOS.cs @@ -3,26 +3,8 @@ namespace Microsoft.Maui.Controls { public partial class Layout { - public static void MapInputTransparent(LayoutHandler handler, Layout layout) => - UpdateInputTransparent(handler, layout); + public static void MapInputTransparent(LayoutHandler handler, Layout layout) { } - public static void MapInputTransparent(ILayoutHandler handler, Layout layout) => - UpdateInputTransparent(handler, layout); - - static void MapInputTransparent(IViewHandler handler, IView layout) => - UpdateInputTransparent(handler, layout); - - static void UpdateInputTransparent(IViewHandler handler, IView layout) - { - if (handler is ILayoutHandler layoutHandler && layout is Layout controlsLayout) - { - layoutHandler.PlatformView?.UpdateInputTransparent(layoutHandler, controlsLayout); - controlsLayout.UpdateDescendantInputTransparent(); - } - else - { - ControlsVisualElementMapper.UpdateProperty(handler, layout, nameof(IView.InputTransparent)); - } - } + public static void MapInputTransparent(ILayoutHandler handler, Layout layout) { } } } From 1224014f46043696287bd1600604f9b2dc4e2f30 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Mon, 1 May 2023 11:17:41 +0800 Subject: [PATCH 06/72] Support compatibility --- src/Controls/src/Core/IInputTransparentElement.cs | 14 ++++++++++++++ src/Controls/src/Core/Layout.cs | 15 +++++++++++++-- src/Controls/src/Core/Layout/Layout.cs | 2 +- src/Controls/src/Core/VisualElement.cs | 8 +++++--- 4 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 src/Controls/src/Core/IInputTransparentElement.cs diff --git a/src/Controls/src/Core/IInputTransparentElement.cs b/src/Controls/src/Core/IInputTransparentElement.cs new file mode 100644 index 000000000000..2aad3b7a19ba --- /dev/null +++ b/src/Controls/src/Core/IInputTransparentElement.cs @@ -0,0 +1,14 @@ +#nullable disable + +namespace Microsoft.Maui.Controls +{ + // There are 2 Layout types: Controls and Compatibility + interface IInputTransparentElement + { + bool InputTransparent { get; } + + bool CascadeInputTransparent { get; } + + Element Parent { get; } + } +} \ No newline at end of file diff --git a/src/Controls/src/Core/Layout.cs b/src/Controls/src/Core/Layout.cs index bd84179ac968..b07c3f2b3956 100644 --- a/src/Controls/src/Core/Layout.cs +++ b/src/Controls/src/Core/Layout.cs @@ -63,7 +63,7 @@ Size ILayoutManager.ArrangeChildren(Rect bounds) } } - public abstract class Layout : View, ILayout, ILayoutController, IPaddingElement, IView, IVisualTreeElement + public abstract class Layout : View, ILayout, ILayoutController, IPaddingElement, IView, IVisualTreeElement, IInputTransparentElement { /// Bindable property for . public static readonly BindableProperty IsClippedToBoundsProperty = @@ -72,7 +72,8 @@ public abstract class Layout : View, ILayout, ILayoutController, IPaddingElement /// Bindable property for . public static readonly BindableProperty CascadeInputTransparentProperty = - BindableProperty.Create(nameof(CascadeInputTransparent), typeof(bool), typeof(Layout), true); + BindableProperty.Create(nameof(CascadeInputTransparent), typeof(bool), typeof(Layout), true, + propertyChanged: OnCascadeInputTransparentPropertyChanged); /// Bindable property for . public static readonly BindableProperty PaddingProperty = PaddingElement.PaddingProperty; @@ -518,5 +519,15 @@ public Size CrossPlatformArrange(Rect bounds) return Frame.Size; } + + static void OnCascadeInputTransparentPropertyChanged(BindableObject bindable, object oldValue, object newValue) + { + // We only need to update if the cascade changes anything, namely when InputTransparent=true. + // When InputTransparent=false, then the cascade property has no effect. + if (bindable is Layout layout && layout.InputTransparent) + { + layout.RefreshInputTransparentProperty(); + } + } } } diff --git a/src/Controls/src/Core/Layout/Layout.cs b/src/Controls/src/Core/Layout/Layout.cs index fc8edd1ccfd9..510279013115 100644 --- a/src/Controls/src/Core/Layout/Layout.cs +++ b/src/Controls/src/Core/Layout/Layout.cs @@ -11,7 +11,7 @@ namespace Microsoft.Maui.Controls { /// [ContentProperty(nameof(Children))] - public abstract partial class Layout : View, Maui.ILayout, IList, IBindableLayout, IPaddingElement, IVisualTreeElement, ISafeAreaView + public abstract partial class Layout : View, Maui.ILayout, IList, IBindableLayout, IPaddingElement, IVisualTreeElement, ISafeAreaView, IInputTransparentElement { protected ILayoutManager _layoutManager; diff --git a/src/Controls/src/Core/VisualElement.cs b/src/Controls/src/Core/VisualElement.cs index 39975d680d61..5b24d3111e8b 100644 --- a/src/Controls/src/Core/VisualElement.cs +++ b/src/Controls/src/Core/VisualElement.cs @@ -12,7 +12,7 @@ namespace Microsoft.Maui.Controls { /// - public partial class VisualElement : NavigableElement, IAnimatable, IVisualElementController, IResourcesProvider, IStyleElement, IFlowDirectionController, IPropertyPropagationController, IVisualController, IWindowController + public partial class VisualElement : NavigableElement, IAnimatable, IVisualElementController, IResourcesProvider, IStyleElement, IFlowDirectionController, IPropertyPropagationController, IVisualController, IWindowController, IInputTransparentElement { /// public new static readonly BindableProperty NavigationProperty = NavigableElement.NavigationProperty; @@ -558,6 +558,8 @@ public bool InputTransparent set { SetValue(InputTransparentProperty, value); } } + bool IInputTransparentElement.CascadeInputTransparent => false; + /// public bool IsEnabled { @@ -611,12 +613,12 @@ private protected bool InputTransparentCore return true; } - var parent = Parent as Layout; + var parent = Parent as IInputTransparentElement; while (parent is not null) { if (parent.CascadeInputTransparent && parent.InputTransparent) return true; - parent = parent.Parent as Layout; + parent = parent.Parent as IInputTransparentElement; } return _inputTransparentExplicit; From c69dabd03eb98d56235a1271465aa153518bc475 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Wed, 3 May 2023 04:50:12 +0800 Subject: [PATCH 07/72] Improved the sample for testing --- .../Pages/Core/InputTransparentPage.xaml | 150 +++++++++++------- 1 file changed, 90 insertions(+), 60 deletions(-) diff --git a/src/Controls/samples/Controls.Sample/Pages/Core/InputTransparentPage.xaml b/src/Controls/samples/Controls.Sample/Pages/Core/InputTransparentPage.xaml index 70ff593aed87..5c10fc7f2ffb 100644 --- a/src/Controls/samples/Controls.Sample/Pages/Core/InputTransparentPage.xaml +++ b/src/Controls/samples/Controls.Sample/Pages/Core/InputTransparentPage.xaml @@ -3,70 +3,100 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Maui.Controls.Sample.Pages.InputTransparentPage"> - - + + + - - - - - - -