Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ public MainPage()
{
InitializeComponent();
}
}
}
38 changes: 27 additions & 11 deletions src/Controls/src/Core/VisualElement/VisualElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ namespace Microsoft.Maui.Controls
/// <remarks>
/// The base class for most .NET MAUI on-screen elements. Provides most properties, events, and methods for presenting an item on screen.
/// </remarks>

[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
public partial class VisualElement : NavigableElement, IAnimatable, IVisualElementController, IResourcesProvider, IStyleElement, IFlowDirectionController, IPropertyPropagationController, IVisualController, IWindowController, IView, IControlsVisualElement
{
Expand Down Expand Up @@ -1607,26 +1606,43 @@ private protected void SetPointerOver(bool value, bool callChangeVisualState = t
/// </summary>
protected internal virtual void ChangeVisualState()
{
// A disabled control should never be in a focused state as part of the feature
// of being disabled is that it cannot receive focus. If it was in focus, then
// it has to go out of focus.
var shouldFocus = IsFocused && IsEnabled;

// If the control cannot have focus, make sure it appears unfocused by moving to
// the Unfocused state.
if (!shouldFocus)
{
VisualStateManager.GoToState(this, VisualStateManager.FocusStates.Unfocused);
}

// Set the Disabled or Normal states depending on the value of IsEnabled and
// IsPointerOver. We set the PointerOver state later, after the Focused state.
if (!IsEnabled)
{
VisualStateManager.GoToState(this, VisualStateManager.CommonStates.Disabled);
}
else if (IsPointerOver)
else if (!IsPointerOver)
{
VisualStateManager.GoToState(this, VisualStateManager.CommonStates.PointerOver);
VisualStateManager.GoToState(this, VisualStateManager.CommonStates.Normal);
}
else

// Go to the Focus state after the Normal state, so that the Focus state can
// override the Normal state's properties if a control is both focused and
// hovered.
if (shouldFocus)
{
VisualStateManager.GoToState(this, VisualStateManager.CommonStates.Normal);
VisualStateManager.GoToState(this, VisualStateManager.FocusStates.Focused);
}

if (IsEnabled)
// The PointerOver state is applied last so that it can override all the states. Even
// though this state is separate here, it should still be part of the CommonStates
// visual state group.
if (IsPointerOver)
{
// Focus needs to be handled independently; otherwise, if no actual Focus state is supplied
// in the control's visual states, the state can end up stuck in PointerOver after the pointer
// exits and the control still has focus.
VisualStateManager.GoToState(this,
IsFocused ? VisualStateManager.CommonStates.Focused : VisualStateManager.CommonStates.Unfocused);
VisualStateManager.GoToState(this, VisualStateManager.CommonStates.PointerOver);
}
}

Expand Down
10 changes: 8 additions & 2 deletions src/Controls/src/Core/VisualStateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,16 @@ public class CommonStates
{
public const string Normal = "Normal";
public const string Disabled = "Disabled";
public const string Focused = "Focused";
public const string Focused = FocusStates.Focused;
public const string Selected = "Selected";
public const string PointerOver = "PointerOver";
internal const string Unfocused = "Unfocused";
}

// TODO: .NET 10 - make public
internal class FocusStates
{
public const string Focused = "Focused";
public const string Unfocused = "Unfocused";
}

/// <summary>Bindable property for attached property <c>VisualStateGroups</c>.</summary>
Expand Down
67 changes: 67 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue19752.xaml
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 src/Controls/tests/TestCases.HostApp/Issues/Issue19752.xaml.cs
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 src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19752.cs
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// pressing a button sets it to be focused, but the pointer over state is appplied after
// Pressing a button sets it to be focused, but the pointer over state is applied 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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// the button is disabled without a focus chnage as it never had focus
// the button is disabled without a focus change as it never had focus

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()
Copy link
Copy Markdown
Contributor

@MartyIX MartyIX Mar 23, 2025

Choose a reason for hiding this comment

The 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

Appium �[35m[Appium]�[39m Welcome to Appium v2.5.4
Appium �[35m[Appium]�[39m Non-default server args:
Appium �[35m[Appium]�[39m { address: �[32m'127.0.0.1'�[39m, basePath: �[32m'/wd/hub'�[39m }
Appium �[35m[Appium]�[39m The autodetected Appium home path: C:\Users\user\.appium
Appium �[35m[Appium]�[39m Attempting to load driver windows...
Appium �[35m[Appium]�[39m Attempting to load driver uiautomator2...
Appium �[35m[Appium]�[39m Attempting to load driver xcuitest...
Appium �[35m[Appium]�[39m Requiring driver at C:\Users\user\.appium\node_modules\appium-uiautomator2-driver\build\index.js
Appium �[35m[Appium]�[39m Requiring driver at C:\Users\user\.appium\node_modules\appium-windows-driver\build\index.js
Appium �[35m[Appium]�[39m Requiring driver at C:\Users\user\.appium\node_modules\appium-xcuitest-driver\build\index.js
Appium �[35m[Appium]�[39m WindowsDriver has been successfully loaded in 0.243s
'testhost.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\9.0.3\System.Diagnostics.StackTrace.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Appium �[35m[Appium]�[39m AndroidUiautomator2Driver has been successfully loaded in 2.399s
Appium �[35m[Appium]�[39m Attempting to load driver mac2...
Appium �[35m[Appium]�[39m XCUITestDriver has been successfully loaded in 5.077s
Appium �[35m[Appium]�[39m Requiring driver at C:\Users\user\.appium\node_modules\appium-mac2-driver\build\index.js
Appium �[35m[Appium]�[39m Mac2Driver has been successfully loaded in 2.984s
Appium �[35m[Appium]�[39m Appium REST http interface listener started on http://127.0.0.1:4723/wd/hub
Appium �[35m[Appium]�[39m Available drivers:
Appium �[35m[Appium]�[39m   - windows@2.12.23 (automationName 'Windows')
Appium �[35m[Appium]�[39m   - uiautomator2@2.45.1 (automationName 'UiAutomator2')
Appium �[35m[Appium]�[39m   - xcuitest@5.2.0 (automationName 'XCUITest')
Appium �[35m[Appium]�[39m   - mac2@1.7.2 (automationName 'Mac2')
Appium �[35m[Appium]�[39m No plugins have been installed. Use the "appium plugin" command to install the one(s) you want to use.
Appium �[38;5;0m[HTTP]�[0m �[37m-->�[39m �[37mGET�[39m �[37m/wd/hub/status�[39m
Appium �[38;5;0m[HTTP]�[0m �[90m{}�[39m
Appium �[38;5;16m[AppiumDriver@efa7]�[0m Calling AppiumDriver.getStatus() with args: []
Appium �[38;5;16m[AppiumDriver@efa7]�[0m Responding to client with driver.getStatus() result: {"ready":true,"message":"The server is ready to accept new connections","build":{"version":"2.5.4"}}
Appium �[38;5;0m[HTTP]�[0m �[37m<-- GET /wd/hub/status �[39m�[32m200�[39m �[90m9 ms - 110�[39m
Appium �[38;5;0m[HTTP]�[0m �[90m�[39m
Appium �[38;5;0m[HTTP]�[0m �[37m-->�[39m �[37mGET�[39m �[37m/wd/hub/status�[39m
Appium �[38;5;0m[HTTP]�[0m �[90m{}�[39m
Appium �[38;5;16m[AppiumDriver@efa7]�[0m Calling AppiumDriver.getStatus() with args: []
Appium �[38;5;16m[AppiumDriver@efa7]�[0m Responding to client with driver.getStatus() result: {"ready":true,"message":"The server is ready to accept new connections","build":{"version":"2.5.4"}}
Appium �[38;5;0m[HTTP]�[0m �[37m<-- GET /wd/hub/status �[39m�[32m200�[39m �[90m2 ms - 110�[39m
Appium �[38;5;0m[HTTP]�[0m �[90m�[39m
'testhost.exe' (CoreCLR: clrhost): Loaded 'X:\maui\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net9.0\VisualTestUtils.MagickNet.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'testhost.exe' (CoreCLR: clrhost): Loaded 'X:\maui\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net9.0\Magick.NET.Core.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Appium �[38;5;0m[HTTP]�[0m �[37m-->�[39m �[37mGET�[39m �[37m/wd/hub/status�[39m
Appium �[38;5;0m[HTTP]�[0m �[90m{}�[39m
Appium �[38;5;16m[AppiumDriver@efa7]�[0m Calling AppiumDriver.getStatus() with args: []
Appium �[38;5;16m[AppiumDriver@efa7]�[0m Responding to client with driver.getStatus() result: {"ready":true,"message":"The server is ready to accept new connections","build":{"version":"2.5.4"}}
Appium �[38;5;0m[HTTP]�[0m �[37m<-- GET /wd/hub/status �[39m�[32m200�[39m �[90m2 ms - 110�[39m
Appium �[38;5;0m[HTTP]�[0m �[90m�[39m
'testhost.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\9.0.3\System.Text.Json.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'testhost.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\9.0.3\System.Text.Encodings.Web.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'testhost.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\9.0.3\System.IO.Pipelines.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Appium �[38;5;0m[HTTP]�[0m Request idempotency key: c3fbc536-d706-4c8a-9069-f6be1c86ce15
Appium �[38;5;0m[HTTP]�[0m �[37m-->�[39m �[37mPOST�[39m �[37m/wd/hub/session�[39m
Appium �[38;5;0m[HTTP]�[0m �[90m{"capabilities":{"firstMatch":[{"platformName":"Windows","appium:app":"X:\\maui\\artifacts\\bin\\Controls.TestCases.WinUI.Tests\\Debug\\net9.0\\..\\..\\..\\Controls.TestCases.HostApp\\Debug\\net9.0-windows10.0.20348.0\\win10-x64\\Controls.TestCases.HostApp.exe","appium:automationName":"Windows","appium:deviceName":"WindowsPC","appium:reportDirectory":"reports","appium:reportFormat":"xml","appium:newCommandTimeout":3000}]}}�[39m
Appium �[38;5;16m[AppiumDriver@efa7]�[0m Calling AppiumDriver.createSession() with args: [null,null,{"firstMatch":[{"platformName":"Windows","appium:app":"X:\\maui\\artifacts\\bin\\Controls.TestCases.WinUI.Tests\\Debug\\net9.0\\..\\..\\..\\Controls.TestCases.HostApp\\Debug\\net9.0-windows10.0.20348.0\\win10-x64\\Controls.TestCases.HostApp.exe","appium:automationName":"Windows","appium:deviceName":"WindowsPC","appium:reportDirectory":"reports","appium:reportFormat":"xml","appium:newCommandTimeout":3000}]}]
Appium �[38;5;16m[AppiumDriver@efa7]�[0m Event 'newSessionRequested' logged at 1742755032779 (19:37:12 GMT+0100 (st┼Öedoevropsk├╜ standardn├¡ ─ìas))
Appium �[35m[Appium]�[39m Attempting to find matching driver for automationName 'Windows' and platformName 'Windows'
Appium �[35m[Appium]�[39m The 'windows' driver was installed and matched caps.
Appium �[35m[Appium]�[39m Will require it at C:\Users\user\.appium\node_modules\appium-windows-driver
Appium �[35m[Appium]�[39m Requiring driver at C:\Users\user\.appium\node_modules\appium-windows-driver\build\index.js
Appium �[38;5;16m[AppiumDriver@efa7]�[0m Appium v2.5.4 creating new WindowsDriver (v2.12.23) session
Appium �[38;5;16m[AppiumDriver@efa7]�[0m Checking BaseDriver versions for Appium and WindowsDriver
Appium �[38;5;16m[AppiumDriver@efa7]�[0m Appium's BaseDriver version is 9.7.0
Appium �[38;5;16m[AppiumDriver@efa7]�[0m WindowsDriver's BaseDriver version is 9.7.0
Appium �[38;5;32m[WindowsDriver@83f0]�[0m Creating session with W3C capabilities: {
Appium �[38;5;32m[WindowsDriver@83f0]�[0m   "alwaysMatch": {
Appium �[38;5;32m[WindowsDriver@83f0]�[0m     "platformName": "Windows",
Appium �[38;5;32m[WindowsDriver@83f0]�[0m     "appium:app": "X:\\maui\\artifacts\\bin\\Controls.TestCases.WinUI.Tests\\Debug\\net9.0\\..\\..\\..\\Controls.TestCases.HostApp\\Debug\\net9.0-windows10.0.20348.0\\win10-x64\\Controls.TestCases.HostApp.exe",
Appium �[38;5;32m[WindowsDriver@83f0]�[0m     "appium:automationName": "Windows",
Appium �[38;5;32m[WindowsDriver@83f0]�[0m     "appium:deviceName": "WindowsPC",
Appium �[38;5;32m[WindowsDriver@83f0]�[0m     "appium:reportDirectory": "reports",
Appium �[38;5;32m[WindowsDriver@83f0]�[0m     "appium:reportFormat": "xml",
Appium �[38;5;32m[WindowsDriver@83f0]�[0m     "appium:newCommandTimeout": 3000
Appium �[38;5;32m[WindowsDriver@83f0]�[0m   },
Appium �[38;5;32m[WindowsDriver@83f0]�[0m   "firstMatch": [
Appium �[38;5;32m[WindowsDriver@83f0]�[0m     {}
Appium �[38;5;32m[WindowsDriver@83f0]�[0m   ]
Appium �[38;5;32m[WindowsDriver@83f0]�[0m }
Appium �[38;5;32m[WindowsDriver@83f0]�[0m The following provided capabilities were not recognized by this driver:
Appium �[38;5;32m[WindowsDriver@83f0]�[0m   deviceName
Appium �[38;5;32m[WindowsDriver@83f0]�[0m   reportDirectory
Appium �[38;5;32m[WindowsDriver@83f0]�[0m   reportFormat
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Session created with session id: 015362d9-d51e-4251-8f2d-a9cca99ca925
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Spawning 'C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe' with args: ["4724/wd/hub"]
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Matched '/status' to command name 'getStatus'
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Proxying [GET /status] to [GET http://127.0.0.1:4724/wd/hub/status] with no body
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m connect ECONNREFUSED 127.0.0.1:4724
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Windows Application Driver listening for requests at: http://127.0.0.1:4724/wd/hub
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Press ENTER to exit.
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Matched '/status' to command name 'getStatus'
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Proxying [GET /status] to [GET http://127.0.0.1:4724/wd/hub/status] with no body
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m ==========================================
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m GET /wd/hub/status HTTP/1.1
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Accept: application/json, */*
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Accept-Encoding: gzip, compress, deflate, br
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Connection: keep-alive
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Content-Type: application/json; charset=utf-8
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Host: 127.0.0.1:4724
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m User-Agent: appium
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Got response with status 200: {"build":{"revision":"2003","time":"Wed Aug 26 07:56:06 2020","version":"1.2.2009"},"os":{"arch":"amd64","name":"windows","version":"10.0.26100"}}
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Starting WinAppDriver session. Will timeout in '20000' ms.
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Matched '/session' to command name 'createSession'
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Proxying [POST /session] to [POST http://127.0.0.1:4724/wd/hub/session] with body: {"desiredCapabilities":{"platformName":"Windows","app":"X:\\maui\\artifacts\\bin\\Controls.TestCases.WinUI.Tests\\Debug\\net9.0\\..\\..\\..\\Controls.TestCases.HostApp\\Debug\\net9.0-windows10.0.20348.0\\win10-x64\\Controls.TestCases.HostApp.exe","automationName":"Windows","deviceName":"WindowsPC","reportDirectory":"reports","reportFormat":"xml","newCommandTimeout":3000}}
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m HTTP/1.1 200 OK
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Content-Length: 146
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Content-Type: application/json
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m 
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m {"build":{"revision":"2003","time":"Wed Aug 26 07:56:06 2020","version":"1.2.2009"},"os":{"arch":"amd64","name":"windows","version":"10.0.26100"}}
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m ==========================================
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m POST /wd/hub/session HTTP/1.1
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Accept: application/json, */*
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Accept-Encoding: gzip, compress, deflate, br
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Connection: keep-alive
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Content-Length: 405
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Content-Type: application/json; charset=utf-8
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Host: 127.0.0.1:4724
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m User-Agent: appium
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m 
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m {"desiredCapabilities":{"platformName":"Windows","app":"X:\\maui\\artifacts\\bin\\Controls.TestCases.WinUI.Tests\\Debug\\net9.0\\..\\..\\..\\Controls.TestCases.HostApp\\Debug\\net9.0-windows10.0.20348.0\\win10-x64\\Controls.TestCases.HostApp.exe","automationName":"Windows","deviceName":"WindowsPC","reportDirectory":"reports","reportFormat":"xml","newCommandTimeout":3000}}
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m HTTP/1.1 500 Internal Error
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Content-Length: 101
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Content-Type: application/json
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m 
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m {"status":13,"value":{"error":"unknown error","message":"The system cannot find the file specified"}}
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Got response with status 500: {"status":13,"value":{"error":"unknown error","message":"The system cannot find the file specified"}}
Appium �[38;5;48m[W3C]�[0m Matched W3C error code 'unknown error' to UnknownError
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Could not start WinAppDriver session error = 'An unknown server-side error occurred while processing the command. Original error: The system cannot find the file specified', attempt = 1
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Matched '/session' to command name 'createSession'
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Proxying [POST /session] to [POST http://127.0.0.1:4724/wd/hub/session] with body: {"desiredCapabilities":{"platformName":"Windows","app":"X:\\maui\\artifacts\\bin\\Controls.TestCases.WinUI.Tests\\Debug\\net9.0\\..\\..\\..\\Controls.TestCases.HostApp\\Debug\\net9.0-windows10.0.20348.0\\win10-x64\\Controls.TestCases.HostApp.exe","automationName":"Windows","deviceName":"WindowsPC","reportDirectory":"reports","reportFormat":"xml","newCommandTimeout":3000}}
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m ==========================================
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m POST /wd/hub/session HTTP/1.1
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Accept: application/json, */*
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Accept-Encoding: gzip, compress, deflate, br
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Connection: keep-alive
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Content-Length: 405
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Content-Type: application/json; charset=utf-8
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Host: 127.0.0.1:4724
Appium 
Appium 
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m User-Agent: appium
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Got response with status 500: {"status":13,"value":{"error":"unknown error","message":"The system cannot find the file specified"}}
Appium �[38;5;48m[W3C]�[0m Matched W3C error code 'unknown error' to UnknownError
Appium �[38;5;32m[WindowsDriver@83f0 (015362d9)]�[0m Could not start WinAppDriver session error = 'An unknown server-side error occurred while processing the command. Original error: The system cannot find the file specified', attempt = 2

(I rebased on the latest main commit -- i.e. 173ca2b)

{
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));
}
}
Loading
Loading