Browse Source

Fix Button.IsPressed not becoming false when a Button without keyboard focus is disabled (#21168)

Removed misleading AI slop test
release/latest
Tom Edwards 4 months ago
committed by Julien Lebosquain
parent
commit
b2a3cc880d
  1. 7
      src/Avalonia.Controls/Button.cs
  2. 83
      tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs
  3. 63
      tests/Avalonia.Controls.UnitTests/ButtonTests.cs

7
src/Avalonia.Controls/Button.cs

@ -552,6 +552,13 @@ namespace Avalonia.Controls
(newFlyout as PopupFlyoutBase)?.SetDefaultPlacementTarget(this);
UpdatePseudoClasses();
}
else if (change.Property == IsEffectivelyEnabledProperty)
{
if (!change.GetNewValue<bool>())
{
IsPressed = false;
}
}
}
protected override AutomationPeer OnCreateAutomationPeer() => new ButtonAutomationPeer(this);

83
tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs

@ -1,8 +1,6 @@
using System;
using System.Windows.Input;
using Avalonia.Base.UnitTests.Input;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.Rendering;
@ -244,47 +242,6 @@ namespace Avalonia.Input.UnitTests
Assert.Equal(3, doubleTappedExecutedTimes);
}
[Fact]
public void ToggleButton_Does_Not_Toggle_When_Command_Becomes_Disabled_Between_TouchBegin_And_TouchEnd()
{
using var app = UnitTestApp(new TimeSpan(200));
var renderer = new Mock<IHitTester>();
var impl = CreateTopLevelImplMock();
var command = new TestCommand(true);
var target = new ToggleButton
{
Width = 100,
Height = 100,
Command = command,
};
var root = CreateInputRoot(impl.Object, target, renderer.Object);
var device = new TouchDevice();
var touchBegin = new RawPointerEventArgs(device, 0, root.PresentationSource, RawPointerEventType.TouchBegin, new Point(50, 50), RawInputModifiers.None)
{
RawPointerId = 1
};
var touchEnd = new RawPointerEventArgs(device, 1, root.PresentationSource, RawPointerEventType.TouchEnd, new Point(50, 50), RawInputModifiers.None)
{
RawPointerId = 1
};
SetHit(renderer, target);
impl.Object.Input!(touchBegin);
Assert.True(target.IsPressed);
Assert.False(target.IsChecked ?? false);
command.IsEnabled = false;
Assert.False(target.IsEffectivelyEnabled);
impl.Object.Input!(touchEnd);
Assert.False(target.IsChecked ?? false);
}
private IDisposable UnitTestApp(TimeSpan doubleClickTime = new TimeSpan())
{
var unitTestApp = UnitTestApplication.Start(
@ -297,7 +254,7 @@ namespace Avalonia.Input.UnitTests
.Bind<IPlatformSettings>().ToConstant(iSettingsMock.Object);
return unitTestApp;
}
private static void SendXTouchContactsWithIds(IInputManager inputManager, TouchDevice device, IInputRoot root, RawPointerEventType type, params long[] touchPointIds)
{
for (int i = 0; i < touchPointIds.Length; i++)
@ -344,44 +301,6 @@ namespace Avalonia.Input.UnitTests
});
}
private sealed class TestCommand : ICommand
{
private bool _enabled;
private EventHandler? _canExecuteChanged;
public TestCommand(bool enabled)
{
_enabled = enabled;
}
public bool IsEnabled
{
get => _enabled;
set
{
if (_enabled == value)
{
return;
}
_enabled = value;
_canExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
public event EventHandler? CanExecuteChanged
{
add => _canExecuteChanged += value;
remove => _canExecuteChanged -= value;
}
public bool CanExecute(object? parameter) => _enabled;
public void Execute(object? parameter)
{
}
}
private class TestTopLevel(ITopLevelImpl impl) : TopLevel(impl)
{

63
tests/Avalonia.Controls.UnitTests/ButtonTests.cs

@ -239,7 +239,7 @@ namespace Avalonia.Controls.UnitTests
bool clicked = false;
Dispatcher.UIThread.RunJobs(null, TestContext.Current.CancellationToken);
target.Click += (s, e) => clicked = true;
RaisePointerEntered(target);
@ -295,9 +295,9 @@ namespace Avalonia.Controls.UnitTests
var raised = 0;
target.Click += (s, e) => ++raised;
target.RaiseEvent(new AccessKeyEventArgs("b", false));
Assert.Equal(1, raised);
}
@ -357,7 +357,7 @@ namespace Avalonia.Controls.UnitTests
RaiseAccessKey(root, accessKey, accessKeySymbol);
Assert.Equal(1, raised);
static FuncControlTemplate<TestTopLevel> CreateTemplate()
{
return new FuncControlTemplate<TestTopLevel>((x, scope) =>
@ -458,7 +458,7 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(2, raised);
}
}
[Fact]
public void Button_IsDefault_Should_Not_Work_When_Button_Is_Not_Effectively_Visible()
{
@ -472,7 +472,7 @@ namespace Avalonia.Controls.UnitTests
window.Show();
target.Click += (s, e) => ++raised;
target.IsDefault = true;
panel.IsVisible = false;
window.RaiseEvent(CreateKeyDownEvent(Key.Enter));
@ -513,7 +513,7 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(2, raised);
}
}
[Fact]
public void Button_IsCancel_Should_Not_Work_When_Button_Is_Not_Effectively_Visible()
{
@ -527,7 +527,7 @@ namespace Avalonia.Controls.UnitTests
window.Show();
target.Click += (s, e) => ++raised;
target.IsCancel = true;
panel.IsVisible = false;
window.RaiseEvent(CreateKeyDownEvent(Key.Escape));
@ -564,7 +564,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
void Should_Not_Fire_Click_Event_On_Space_Key_When_It_Is_Not_Focus()
public void Should_Not_Fire_Click_Event_On_Space_Key_When_It_Is_Not_Focus()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
@ -586,6 +586,51 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void Button_Unpressed_When_Disabled()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var target = new Button()
{
// Disabling a control implies focus loss, and focus loss
// has its own code path to un-press the button. So we have
// to avoid hitting that path to get an accurate result.
Focusable = false,
};
var window = new Window { Content = target };
window.Show();
RaisePointerPressed(target, 1, MouseButton.Left, new Point(50, 50));
Assert.True(target.IsPressed);
Assert.False(target.IsFocused);
target.IsEnabled = false;
Assert.False(target.IsPressed);
}
}
[Fact]
public void Button_Unpressed_When_Focus_Lost()
{
using (UnitTestApplication.Start(TestServices.FocusableWindow))
{
var target = new Button();
var other = new Button();
var window = new Window { Content = new StackPanel { Children = { target, other } } };
window.Show();
RaisePointerPressed(target, 1, MouseButton.Left, new Point(50, 50));
Assert.True(target.IsPressed);
Assert.True(target.IsFocused);
Assert.True(other.Focus());
Assert.False(target.IsPressed);
}
}
private KeyEventArgs CreateKeyDownEvent(Key key, Interactive? source = null)
{
return new KeyEventArgs { RoutedEvent = InputElement.KeyDownEvent, Key = key, Source = source };

Loading…
Cancel
Save