-
Notifications
You must be signed in to change notification settings - Fork 2k
Fix iOS/Win InputTransparent layouts and add several UI tests #17286
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
239 changes: 239 additions & 0 deletions
239
src/Controls/samples/Controls.Sample.UITests/Concepts/InputTransparencyGalleryPage.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,239 @@ | ||
| using System; | ||
| using Microsoft.Maui.Controls; | ||
|
|
||
| namespace Maui.Controls.Sample | ||
| { | ||
| internal class InputTransparencyGalleryPage : CoreGalleryBasePage | ||
| { | ||
| protected override void Build() | ||
| { | ||
| // Basic test with view defaults, should be clickable | ||
| Add(Test.InputTransparency.Default, new Button { Text = "Click Me!" }) | ||
| .With(t => t.View.Clicked += (s, e) => t.ReportSuccessEvent()); | ||
|
|
||
| // Test when InputTransparent is explicitly set to False, should be clickable | ||
| Add(Test.InputTransparency.IsFalse, new Button { Text = "Click Me!", InputTransparent = false }) | ||
| .With(t => t.View.Clicked += (s, e) => t.ReportSuccessEvent()); | ||
|
|
||
| // Test when InputTransparent is explicitly set to True, should NOT be clickable | ||
| // and we emulate this by putting another button underneath that should be clickable | ||
| Add(Test.InputTransparency.IsTrue, () => | ||
| { | ||
| var bottom = new Button { Text = "Bottom Button" }; | ||
| var top = new Button { Text = "Click Me!", InputTransparent = true }; | ||
| var grid = new Grid { bottom, top }; | ||
| return (grid, new { Bottom = bottom, Top = top }); | ||
| }) | ||
| .With(t => | ||
| { | ||
| var v = t.ViewContainer.View; | ||
| var bottom = t.Additional.Bottom; | ||
| var top = Annotate(t.Additional.Top, v); | ||
| bottom.Clicked += (s, e) => t.ViewContainer.ReportSuccessEvent(); | ||
| top.Clicked += (s, e) => t.ViewContainer.ReportFailEvent(); | ||
| }); | ||
|
|
||
| // Test when there is an InputTransparent layout over the button, should be clickable | ||
| Add(Test.InputTransparency.TransLayoutOverlay, () => | ||
| { | ||
| var button = new Button { Text = "Click Me!" }; | ||
| var grid = new Grid | ||
| { | ||
| new Grid { button }, | ||
| new Grid | ||
| { | ||
| InputTransparent = true, | ||
| Background = Brush.Red, | ||
| Opacity = 0.5, | ||
| } | ||
| }; | ||
| return (grid, new { Button = button }); | ||
| }) | ||
| .With(t => | ||
| { | ||
| var v = t.ViewContainer.View; | ||
| var button = Annotate(t.Additional.Button, v); | ||
| button.Clicked += (s, e) => t.ViewContainer.ReportSuccessEvent(); | ||
| }); | ||
|
|
||
| // Test when there is an InputTransparent layout over the button, should NOT be clickable | ||
| // but the button IN the layout should be clickable because it is not cascading | ||
| Add(Test.InputTransparency.TransLayoutOverlayWithButton, () => | ||
| { | ||
| var bottom = new Button { Text = "Bottom Button" }; | ||
| var top = new Button { Text = "Click Me!" }; | ||
| var grid = new Grid | ||
| { | ||
| new Grid { bottom }, | ||
| new Grid | ||
| { | ||
| InputTransparent = true, | ||
| CascadeInputTransparent = false, | ||
| Background = Brush.Red, | ||
| Opacity = 0.5, | ||
| Children = { top }, | ||
| } | ||
| }; | ||
| return (grid, new { Bottom = bottom, Top = top }); | ||
| }) | ||
| .With(t => | ||
| { | ||
| var v = t.ViewContainer.View; | ||
| var bottom = t.Additional.Bottom; | ||
| var top = Annotate(t.Additional.Top, v); | ||
| bottom.Clicked += (s, e) => t.ViewContainer.ReportFailEvent(); | ||
| top.Clicked += (s, e) => t.ViewContainer.ReportSuccessEvent(); | ||
| }); | ||
|
|
||
| // Test when there is an InputTransparent layout over the button, should be clickable | ||
| Add(Test.InputTransparency.CascadeTransLayoutOverlay, () => | ||
| { | ||
| var button = new Button { Text = "Click Me!" }; | ||
| var grid = new Grid | ||
| { | ||
| new Grid { button }, | ||
| new Grid | ||
| { | ||
| InputTransparent = true, | ||
| CascadeInputTransparent = true, | ||
| Background = Brush.Red, | ||
| Opacity = 0.5, | ||
| } | ||
| }; | ||
| return (grid, new { Button = button }); | ||
| }) | ||
| .With(t => | ||
| { | ||
| var v = t.ViewContainer.View; | ||
| var button = Annotate(t.Additional.Button, v); | ||
| button.Clicked += (s, e) => t.ViewContainer.ReportSuccessEvent(); | ||
| }); | ||
|
|
||
| // Test when there is an InputTransparent layout over the button, should be clickable | ||
| // and the button IN the layout should NOT be clickable because it is cascading | ||
| Add(Test.InputTransparency.CascadeTransLayoutOverlayWithButton, () => | ||
| { | ||
| var bottom = new Button { Text = "Bottom Button" }; | ||
| var top = new Button { Text = "Click Me!" }; | ||
| var grid = new Grid | ||
| { | ||
| new Grid { bottom }, | ||
| new Grid | ||
| { | ||
| InputTransparent = true, | ||
| CascadeInputTransparent = true, | ||
| Background = Brush.Red, | ||
| Opacity = 0.5, | ||
| Children = { top }, | ||
| } | ||
| }; | ||
| return (grid, new { Bottom = bottom, Top = top }); | ||
| }) | ||
| .With(t => | ||
| { | ||
| var v = t.ViewContainer.View; | ||
| var bottom = t.Additional.Bottom; | ||
| var top = Annotate(t.Additional.Top, v); | ||
| bottom.Clicked += (s, e) => t.ViewContainer.ReportSuccessEvent(); | ||
| top.Clicked += (s, e) => t.ViewContainer.ReportFailEvent(); | ||
| }); | ||
|
|
||
| // Tests for a nested layout (root grid, nested grid, button) with some variations to ensure | ||
| // that all combinations are correctly clickable | ||
| foreach (var state in Test.InputTransparencyMatrix.States) | ||
| { | ||
| var (rt, rc, nt, nc, t) = state.Key; | ||
| var (clickable, passthru) = state.Value; | ||
|
|
||
| AddNesting(rt, rc, nt, nc, t, clickable, passthru); | ||
| } | ||
| } | ||
|
|
||
| void AddNesting(bool rootTrans, bool rootCascade, bool nestedTrans, bool nestedCascade, bool trans, bool isClickable, bool isPassThru) => | ||
| Add(Test.InputTransparencyMatrix.GetKey(rootTrans, rootCascade, nestedTrans, nestedCascade, trans, isClickable, isPassThru), () => | ||
| { | ||
| var bottom = new Button { Text = "Bottom Button" }; | ||
| var top = new Button | ||
| { | ||
| InputTransparent = trans, | ||
| Text = "Click Me!" | ||
| }; | ||
| var grid = new Grid | ||
| { | ||
| new Grid { bottom }, | ||
| new Grid | ||
| { | ||
| InputTransparent = rootTrans, | ||
| CascadeInputTransparent = rootCascade, | ||
| Children = | ||
| { | ||
| new Grid | ||
| { | ||
| InputTransparent = nestedTrans, | ||
| CascadeInputTransparent = nestedCascade, | ||
| Children = { top } | ||
| } | ||
| }, | ||
| } | ||
| }; | ||
| return (grid, new { Bottom = bottom, Top = top }); | ||
| }) | ||
| .With(t => | ||
| { | ||
| var v = t.ViewContainer.View; | ||
| var bottom = t.Additional.Bottom; | ||
| var top = Annotate(t.Additional.Top, v); | ||
| if (isClickable) | ||
| { | ||
| // if the button is clickable, then it should be clickable | ||
| bottom.Clicked += (s, e) => t.ViewContainer.ReportFailEvent(); | ||
| top.Clicked += (s, e) => t.ViewContainer.ReportSuccessEvent(); | ||
| } | ||
| else if (!isPassThru) | ||
| { | ||
| // if one of the parent layouts are NOT transparent, then | ||
| // the tap should NOT go through to the bottom button | ||
| #if ANDROID | ||
| // TODO: Android is broken with everything passing through | ||
| // https://github.com/dotnet/maui/issues/10252 | ||
| bottom.Clicked += (s, e) => t.ViewContainer.ReportSuccessEvent(); | ||
| top.Clicked += (s, e) => t.ViewContainer.ReportFailEvent(); | ||
| #else | ||
| bottom.Clicked += (s, e) => t.ViewContainer.ReportFailEvent(); | ||
| top.Clicked += (s, e) => t.ViewContainer.ReportFailEvent(); | ||
| #endif | ||
| } | ||
| else | ||
| { | ||
| // otherwise, the tap should go through | ||
| bottom.Clicked += (s, e) => t.ViewContainer.ReportSuccessEvent(); | ||
| top.Clicked += (s, e) => t.ViewContainer.ReportFailEvent(); | ||
| } | ||
| }); | ||
|
|
||
| (ExpectedEventViewContainer<View> ViewContainer, T Additional) Add<T>(Test.InputTransparency test, Func<(View View, T Additional)> func) => | ||
| Add(test.ToString(), func); | ||
|
|
||
| (ExpectedEventViewContainer<View> ViewContainer, T Additional) Add<T>(string test, Func<(View View, T Additional)> func) | ||
| { | ||
| var result = func(); | ||
| var vc = new ExpectedEventViewContainer<View>(test, result.View); | ||
| Add(vc); | ||
| return (vc, result.Additional); | ||
| } | ||
|
|
||
| ExpectedEventViewContainer<Button> Add(Test.InputTransparency test, Button button) => | ||
| Add(new ExpectedEventViewContainer<Button>(test, button)); | ||
|
|
||
| static T Annotate<T>(T view, View desired) | ||
| where T : View | ||
| { | ||
| #if WINDOWS | ||
| // Windows does not have layouts in the automation tree | ||
| // and some of the tests have the layout as the root | ||
| view.AutomationId = desired.AutomationId; | ||
| #endif | ||
| return view; | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
src/Controls/samples/Controls.Sample/Converters/NegativeConverter.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| using System; | ||
| using System.Globalization; | ||
| using Microsoft.Maui.Controls; | ||
|
|
||
| namespace Controls.Sample.Converters | ||
| { | ||
| public class NegativeConverter : IValueConverter | ||
| { | ||
| public object Convert(object value, Type targetType, object parameter, CultureInfo culture) | ||
| { | ||
| if (value is bool v) | ||
| return !v; | ||
| else | ||
| return false; | ||
| } | ||
|
|
||
| public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) | ||
| { | ||
| if (value is bool v) | ||
| return !v; | ||
| else | ||
| return true; | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Android currently is semi-broken: #10252 but fixed in #13725