Skip to content

Commit 4d6cd42

Browse files
authored
Merge pull request #12347 from flexxxxer/master
Fix not working hotkey on a custom control. closes #12323
2 parents b702e24 + a5c7b1d commit 4d6cd42

File tree

2 files changed

+124
-2
lines changed

2 files changed

+124
-2
lines changed

src/Avalonia.Controls/HotkeyManager.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,10 @@ static HotKeyManager()
149149
return;
150150

151151
var control = args.Sender as Control;
152-
if (control is not IClickableControl)
152+
if (control is not IClickableControl and not ICommandSource)
153153
{
154154
Logging.Logger.TryGet(Logging.LogEventLevel.Warning, Logging.LogArea.Control)?.Log(control,
155-
$"The element {args.Sender.GetType().Name} does not implement IClickableControl and does not support binding a HotKey ({args.NewValue}).");
155+
$"The element {args.Sender.GetType().Name} does not implement IClickableControl nor ICommandSource and does not support binding a HotKey ({args.NewValue}).");
156156
return;
157157
}
158158

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
using System;
2+
using System.Windows.Input;
3+
using Avalonia.Input;
4+
using Avalonia.Input.Raw;
5+
using Avalonia.LogicalTree;
6+
using Avalonia.Platform;
7+
using Avalonia.UnitTests;
8+
using Moq;
9+
using Xunit;
10+
11+
namespace Avalonia.Controls.UnitTests
12+
{
13+
internal class HotKeyedTextBox : TextBox, ICommandSource
14+
{
15+
private class DelegateCommand : ICommand
16+
{
17+
private readonly Action _action;
18+
public DelegateCommand(Action action) => _action = action;
19+
public event EventHandler CanExecuteChanged { add { } remove { } }
20+
public bool CanExecute(object parameter) => true;
21+
public void Execute(object parameter) => _action();
22+
}
23+
24+
public static readonly StyledProperty<KeyGesture> HotKeyProperty =
25+
HotKeyManager.HotKeyProperty.AddOwner<HotKeyedTextBox>();
26+
27+
private KeyGesture _hotkey;
28+
29+
public KeyGesture HotKey
30+
{
31+
get => GetValue(HotKeyProperty);
32+
set => SetValue(HotKeyProperty, value);
33+
}
34+
35+
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
36+
{
37+
if (_hotkey != null)
38+
{
39+
this.SetValue(HotKeyProperty, _hotkey);
40+
}
41+
42+
base.OnAttachedToLogicalTree(e);
43+
}
44+
45+
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
46+
{
47+
if (this.HotKey != null)
48+
{
49+
_hotkey = this.HotKey;
50+
this.SetValue(HotKeyProperty, null);
51+
}
52+
53+
base.OnDetachedFromLogicalTree(e);
54+
}
55+
56+
public void CanExecuteChanged(object sender, EventArgs e)
57+
{
58+
}
59+
60+
protected override Type StyleKeyOverride => typeof(TextBox);
61+
62+
public ICommand Command => _command;
63+
64+
public object CommandParameter => null;
65+
66+
private readonly DelegateCommand _command;
67+
68+
public HotKeyedTextBox()
69+
{
70+
_command = new DelegateCommand(() => Focus());
71+
}
72+
}
73+
74+
public class HotKeyedControlsTests
75+
{
76+
private static Window PreparedWindow(object content = null)
77+
{
78+
var platform = AvaloniaLocator.Current.GetRequiredService<IWindowingPlatform>();
79+
var windowImpl = Mock.Get(platform.CreateWindow());
80+
windowImpl.Setup(x => x.Compositor).Returns(RendererMocks.CreateDummyCompositor());
81+
var w = new Window(windowImpl.Object) { Content = content };
82+
w.ApplyTemplate();
83+
return w;
84+
}
85+
86+
private static IDisposable CreateServicesWithFocus()
87+
{
88+
return UnitTestApplication.Start(
89+
TestServices.StyledWindow.With(
90+
windowingPlatform: new MockWindowingPlatform(
91+
null,
92+
window => MockWindowingPlatform.CreatePopupMock(window).Object),
93+
focusManager: new FocusManager(),
94+
keyboardDevice: () => new KeyboardDevice()));
95+
}
96+
97+
[Fact]
98+
public void HotKeyedTextBox_Focus_Performed_On_Hotkey()
99+
{
100+
using var _ = CreateServicesWithFocus();
101+
102+
var keyboardDevice = new KeyboardDevice();
103+
var hotKeyedTextBox = new HotKeyedTextBox { HotKey = new KeyGesture(Key.F, KeyModifiers.Control) };
104+
var root = PreparedWindow();
105+
root.Content = hotKeyedTextBox;
106+
root.Show();
107+
108+
Assert.False(hotKeyedTextBox.IsFocused);
109+
110+
keyboardDevice.ProcessRawEvent(
111+
new RawKeyEventArgs(
112+
keyboardDevice,
113+
0,
114+
root,
115+
RawKeyEventType.KeyDown,
116+
Key.F,
117+
RawInputModifiers.Control));
118+
119+
Assert.True(hotKeyedTextBox.IsFocused);
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)