Fixed overlay popups not automatically closing (#16564)

* Fixed overlay popups not automatically closing

* Fix overlay tooltip tests not actually generating overlay tooltips
Verify popup type whenever we verify that the popup is open

* Fixed overlay tooltips not being attached to the visual tree in tests
This commit is contained in:
Tom Edwards 2024-08-04 06:24:47 +02:00 коммит произвёл GitHub
Родитель e0e99b31a8
Коммит d1cdb29ba0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
2 изменённых файлов: 78 добавлений и 21 удалений

Просмотреть файл

@ -78,7 +78,7 @@ namespace Avalonia.Controls
{
var currentToolTip = _tipControl?.GetValue(ToolTip.ToolTipProperty);
if (root == currentToolTip?.VisualRoot)
if (root == currentToolTip?.PopupHost?.HostedVisualTreeRoot)
{
// Don't update while the pointer is over a tooltip
return;

Просмотреть файл

@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.Reactive;
using System.Runtime.CompilerServices;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Threading;
using Avalonia.UnitTests;
@ -15,18 +18,65 @@ namespace Avalonia.Controls.UnitTests
public class ToolTipTests_Popup : ToolTipTests
{
protected override TestServices ConfigureServices(TestServices baseServices) => baseServices;
protected override void SetupWindowMock(Mock<IWindowImpl> windowImpl) { }
protected override void VerifyToolTipType(Control control)
{
var toolTip = control.GetValue(ToolTip.ToolTipProperty);
Assert.IsType<PopupRoot>(toolTip.PopupHost);
Assert.Same(toolTip.VisualRoot, toolTip.PopupHost);
}
}
public class ToolTipTests_Overlay : ToolTipTests
public class ToolTipTests_Overlay : ToolTipTests, IDisposable
{
private readonly IDisposable _toolTipOpenSubscription;
public ToolTipTests_Overlay()
{
_toolTipOpenSubscription = ToolTip.IsOpenProperty.Changed.Subscribe(new AnonymousObserver<AvaloniaPropertyChangedEventArgs<bool>>(e =>
{
if (e.Sender is Visual { VisualRoot: {} root } visual)
OverlayLayer.GetOverlayLayer(visual).Measure(root.ClientSize);
}));
}
public void Dispose()
{
_toolTipOpenSubscription.Dispose();
}
protected override TestServices ConfigureServices(TestServices baseServices) =>
baseServices.With(windowingPlatform: new MockWindowingPlatform(popupImpl: window => null));
protected override void SetupWindowMock(Mock<IWindowImpl> windowImpl)
{
windowImpl.Setup(x => x.CreatePopup()).Returns(default(IPopupImpl));
}
protected override void VerifyToolTipType(Control control)
{
var toolTip = control.GetValue(ToolTip.ToolTipProperty);
Assert.IsType<OverlayPopupHost>(toolTip.PopupHost);
Assert.Same(toolTip.VisualRoot, control.VisualRoot);
}
}
public abstract class ToolTipTests
{
protected abstract TestServices ConfigureServices(TestServices baseServices);
protected abstract void SetupWindowMock(Mock<IWindowImpl> windowImpl);
protected abstract void VerifyToolTipType(Control control);
private void AssertToolTipOpen(Control control)
{
Assert.True(ToolTip.GetIsOpen(control));
VerifyToolTipType(control);
}
private static readonly MouseDevice s_mouseDevice = new(new Pointer(0, PointerType.Mouse, true));
[Fact]
@ -46,7 +96,7 @@ namespace Avalonia.Controls.UnitTests
SetupWindowAndActivateToolTip(panel, target);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
panel.Children.Remove(target);
@ -74,7 +124,7 @@ namespace Avalonia.Controls.UnitTests
mouseEnter(target);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
panel.Children.Remove(target);
@ -95,7 +145,7 @@ namespace Avalonia.Controls.UnitTests
SetupWindowAndActivateToolTip(target);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
}
}
@ -112,7 +162,7 @@ namespace Avalonia.Controls.UnitTests
SetupWindowAndActivateToolTip(target);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
Assert.Equal("Tip", target.GetValue(ToolTip.ToolTipProperty).Content);
ToolTip.SetTip(target, "Tip1");
@ -139,7 +189,7 @@ namespace Avalonia.Controls.UnitTests
timer.ForceFire();
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
}
}
@ -188,6 +238,7 @@ namespace Avalonia.Controls.UnitTests
ToolTip.SetIsOpen(decorator, true);
Assert.Equal(new[] { ":open" }, toolTip.Classes);
VerifyToolTipType(decorator);
}
}
@ -197,8 +248,11 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(ConfigureServices(TestServices.StyledWindow)))
{
var toolTip = new ToolTip();
var window = new Window();
var windowImpl = MockWindowingPlatform.CreateWindowMock();
SetupWindowMock(windowImpl);
var window = new Window(windowImpl.Object);
var decorator = new Decorator()
{
[ToolTip.TipProperty] = toolTip
@ -211,6 +265,7 @@ namespace Avalonia.Controls.UnitTests
window.Presenter.ApplyTemplate();
ToolTip.SetIsOpen(decorator, true);
AssertToolTipOpen(decorator);
ToolTip.SetIsOpen(decorator, false);
Assert.Empty(toolTip.Classes);
@ -230,7 +285,7 @@ namespace Avalonia.Controls.UnitTests
SetupWindowAndActivateToolTip(target);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
target[ToolTip.TipProperty] = null;
@ -253,13 +308,13 @@ namespace Avalonia.Controls.UnitTests
mouseEnter(target);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
var tooltip = Assert.IsType<ToolTip>(target.GetValue(ToolTip.ToolTipProperty));
mouseEnter(tooltip);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
}
}
@ -277,16 +332,16 @@ namespace Avalonia.Controls.UnitTests
var mouseEnter = SetupWindowAndGetMouseEnterAction(target);
mouseEnter(target);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
var tooltip = Assert.IsType<ToolTip>(target.GetValue(ToolTip.ToolTipProperty));
mouseEnter(tooltip);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
mouseEnter(target);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
}
}
@ -311,12 +366,12 @@ namespace Avalonia.Controls.UnitTests
var mouseEnter = SetupWindowAndGetMouseEnterAction(panel);
mouseEnter(target);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
var tooltip = Assert.IsType<ToolTip>(target.GetValue(ToolTip.ToolTipProperty));
mouseEnter(tooltip);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
mouseEnter(other);
@ -352,7 +407,7 @@ namespace Avalonia.Controls.UnitTests
Assert.False(ToolTip.GetIsOpen(other)); // long delay
mouseEnter(target);
Assert.True(ToolTip.GetIsOpen(target)); // no delay
AssertToolTipOpen(target); // no delay
mouseEnter(other);
Assert.True(ToolTip.GetIsOpen(other)); // delay skipped, a tooltip was already open
@ -360,7 +415,7 @@ namespace Avalonia.Controls.UnitTests
// Now disable the between-show system
mouseEnter(target);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
ToolTip.SetBetweenShowDelay(other, -1);
@ -389,7 +444,7 @@ namespace Avalonia.Controls.UnitTests
SetupWindowAndActivateToolTip(target);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
target[ToolTip.TipProperty] = null;
@ -442,7 +497,7 @@ namespace Avalonia.Controls.UnitTests
SetupWindowAndActivateToolTip(target);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
target[ToolTip.TipProperty] = null;
@ -463,7 +518,7 @@ namespace Avalonia.Controls.UnitTests
var mouseEnter = SetupWindowAndGetMouseEnterAction(target);
mouseEnter(target);
Assert.True(ToolTip.GetIsOpen(target));
AssertToolTipOpen(target);
var topLevel = TopLevel.GetTopLevel(target);
topLevel.PlatformImpl.Input(new RawPointerEventArgs(s_mouseDevice, (ulong)DateTime.Now.Ticks, topLevel,
@ -476,6 +531,8 @@ namespace Avalonia.Controls.UnitTests
private Action<Control> SetupWindowAndGetMouseEnterAction(Control windowContent, [CallerMemberName] string testName = null)
{
var windowImpl = MockWindowingPlatform.CreateWindowMock();
SetupWindowMock(windowImpl);
var hitTesterMock = new Mock<IHitTester>();
var window = new Window(windowImpl.Object)