Browse Source

[Win] Handle mouse movement in non-client areas of window. (#19922) (#20076)

* V1: Handle MouseLeave Event to handle caption buttons

* Update methods
pull/20084/head
Tim Miller 3 months ago
committed by GitHub
parent
commit
5273d9bc93
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 7
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  2. 69
      src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs
  3. 1
      src/Windows/Avalonia.Win32/WindowImpl.cs

7
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -2298,6 +2298,13 @@ namespace Avalonia.Win32.Interop
public int dwHoverTime;
}
// TrackMouseEvent flags
public const uint TME_HOVER = 0x00000001;
public const uint TME_LEAVE = 0x00000002;
public const uint TME_NONCLIENT = 0x00000010;
public const uint TME_QUERY = 0x40000000;
public const uint TME_CANCEL = 0x80000000;
[Flags]
public enum WindowPlacementFlags : uint
{

69
src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs

@ -1,4 +1,5 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Raw;
@ -141,24 +142,64 @@ namespace Avalonia.Win32
case WindowsMessage.WM_NCMOUSEMOVE when !IsMouseInPointerEnabled:
case WindowsMessage.WM_NCLBUTTONDOWN when !IsMouseInPointerEnabled:
case WindowsMessage.WM_NCLBUTTONUP when !IsMouseInPointerEnabled:
if (lRet == IntPtr.Zero
&& ShouldRedirectNonClientInput(hWnd, wParam, lParam))
if (lRet == IntPtr.Zero)
{
e = new RawPointerEventArgs(
_mouseDevice,
unchecked((uint)GetMessageTime()),
Owner,
(WindowsMessage)msg switch
var shouldRedirect = ShouldRedirectNonClientInput(hWnd, wParam, lParam);
if (shouldRedirect)
{
// Track non-client mouse to receive WM_NCMOUSELEAVE
if (!_trackingNonClientMouse)
{
WindowsMessage.WM_NCMOUSEMOVE => RawPointerEventType.Move,
WindowsMessage.WM_NCLBUTTONDOWN => RawPointerEventType.LeftButtonDown,
WindowsMessage.WM_NCLBUTTONUP => RawPointerEventType.LeftButtonUp,
_ => throw new ArgumentOutOfRangeException(nameof(msg), msg, null)
},
PointToClient(PointFromLParam(lParam)),
RawInputModifiers.None);
var tm = new TRACKMOUSEEVENT
{
cbSize = Marshal.SizeOf<TRACKMOUSEEVENT>(),
dwFlags = TME_LEAVE | TME_NONCLIENT,
hwndTrack = _hwnd,
dwHoverTime = 0,
};
TrackMouseEvent(ref tm);
_trackingNonClientMouse = true;
}
e = new RawPointerEventArgs(
_mouseDevice,
unchecked((uint)GetMessageTime()),
Owner,
(WindowsMessage)msg switch
{
WindowsMessage.WM_NCMOUSEMOVE => RawPointerEventType.Move,
WindowsMessage.WM_NCLBUTTONDOWN => RawPointerEventType.LeftButtonDown,
WindowsMessage.WM_NCLBUTTONUP => RawPointerEventType.LeftButtonUp,
_ => throw new ArgumentOutOfRangeException(nameof(msg), msg, null)
},
PointToClient(PointFromLParam(lParam)),
RawInputModifiers.None);
}
else if (_trackingNonClientMouse && (WindowsMessage)msg == WindowsMessage.WM_NCMOUSEMOVE)
{
// Mouse moved in NC area but not over caption buttons - send leave event
_trackingNonClientMouse = false;
e = new RawPointerEventArgs(
_mouseDevice,
unchecked((uint)GetMessageTime()),
Owner,
RawPointerEventType.LeaveWindow,
new Point(-1, -1),
RawInputModifiers.None);
}
}
break;
case WindowsMessage.WM_NCMOUSELEAVE when !IsMouseInPointerEnabled:
_trackingNonClientMouse = false;
e = new RawPointerEventArgs(
_mouseDevice,
unchecked((uint)GetMessageTime()),
Owner,
RawPointerEventType.LeaveWindow,
new Point(-1, -1),
RawInputModifiers.None);
break;
case WindowsMessage.WM_NCPOINTERUPDATE when _wmPointerEnabled:
case WindowsMessage.WM_NCPOINTERDOWN when _wmPointerEnabled:
case WindowsMessage.WM_NCPOINTERUP when _wmPointerEnabled:

1
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -89,6 +89,7 @@ namespace Avalonia.Win32
private IconImpl? _iconImpl;
private readonly Dictionary<(Icons type, uint dpi), Win32Icon> _iconCache = new();
private bool _trackingMouse;//ToDo - there is something missed. Needs investigation @Steven Kirk
private bool _trackingNonClientMouse;
private bool _topmost;
private double _scaling = 1;
private uint _dpi = 96;

Loading…
Cancel
Save