Avalonia
Avalonia copied to clipboard
Pointer events don't work on Win32 `EmbeddableControlRoot`
Describe the bug
Pointer events don't work on Win32 EmbeddableControlRoot
.
EDIT: Related to #9268.
To Reproduce
Steps to reproduce the behavior:
-
Repro control
Code
public sealed class Embed : NativeControlHost { private sealed class GraphicsRoot : EmbeddableControlRoot { public GraphicsRoot(ITopLevelImpl impl) : base(impl) { AddHandler(PointerPressedEvent, (sender, e) => OnPointerPressed(e), handledEventsToo: true); } protected override void OnPointerPressed(PointerPressedEventArgs e) { Debug.WriteLine("DEBUG"); } } [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr CreateWindowEx( int dwExStyle, uint lpClassName, string? lpWindowName, uint dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam ); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr SetParent( IntPtr hWndChild, IntPtr hWndNewParent ); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr SetWindowLongPtrA( IntPtr hWnd, int nIndex, IntPtr dwNewLong ); private sealed class Win32EmbedWindowImpl : Avalonia.Win32.WindowImpl { private const int GWL_STYLE = -16; private const long WS_CHILD = 0x40000000L; public Win32EmbedWindowImpl(IPlatformHandle parent) { Embed.SetParent(Handle.Handle, parent.Handle); SetWindowLongPtrA(Handle.Handle, GWL_STYLE, (IntPtr)WS_CHILD); } protected override IntPtr CreateWindowOverride(ushort atom) => CreateWindowEx( 0, atom, null, 0, 0, 0, 1, 1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero ); } private GraphicsRoot _root; protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent) { var embedImpl = new Win32EmbedWindowImpl(parent); _root = new GraphicsRoot(embedImpl); _root.Prepare(); _root.GotFocus += (sender, e) => { var y = 3; }; _root.Background = Brushes.Transparent; _root.PointerPressed += (sender, e) => { Debug.WriteLine("DEBUG"); }; _root.PointerWheelChanged += (sender, e) => { Debug.WriteLine("DEBUG"); }; _root.KeyDown += (sender, e) => { Debug.WriteLine("DEBUG_KEY"); }; return embedImpl.Handle; } protected override void DestroyNativeControlCore(IPlatformHandle control) { _root.Dispose(); _root = null; } }
-
On
MainWindow.xaml
set the content to<v:Embed />
. -
Run the app
-
Press the black area, nothing gets printed on debug output
-
Press some keyboard keys, something gets printed on debug output
Expected behavior
Pointer events should be raised.
Desktop (please complete the following information):
- OS: Windows 11
- Version: 11.0.0-preview2
Check if pointer events are raised from ITopLevelImpl.Input
I added this to the GraphicsRoot
constructor:
impl.Input = e =>
{
Debug.WriteLine("DEBUG_TOPLEVEL_INPUT");
};
And it prints the message for all pointer events.
That probably means that hit-testing has failed to find any controls to trigger pointer events on. Are you sure that you have started the renderer? It doesn't happen automatically.
Tried to add _root.Renderer.Start();
after _root.Prepare();
and it still doesn't work...
Also, I'm guessing the renderer will conflict with the Vulkan rendering, and it may also cause performance issue, so is it possible to have pointer events on EmbeddableControlRoot
without rendering?
UI elements have to be processed by the renderer to have hit-testing information.
Then maybe I should create an implementation of ITopLevelImpl
for Vulkan, with a custom renderer? I guess that would work?
private sealed class Win32EmbedWindowImpl : Avalonia.Win32.WindowImpl
I don't think we want to keep WindowImpl a not internal class.
@jp2masa hit testing is processed by renderer, yes. In case of compositing renderer - https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs#L87
I just tried to replace that class with PlatformManager.CreateEmbeddableWindow()
, and it works, but not sure what's the role of the parent handle now (is parent set automatically later or it doesn't have to be set at all?). Since my last comment, I also created a custom top level impl and renderer, which seem to work fine, so I'll close this.