committed by
GitHub
28 changed files with 594 additions and 189 deletions
@ -0,0 +1,33 @@ |
|||
using System; |
|||
using Avalonia.Automation.Peers; |
|||
|
|||
namespace Avalonia.Automation.Provider |
|||
{ |
|||
/// <summary>
|
|||
/// Exposure methods and properties to support UI Automation client access to the root of an
|
|||
/// automation tree hosted by another UI framework.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This interface is implemented by the <see cref="AutomationPeer"/> class, and can be used
|
|||
/// to embed an automation tree from a 3rd party UI framework that wishes to use Avalonia's
|
|||
/// automation support.
|
|||
/// </remarks>
|
|||
public interface IEmbeddedRootProvider |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the currently focused element.
|
|||
/// </summary>
|
|||
AutomationPeer? GetFocus(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the element at the specified point, expressed in top-level coordinates.
|
|||
/// </summary>
|
|||
/// <param name="p">The point.</param>
|
|||
AutomationPeer? GetPeerFromPoint(Point p); |
|||
|
|||
/// <summary>
|
|||
/// Raised by the automation peer when the focus changes.
|
|||
/// </summary>
|
|||
event EventHandler? FocusChanged; |
|||
} |
|||
} |
|||
@ -0,0 +1,122 @@ |
|||
using System; |
|||
using System.Windows.Input; |
|||
using Avalonia.Input; |
|||
using Avalonia.Input.Raw; |
|||
using Avalonia.LogicalTree; |
|||
using Avalonia.Platform; |
|||
using Avalonia.UnitTests; |
|||
using Moq; |
|||
using Xunit; |
|||
|
|||
namespace Avalonia.Controls.UnitTests |
|||
{ |
|||
internal class HotKeyedTextBox : TextBox, ICommandSource |
|||
{ |
|||
private class DelegateCommand : ICommand |
|||
{ |
|||
private readonly Action _action; |
|||
public DelegateCommand(Action action) => _action = action; |
|||
public event EventHandler CanExecuteChanged { add { } remove { } } |
|||
public bool CanExecute(object parameter) => true; |
|||
public void Execute(object parameter) => _action(); |
|||
} |
|||
|
|||
public static readonly StyledProperty<KeyGesture> HotKeyProperty = |
|||
HotKeyManager.HotKeyProperty.AddOwner<HotKeyedTextBox>(); |
|||
|
|||
private KeyGesture _hotkey; |
|||
|
|||
public KeyGesture HotKey |
|||
{ |
|||
get => GetValue(HotKeyProperty); |
|||
set => SetValue(HotKeyProperty, value); |
|||
} |
|||
|
|||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) |
|||
{ |
|||
if (_hotkey != null) |
|||
{ |
|||
this.SetValue(HotKeyProperty, _hotkey); |
|||
} |
|||
|
|||
base.OnAttachedToLogicalTree(e); |
|||
} |
|||
|
|||
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) |
|||
{ |
|||
if (this.HotKey != null) |
|||
{ |
|||
_hotkey = this.HotKey; |
|||
this.SetValue(HotKeyProperty, null); |
|||
} |
|||
|
|||
base.OnDetachedFromLogicalTree(e); |
|||
} |
|||
|
|||
public void CanExecuteChanged(object sender, EventArgs e) |
|||
{ |
|||
} |
|||
|
|||
protected override Type StyleKeyOverride => typeof(TextBox); |
|||
|
|||
public ICommand Command => _command; |
|||
|
|||
public object CommandParameter => null; |
|||
|
|||
private readonly DelegateCommand _command; |
|||
|
|||
public HotKeyedTextBox() |
|||
{ |
|||
_command = new DelegateCommand(() => Focus()); |
|||
} |
|||
} |
|||
|
|||
public class HotKeyedControlsTests |
|||
{ |
|||
private static Window PreparedWindow(object content = null) |
|||
{ |
|||
var platform = AvaloniaLocator.Current.GetRequiredService<IWindowingPlatform>(); |
|||
var windowImpl = Mock.Get(platform.CreateWindow()); |
|||
windowImpl.Setup(x => x.Compositor).Returns(RendererMocks.CreateDummyCompositor()); |
|||
var w = new Window(windowImpl.Object) { Content = content }; |
|||
w.ApplyTemplate(); |
|||
return w; |
|||
} |
|||
|
|||
private static IDisposable CreateServicesWithFocus() |
|||
{ |
|||
return UnitTestApplication.Start( |
|||
TestServices.StyledWindow.With( |
|||
windowingPlatform: new MockWindowingPlatform( |
|||
null, |
|||
window => MockWindowingPlatform.CreatePopupMock(window).Object), |
|||
focusManager: new FocusManager(), |
|||
keyboardDevice: () => new KeyboardDevice())); |
|||
} |
|||
|
|||
[Fact] |
|||
public void HotKeyedTextBox_Focus_Performed_On_Hotkey() |
|||
{ |
|||
using var _ = CreateServicesWithFocus(); |
|||
|
|||
var keyboardDevice = new KeyboardDevice(); |
|||
var hotKeyedTextBox = new HotKeyedTextBox { HotKey = new KeyGesture(Key.F, KeyModifiers.Control) }; |
|||
var root = PreparedWindow(); |
|||
root.Content = hotKeyedTextBox; |
|||
root.Show(); |
|||
|
|||
Assert.False(hotKeyedTextBox.IsFocused); |
|||
|
|||
keyboardDevice.ProcessRawEvent( |
|||
new RawKeyEventArgs( |
|||
keyboardDevice, |
|||
0, |
|||
root, |
|||
RawKeyEventType.KeyDown, |
|||
Key.F, |
|||
RawInputModifiers.Control)); |
|||
|
|||
Assert.True(hotKeyedTextBox.IsFocused); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
using System; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.ApplicationLifetimes; |
|||
using Avalonia.Reactive; |
|||
using Avalonia.Threading; |
|||
using Avalonia.UnitTests; |
|||
using JetBrains.dotMemoryUnit; |
|||
using Xunit; |
|||
using Xunit.Abstractions; |
|||
|
|||
namespace Avalonia.LeakTests; |
|||
|
|||
internal class ViewModelForDisposingTest |
|||
{ |
|||
~ViewModelForDisposingTest() { ; } |
|||
} |
|||
|
|||
[DotMemoryUnit(FailIfRunWithoutSupport = false)] |
|||
public class DataContextTests |
|||
{ |
|||
public DataContextTests(ITestOutputHelper atr) |
|||
{ |
|||
DotMemoryUnitTestOutput.SetOutputMethod(atr.WriteLine); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Window_DataContext_Disposed_After_Window_Close_With_Lifetime() |
|||
{ |
|||
static IDisposable Run() |
|||
{ |
|||
var unitTestApp = UnitTestApplication.Start(TestServices.StyledWindow); |
|||
var lifetime = new ClassicDesktopStyleApplicationLifetime(); |
|||
lifetime.ShutdownMode = ShutdownMode.OnExplicitShutdown; |
|||
var window = new Window { DataContext = new ViewModelForDisposingTest() }; |
|||
window.Show(); |
|||
window.Close(); |
|||
|
|||
return Disposable.Create(lifetime, lt => lt.Shutdown()) |
|||
.DisposeWith(new CompositeDisposable(lifetime, unitTestApp)); |
|||
} |
|||
|
|||
using var _ = Run(); |
|||
// Process all Loaded events to free control reference(s)
|
|||
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); |
|||
GC.Collect(); |
|||
|
|||
dotMemory.Check(m => Assert.Equal(0, |
|||
m.GetObjects(o => o.Type.Is<ViewModelForDisposingTest>()).ObjectsCount)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Window_DataContext_Disposed_After_Window_Close_Without_Lifetime() |
|||
{ |
|||
static void Run() |
|||
{ |
|||
using var _ = UnitTestApplication.Start(TestServices.StyledWindow); |
|||
var window = new Window { DataContext = new ViewModelForDisposingTest() }; |
|||
window.Show(); |
|||
window.Close(); |
|||
} |
|||
|
|||
Run(); |
|||
// Process all Loaded events to free control reference(s)
|
|||
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); |
|||
GC.Collect(); |
|||
|
|||
dotMemory.Check(m => Assert.Equal(0, |
|||
m.GetObjects(o => o.Type.Is<ViewModelForDisposingTest>()).ObjectsCount)); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue