diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/AfterToggleBackgroundColors.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/AfterToggleBackgroundColors.png new file mode 100644 index 000000000000..5f87399b5d21 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/AfterToggleBackgroundColors.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BeforeToggleBackgroundColors.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BeforeToggleBackgroundColors.png new file mode 100644 index 000000000000..6f4d8fce30f3 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BeforeToggleBackgroundColors.png differ diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue17389.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue17389.cs new file mode 100644 index 000000000000..0f4fe56486cc --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue17389.cs @@ -0,0 +1,147 @@ +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 17389, "InputTransparent should not affect background color on Windows layouts", PlatformAffected.UWP)] +public class Issue17389 : TestContentPage +{ + Grid redGrid; + Grid greenGrid; + Border blueBorder; + ContentView purpleContent; + Label tapCountLabel; + Label redGridLabel; + Label greenGridLabel; + Label blueBorderLabel; + Label purpleContentLabel; + int tapCount; + + protected override void Init() + { + tapCountLabel = new Label { Text = "Tap count: 0", HorizontalOptions = LayoutOptions.Center }; + + redGrid = CreateBackgroundTestGrid(Colors.Red, false, "RedGrid", out redGridLabel); + greenGrid = CreateBackgroundTestGrid(Colors.Green, false, "GreenGrid", out greenGridLabel); + + blueBorderLabel = new Label { Text = "Blue Border (InputTransparent=False)", HorizontalOptions = LayoutOptions.Center, AutomationId = "BlueBorder" }; + blueBorder = new Border + { + BackgroundColor = Colors.Blue, + InputTransparent = false, + WidthRequest = 200, + HeightRequest = 100, + Content = blueBorderLabel + }; + AddTapGesture(blueBorder); + + purpleContentLabel = new Label { Text = "Purple Content (InputTransparent=False)", AutomationId = "PurpleContent" }; + purpleContent = new ContentView + { + BackgroundColor = Colors.Purple, + InputTransparent = false, + WidthRequest = 200, + HeightRequest = 100, + Content = purpleContentLabel + }; + AddTapGesture(purpleContent); + + Content = CreateMainContent(); + } + + ScrollView CreateMainContent() + { + return new ScrollView + { + Content = new StackLayout + { + Spacing = 20, + Children = + { + new Label + { + Text = "InputTransparent Background Test", + FontSize = 18, + FontAttributes = FontAttributes.Bold, + HorizontalOptions = LayoutOptions.Center + }, + tapCountLabel, + new Button + { + Text = "Toggle InputTransparent", + Command = new Command(ToggleInputTransparent), + AutomationId = "ToggleInputTransparentButton" + }, + new Button + { + Text = "Toggle Background Colors", + Command = new Command(ToggleBackgroundColors), + AutomationId = "ToggleBackgroundColorsButton" + }, + redGrid, + greenGrid, + blueBorder, + purpleContent + } + } + }; + } + + Grid CreateBackgroundTestGrid(Color bgColor, bool inputTransparent, string labelText, out Label label) + { + label = new Label { Text = $"{labelText} (InputTransparent={inputTransparent})", HorizontalOptions = LayoutOptions.Center, AutomationId = $"{labelText}" }; + + Grid childGrid = new Grid + { + BackgroundColor = bgColor, + InputTransparent = inputTransparent, + Children = { label } + }; + + AddTapGesture(childGrid); + + Grid parentGrid = new Grid + { + WidthRequest = 200, + HeightRequest = 100, + InputTransparent = inputTransparent, + BackgroundColor = Colors.LightGray, + Children = { childGrid } + }; + + AddTapGesture(parentGrid); + return parentGrid; + } + + void AddTapGesture(View view) + { + view.GestureRecognizers.Add(new TapGestureRecognizer + { + Command = new Command(() => + { + tapCount++; + tapCountLabel.Text = $"Tap count: {tapCount}"; + }) + }); + } + + void ToggleInputTransparent() + { + tapCount = 0; + tapCountLabel.Text = $"Tap count: {0}"; + redGrid.InputTransparent = !redGrid.InputTransparent; + greenGrid.InputTransparent = !greenGrid.InputTransparent; + blueBorder.InputTransparent = !blueBorder.InputTransparent; + purpleContent.InputTransparent = !purpleContent.InputTransparent; + + redGridLabel.Text = $"Red Grid (InputTransparent={redGrid.InputTransparent})"; + greenGridLabel.Text = $"Green Grid (InputTransparent={greenGrid.InputTransparent})"; + blueBorderLabel.Text = $"Blue Border (InputTransparent={blueBorder.InputTransparent})"; + purpleContentLabel.Text = $"Purple Content (InputTransparent={purpleContent.InputTransparent})"; + } + + void ToggleBackgroundColors() + { + redGrid.BackgroundColor = Colors.Yellow; + greenGrid.BackgroundColor = Colors.Blue; + blueBorder.BackgroundColor = Colors.LightBlue; + purpleContent.BackgroundColor = Colors.Pink; + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/AfterToggleBackgroundColors.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/AfterToggleBackgroundColors.png new file mode 100644 index 000000000000..14c2021fea2b Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/AfterToggleBackgroundColors.png differ diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BeforeToggleBackgroundColors.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BeforeToggleBackgroundColors.png new file mode 100644 index 000000000000..1584e854e131 Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BeforeToggleBackgroundColors.png differ diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17389.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17389.cs new file mode 100644 index 000000000000..a26c93a25efc --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17389.cs @@ -0,0 +1,62 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue17389 : _IssuesUITest +{ + public Issue17389(TestDevice testDevice) : base(testDevice) + { + } + + public override string Issue => "InputTransparent should not affect background color on Windows layouts"; + + [Test, Order(1)] + [Category(UITestCategories.Layout)] + public void ValidateBackgroundColorDoesNotAffectInputTransparent() + { + App.WaitForElement("ToggleInputTransparentButton"); + + string[] layouts = new[] + { + "RedGrid", + "GreenGrid", + "BlueBorder", + "PurpleContent" + }; + + foreach (var layout in layouts) + { + App.Tap(layout); + } + + App.WaitForElement("Tap count: 4"); + App.Click("ToggleInputTransparentButton"); + + foreach (var layout in layouts) + { + App.Tap(layout); + } + + App.WaitForElement("Tap count: 0"); + } + + [Test, Order(2)] + [Category(UITestCategories.Layout)] + public void ValidateInputTransparentBackgroundColorToggle() + { + Exception? exception = null; + + App.WaitForElement("ToggleBackgroundColorsButton"); + VerifyScreenshotOrSetException(ref exception, "BeforeToggleBackgroundColors"); + App.WaitForElement("ToggleBackgroundColorsButton"); + App.Tap("ToggleBackgroundColorsButton"); + VerifyScreenshotOrSetException(ref exception, "AfterToggleBackgroundColors"); + + if (exception != null) + { + throw exception; + } + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/AfterToggleBackgroundColors.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/AfterToggleBackgroundColors.png new file mode 100644 index 000000000000..6ec211c4ffe6 Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/AfterToggleBackgroundColors.png differ diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/BeforeToggleBackgroundColors.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/BeforeToggleBackgroundColors.png new file mode 100644 index 000000000000..16606e2f62d0 Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/BeforeToggleBackgroundColors.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/AfterToggleBackgroundColors.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/AfterToggleBackgroundColors.png new file mode 100644 index 000000000000..eddaf08135d6 Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/AfterToggleBackgroundColors.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BeforeToggleBackgroundColors.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BeforeToggleBackgroundColors.png new file mode 100644 index 000000000000..ddbbc70414a1 Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BeforeToggleBackgroundColors.png differ diff --git a/src/Core/src/Platform/Windows/LayoutPanel.cs b/src/Core/src/Platform/Windows/LayoutPanel.cs index 2ee75ec4df3b..c64b63fa6837 100644 --- a/src/Core/src/Platform/Windows/LayoutPanel.cs +++ b/src/Core/src/Platform/Windows/LayoutPanel.cs @@ -9,6 +9,7 @@ namespace Microsoft.Maui.Platform { public partial class LayoutPanel : MauiPanel { + const int BackgroundLayerZIndex = int.MinValue; Canvas? _backgroundLayer; public bool ClipsToBounds { get; set; } @@ -19,6 +20,12 @@ protected override WSize ArrangeOverride(WSize finalSize) { var actual = base.ArrangeOverride(finalSize); + if (_backgroundLayer is not null) + { + _backgroundLayer.Measure(finalSize); + _backgroundLayer.Arrange(new WRect(0, 0, finalSize.Width, finalSize.Height)); + } + if (!(Parent is ContentPanel contentPanel && contentPanel.BorderStroke?.Shape is not null)) { Clip = ClipsToBounds ? new RectangleGeometry { Rect = new WRect(0, 0, finalSize.Width, finalSize.Height) } : null; @@ -58,7 +65,7 @@ void MakeInputTransparent(Brush? background) void MakeInputVisible(Brush? background) { - // If we aren't input transparent, we don't need the background layer hack + // If we aren't input transparent, we don't need the background layer hack RemoveBackgroundLayer(); if (background == null) @@ -74,22 +81,25 @@ void MakeInputVisible(Brush? background) void AddBackgroundLayer() { // In WinUI, once a control has hit testing disabled, all of its child controls - // also have hit testing disabled. The exception is a Panel with its + // also have hit testing disabled. The exception is a Panel with its // Background Brush set to `null`; the Panel will be invisible to hit testing, but its - // children will work just fine. + // children will work just fine. // In order to handle the situation where we need the layout to be invisible to hit testing, // the child controls to be visible to hit testing, *and* we need to support non-null - // background brushes, we insert another empty Panel which is invisible to hit testing; that - // Panel will be our Background brush + // background brushes, we append another empty Panel which is invisible to hit testing; that + // Panel will be our Background brush. We force it behind real layout children using ZIndex. - if (_backgroundLayer != null) + if (_backgroundLayer == null) { - return; + _backgroundLayer = new Canvas { IsHitTestVisible = false }; + Canvas.SetZIndex(_backgroundLayer, BackgroundLayerZIndex); } - _backgroundLayer = new Canvas { IsHitTestVisible = false }; - CachedChildren.Insert(0, _backgroundLayer); + if (!CachedChildren.Contains(_backgroundLayer)) + { + CachedChildren.Add(_backgroundLayer); + } } void RemoveBackgroundLayer() @@ -103,4 +113,4 @@ void RemoveBackgroundLayer() _backgroundLayer = null; } } -} +} \ No newline at end of file diff --git a/src/Core/src/Platform/Windows/ViewExtensions.cs b/src/Core/src/Platform/Windows/ViewExtensions.cs index 18fea5a6b15a..9b7019121858 100644 --- a/src/Core/src/Platform/Windows/ViewExtensions.cs +++ b/src/Core/src/Platform/Windows/ViewExtensions.cs @@ -439,7 +439,7 @@ public static void UpdateInputTransparent(this FrameworkElement nativeView, IVie public static void UpdateInputTransparent(this LayoutPanel layoutPanel, ILayoutHandler handler, ILayout layout) { - // Nothing to do yet, but we might need to adjust the wrapper view + // Nothing to do yet, but we might need to adjust the wrapper view } } -} +} \ No newline at end of file