-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Improve VisualState order and prevent sticky Focused visual state #27477
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
Open
mattleibow
wants to merge
9
commits into
main
Choose a base branch
from
dev/focus-visual-states
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
4c54fbb
Make `PointerOver` to have higher priority than `Focused`
MartyIX 89d21a4
Sandbox demo for easy testing
MartyIX 825cd7a
Revert "Sandbox demo for easy testing"
MartyIX dc831a6
Copy WinUI focus state pattern
mattleibow f61491c
Update src/TestUtils/src/UITest.Appium/HelperExtensions.cs
mattleibow 403d1e5
Update src/TestUtils/src/UITest.Appium/HelperExtensions.cs
mattleibow a31747c
Update src/TestUtils/src/UITest.Appium/HelperExtensions.cs
mattleibow 72e7d0c
Add checks for actual screen density
mattleibow dd79425
Try and order the state changes better
mattleibow 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
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 |
|---|---|---|
|
|
@@ -6,4 +6,4 @@ public MainPage() | |
| { | ||
| InitializeComponent(); | ||
| } | ||
| } | ||
| } | ||
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
67 changes: 67 additions & 0 deletions
67
src/Controls/tests/TestCases.HostApp/Issues/Issue19752.xaml
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,67 @@ | ||
| <?xml version="1.0" encoding="utf-8" ?> | ||
| <ContentPage | ||
| xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | ||
| xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | ||
| x:Class="Maui.Controls.Sample.Issues.Issue19752" | ||
| xmlns:cv1="clr-namespace:Maui.Controls.Sample" | ||
| xmlns:local="clr-namespace:Maui.Controls.Sample.Issues" | ||
| x:Name="ThisMainPage" | ||
| Title="Main Page"> | ||
|
|
||
| <VerticalStackLayout Padding="20,120" Spacing="20"> | ||
| <VerticalStackLayout.Resources> | ||
| <Style TargetType="Button"> | ||
| <Setter Property="VisualStateManager.VisualStateGroups"> | ||
| <VisualStateGroupList> | ||
| <VisualStateGroup x:Name="CommonStates"> | ||
| <VisualState x:Name="Normal"> | ||
| <VisualState.Setters> | ||
| <Setter Property="Text" Value="Normal"/> | ||
| </VisualState.Setters> | ||
| </VisualState> | ||
| <VisualState x:Name="PointerOver"> | ||
| <VisualState.Setters> | ||
| <Setter Property="Text" Value="PointerOver"/> | ||
| </VisualState.Setters> | ||
| </VisualState> | ||
| <VisualState x:Name="Pressed"> | ||
| <VisualState.Setters> | ||
| <Setter Property="Text" Value="Pressed"/> | ||
| </VisualState.Setters> | ||
| </VisualState> | ||
| <VisualState x:Name="Disabled"> | ||
| <VisualState.Setters> | ||
| <Setter Property="Text" Value="Disabled"/> | ||
| </VisualState.Setters> | ||
| </VisualState> | ||
| </VisualStateGroup> | ||
| <VisualStateGroup x:Name="FocusStates"> | ||
| <VisualState x:Name="Focused"> | ||
| <VisualState.Setters> | ||
| <Setter Property="Text" Value="Focused"/> | ||
| <Setter Property="BorderColor" Value="Red"/> | ||
| <Setter Property="Margin" Value="20,0"/> | ||
| </VisualState.Setters> | ||
| </VisualState> | ||
| <VisualState x:Name="Unfocused"> | ||
| <VisualState.Setters> | ||
| <Setter Property="Text" Value="Unfocused"/> | ||
| <Setter Property="BorderColor" Value="Lime"/> | ||
| <Setter Property="Margin" Value="0"/> | ||
| </VisualState.Setters> | ||
| </VisualState> | ||
| </VisualStateGroup> | ||
| </VisualStateGroupList> | ||
| </Setter> | ||
| </Style> | ||
| </VerticalStackLayout.Resources> | ||
|
|
||
| <Button x:Name="button1" AutomationId="button1" Clicked="OnButtonClicked" /> | ||
|
|
||
| <Button x:Name="button2" AutomationId="button2" Clicked="OnButtonClicked" IsEnabled="False" /> | ||
|
|
||
| <Button x:Name="button3" AutomationId="button3" Clicked="OnButtonClicked" /> | ||
|
|
||
| </VerticalStackLayout> | ||
|
|
||
| </ContentPage> |
29 changes: 29 additions & 0 deletions
29
src/Controls/tests/TestCases.HostApp/Issues/Issue19752.xaml.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,29 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Collections.ObjectModel; | ||
| using System.ComponentModel; | ||
| using System.Runtime.CompilerServices; | ||
| using System.Windows.Input; | ||
| using System.Collections.Specialized; | ||
|
|
||
| namespace Maui.Controls.Sample.Issues | ||
| { | ||
| [XamlCompilation(XamlCompilationOptions.Compile)] | ||
| [Issue(IssueTracker.Github, 19752, "Button does not behave properly when pointer hovers over the button because it's in focused state.")] | ||
| public partial class Issue19752 | ||
| { | ||
| public Issue19752() | ||
| { | ||
| InitializeComponent(); | ||
| } | ||
|
|
||
| private void OnButtonClicked(object sender, EventArgs e) | ||
| { | ||
| // this code just enables all the buttons and disables the current one | ||
| // except for the first button which is always enabled | ||
|
|
||
| button2.IsEnabled = sender != button2; | ||
| button3.IsEnabled = sender != button3; | ||
| } | ||
| } | ||
| } |
125 changes: 125 additions & 0 deletions
125
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19752.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,125 @@ | ||||||
| using NUnit.Framework; | ||||||
| using UITest.Appium; | ||||||
| using UITest.Core; | ||||||
|
|
||||||
| namespace Microsoft.Maui.TestCases.Tests.Issues; | ||||||
|
|
||||||
| [Category(UITestCategories.Focus)] | ||||||
| public class Issue19752(TestDevice device) : _IssuesUITest(device) | ||||||
| { | ||||||
| public override string Issue => "Button does not behave properly when pointer hovers over the button because it's in focused state."; | ||||||
|
|
||||||
| protected override bool ResetAfterEachTest => true; | ||||||
|
|
||||||
| [Test] | ||||||
| public void InitialStateAreAllCorrect() | ||||||
| { | ||||||
| Assert.That(App.FindElement("button1").GetText(), Is.EqualTo("Normal")); | ||||||
| Assert.That(App.FindElement("button2").GetText(), Is.EqualTo("Disabled")); | ||||||
| Assert.That(App.FindElement("button3").GetText(), Is.EqualTo("Normal")); | ||||||
| } | ||||||
|
|
||||||
| [Test] | ||||||
| public void HoveringOverButtonMovesToPointerOverState() | ||||||
| { | ||||||
| App.MoveCursor("button1"); | ||||||
|
|
||||||
| // when the mouse moves over a button, it gets a state | ||||||
| Assert.That(App.FindElement("button1").GetText(), Is.EqualTo("PointerOver")); | ||||||
| } | ||||||
|
|
||||||
| // TODO: find a way to send actions to appium and then read values simultaneously | ||||||
| // [Test] | ||||||
| // public void PressingButtonMovesToPressedState() | ||||||
| // { | ||||||
| // Task.Run(() => | ||||||
| // { | ||||||
| //#if MACCATALYST | ||||||
| // App.LongPress("button1"); | ||||||
| //#else | ||||||
| // App.TouchAndHold("button1"); | ||||||
| //#endif | ||||||
| // }); | ||||||
|
|
||||||
| // // pressing and holding the mouse is pressed | ||||||
| // App.WaitForTextToBePresentInElement("button1", "Pressed"); | ||||||
| // Assert.That(App.FindElement("button1").GetText(), Is.EqualTo("Pressed")); | ||||||
| // } | ||||||
|
|
||||||
| [Test] | ||||||
| public void PressingAndReleasingButtonMovesToPointerOverState() | ||||||
| { | ||||||
| var rectBefore = App.FindElement("button1").GetRect(); | ||||||
|
|
||||||
| App.Tap("button1"); | ||||||
|
|
||||||
| // pressing a button sets it to be focused, but the pointer over state is appplied after | ||||||
| Assert.That(App.FindElement("button1").GetText(), Is.EqualTo("PointerOver")); | ||||||
|
|
||||||
| // we are shrinking the focused button a bit | ||||||
| var rectAfter = App.FindElement("button1").GetRect(); | ||||||
| Assert.That(rectBefore, Is.Not.EqualTo(rectAfter)); | ||||||
| } | ||||||
|
|
||||||
| [Test] | ||||||
| public void HoveringOverButtonAndThenMovingOffMovesToNormalState() | ||||||
| { | ||||||
| var rectBefore = App.FindElement("button1").GetRect(); | ||||||
|
|
||||||
| App.MoveCursor("button1"); | ||||||
| App.MoveCursor("button2"); | ||||||
|
|
||||||
| // hovering over a button and then moving off goes back to the normal state | ||||||
| // and does not affect focus | ||||||
| Assert.That(App.FindElement("button1").GetText(), Is.EqualTo("Normal")); | ||||||
|
|
||||||
| // we are shrinking the focused button a bit, but the button is still not focused | ||||||
| var rectAfter = App.FindElement("button1").GetRect(); | ||||||
| Assert.That(rectBefore, Is.EqualTo(rectAfter)); | ||||||
| } | ||||||
|
|
||||||
| [Test] | ||||||
| public void EnablingButtonMovesToNormalState() | ||||||
| { | ||||||
| App.Tap("button1"); | ||||||
|
|
||||||
| // enabling a button just switches to the normal state | ||||||
| Assert.That(App.FindElement("button2").GetText(), Is.EqualTo("Normal")); | ||||||
| } | ||||||
|
|
||||||
| [Test] | ||||||
| public void DisablingUnfocusedButtonMovesToDisabledState() | ||||||
| { | ||||||
| var rectBefore = App.FindElement("button2").GetRect(); | ||||||
|
|
||||||
| App.Tap("button1"); // focus button 1 | ||||||
| App.Tap("button2"); // move the focus to button 2, but then disable it | ||||||
|
|
||||||
| // the button is disabled without a focus chnage as it never had focus | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| Assert.That(App.FindElement("button2").GetText(), Is.EqualTo("Disabled")); | ||||||
|
|
||||||
| // we are shrinking the focused button a bit, but the button never had focus | ||||||
| var rectAfter = App.FindElement("button2").GetRect(); | ||||||
| Assert.That(rectBefore, Is.EqualTo(rectAfter)); | ||||||
|
|
||||||
| // this forces focus to button 3 which is set on top of the normal state | ||||||
| Assert.That(App.FindElement("button3").GetText(), Is.EqualTo("Focused")); | ||||||
| } | ||||||
|
|
||||||
| [Test] | ||||||
| public void DisablingFocusedButtonMovesToDisabledState() | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if my setup is wrong but I tried to run this test on Windows and I got: Details
(I rebased on the latest |
||||||
| { | ||||||
| var rectBefore = App.FindElement("button3").GetRect(); | ||||||
|
|
||||||
| App.Tap("button1"); // focus button 1 | ||||||
| App.Tap("button2"); // move the focus to button 2, but then disable it forcing focus to button 3 | ||||||
| App.Tap("button3"); // disable the focused button | ||||||
|
|
||||||
| // this disables the button, but the unfocus change is applied before all states | ||||||
| Assert.That(App.FindElement("button3").GetText(), Is.EqualTo("Disabled")); | ||||||
|
|
||||||
| // we are shrinking the focused button a bit, so it should have been unfocused after disabling | ||||||
| var rectAfter = App.FindElement("button3").GetRect(); | ||||||
| Assert.That(rectBefore, Is.EqualTo(rectAfter)); | ||||||
| } | ||||||
| } | ||||||
Oops, something went wrong.
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.
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.