Skip to content

Commit

Permalink
feat(skiawpf): Added support for Horizontal Mouse Wheel
Browse files Browse the repository at this point in the history
  • Loading branch information
carldebilly committed Jul 31, 2020
1 parent cafc618 commit 108aedd
Showing 1 changed file with 112 additions and 48 deletions.
160 changes: 112 additions & 48 deletions src/Uno.UI.Runtime.Skia.Wpf/WPF/WpfUIElementPointersSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,57 +7,82 @@
using WpfApplication = System.Windows.Application;
using WpfWindow = System.Windows.Window;
using System.Threading;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using Windows.UI.Input;
using MouseDevice = System.Windows.Input.MouseDevice;
using System.Reflection;

namespace Uno.UI.Skia.Platform
{
public class WpfUIElementPointersSupport : ICoreWindowExtension
{
private CoreWindow _owner;
private ICoreWindowEvents _ownerEvents;
private WpfWindow _mainWpfWindow;
private WpfHost _host;
private readonly ICoreWindowEvents _ownerEvents;
private readonly WpfHost _host;

private static int _currentFrameId;
private HwndSource _hwndSource;

// Win32 constants
private const int WM_MOUSEWHEEL = 0x020A;
private const int WM_MOUSEHWHEEL = 0x020E;
private const int WM_DPICHANGED = 0x02E0;

[Flags]
private enum MouseModifierKeys : int
{
MK_LBUTTON = 0x0001,
MK_RBUTTON = 0x0002,
MK_SHIFT = 0x0004,
MK_CONTROL = 0x0008,
MK_MBUTTON = 0x0010,
MK_XBUTTON1 = 0x0020,
MK_XBUTTON2 = 0x0040,
};


public WpfUIElementPointersSupport(object owner)
{
_owner = (CoreWindow)owner;
_ownerEvents = (ICoreWindowEvents)owner;

_mainWpfWindow = WpfApplication.Current.MainWindow;
_host = WpfHost.Current;

_host.MouseEnter += HostOnMouseEnter;
_host.MouseLeave += HostOnMouseLeave;
_host.MouseMove += HostOnMouseMove;
_host.MouseDown += HostOnMouseDown;
_host.MouseUp += HostOnMouseUp;
_host.MouseWheel += HostOnMouseWheel;

// Hook for native events
_host.Loaded += HookNative;

void HookNative(object sender, RoutedEventArgs e)
{
_host.Loaded -= HookNative;

var win = Window.GetWindow(_host);
var fromDependencyObject = PresentationSource.FromDependencyObject(win);
_hwndSource = fromDependencyObject as HwndSource;
_hwndSource?.AddHook(OnWmMessage);
}
}

private static uint GetNextFrameId() => (uint)Interlocked.Increment(ref _currentFrameId);

private static PointerPointProperties BuildPointerProperties(MouseEventArgs args, int wheelDelta = 0)
{
var properties = new PointerPointProperties
private static PointerPointProperties BuildPointerProperties(MouseEventArgs args) =>
new PointerPointProperties
{
IsLeftButtonPressed = args.LeftButton == MouseButtonState.Pressed,
IsMiddleButtonPressed = args.MiddleButton == MouseButtonState.Pressed,
IsRightButtonPressed = args.RightButton == MouseButtonState.Pressed,
IsPrimary = true
IsXButton1Pressed = args.XButton1 == MouseButtonState.Pressed,
IsXButton2Pressed = args.XButton2 == MouseButtonState.Pressed,
IsPrimary = true,
IsInRange = true,
};

if (wheelDelta != 0)
{
properties.MouseWheelDelta = -wheelDelta / 10;
}

return properties;
}

private static PointerDevice GetPointerDevice(MouseEventArgs args)
{
return args.Device switch
Expand All @@ -75,6 +100,7 @@ private void HostOnMouseEnter(object sender, MouseEventArgs args)
{
var position = args.GetPosition(_host);

var properties = BuildPointerProperties(args);
_ownerEvents.RaisePointerEntered(
new PointerEventArgs(
new Windows.UI.Input.PointerPoint(
Expand All @@ -84,8 +110,8 @@ private void HostOnMouseEnter(object sender, MouseEventArgs args)
pointerId: 0,
rawPosition: new Windows.Foundation.Point(position.X, position.Y),
position: new Windows.Foundation.Point(position.X, position.Y),
isInContact: false,
properties: BuildPointerProperties(args)
isInContact: properties.HasPressedButton,
properties: properties
)
)
);
Expand All @@ -102,6 +128,7 @@ private void HostOnMouseLeave(object sender, MouseEventArgs args)
{
var position = args.GetPosition(_host);

var properties = BuildPointerProperties(args);
_ownerEvents.RaisePointerExited(
new PointerEventArgs(
new Windows.UI.Input.PointerPoint(
Expand All @@ -111,8 +138,8 @@ private void HostOnMouseLeave(object sender, MouseEventArgs args)
pointerId: 0,
rawPosition: new Windows.Foundation.Point(position.X, position.Y),
position: new Windows.Foundation.Point(position.X, position.Y),
isInContact: false,
properties: BuildPointerProperties(args)
isInContact: properties.HasPressedButton,
properties: properties
)
)
);
Expand All @@ -129,6 +156,7 @@ private void HostOnMouseMove(object sender, MouseEventArgs args)
{
var position = args.GetPosition(_host);

var properties = BuildPointerProperties(args);
_ownerEvents.RaisePointerMoved(
new PointerEventArgs(
new Windows.UI.Input.PointerPoint(
Expand All @@ -138,8 +166,8 @@ private void HostOnMouseMove(object sender, MouseEventArgs args)
pointerId: 0,
rawPosition: new Windows.Foundation.Point(position.X, position.Y),
position: new Windows.Foundation.Point(position.X, position.Y),
isInContact: false,
properties: BuildPointerProperties(args)
isInContact: properties.HasPressedButton,
properties: properties
)
)
);
Expand All @@ -165,7 +193,7 @@ private void HostOnMouseDown(object sender, MouseButtonEventArgs args)
pointerId: 0,
rawPosition: new Windows.Foundation.Point(position.X, position.Y),
position: new Windows.Foundation.Point(position.X, position.Y),
isInContact: false,
isInContact: true,
properties: BuildPointerProperties(args)
)
)
Expand All @@ -183,6 +211,7 @@ private void HostOnMouseUp(object sender, MouseButtonEventArgs args)
{
var position = args.GetPosition(_host);

var properties = BuildPointerProperties(args);
_ownerEvents.RaisePointerReleased(
new PointerEventArgs(
new Windows.UI.Input.PointerPoint(
Expand All @@ -192,8 +221,8 @@ private void HostOnMouseUp(object sender, MouseButtonEventArgs args)
pointerId: 0,
rawPosition: new Windows.Foundation.Point(position.X, position.Y),
position: new Windows.Foundation.Point(position.X, position.Y),
isInContact: false,
properties: BuildPointerProperties(args)
isInContact: properties.HasPressedButton,
properties: properties
)
)
);
Expand All @@ -204,31 +233,66 @@ private void HostOnMouseUp(object sender, MouseButtonEventArgs args)
}
}

private void HostOnMouseWheel(object sender, MouseWheelEventArgs args)
private IntPtr OnWmMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
{
try
static short GetLoWord(int i) => (short)(i & 0xFFFF);
static short GetHiWord(int i) => (short)(i >> 16);

switch (msg)
{
var position = args.GetPosition(_host);
case WM_DPICHANGED:
break;
case WM_MOUSEHWHEEL:
case WM_MOUSEWHEEL:
{
var keys = (MouseModifierKeys)wparam;

_ownerEvents.RaisePointerWheelChanged(
new PointerEventArgs(
new Windows.UI.Input.PointerPoint(
frameId: GetNextFrameId(),
timestamp: (uint)args.Timestamp,
device: GetPointerDevice(args),
pointerId: 0,
rawPosition: new Windows.Foundation.Point(position.X, position.Y),
position: new Windows.Foundation.Point(position.X, position.Y),
isInContact: false,
properties: BuildPointerProperties(args, args.Delta)
// Horizontal Mouse wheel is also supported by holding SHIFT key
// That's a very usual practice on most OS & applications
var isHorizontalMouseWheel = msg == WM_MOUSEHWHEEL || keys.HasFlag(MouseModifierKeys.MK_SHIFT);

// Vertical: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-mousewheel
// Horizontal: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-mousehwheel

var l = (int)lparam;
var screenPosition = new Point(GetLoWord(l), GetHiWord(l));
var wpfPosition = _host.PointFromScreen(screenPosition);
var position = new Windows.Foundation.Point(wpfPosition.X, wpfPosition.Y);

var properties = new PointerPointProperties
{
IsLeftButtonPressed = keys.HasFlag(MouseModifierKeys.MK_LBUTTON),
IsMiddleButtonPressed = keys.HasFlag(MouseModifierKeys.MK_MBUTTON),
IsRightButtonPressed = keys.HasFlag(MouseModifierKeys.MK_RBUTTON),
IsXButton1Pressed = keys.HasFlag(MouseModifierKeys.MK_XBUTTON1),
IsXButton2Pressed = keys.HasFlag(MouseModifierKeys.MK_XBUTTON2),
IsHorizontalMouseWheel = isHorizontalMouseWheel,
IsPrimary = true,
IsInRange = true,
MouseWheelDelta = -((int)wparam >> 16) / 10
};


_ownerEvents.RaisePointerWheelChanged(
new PointerEventArgs(
new Windows.UI.Input.PointerPoint(
frameId: GetNextFrameId(),
timestamp: (uint)Environment.TickCount,
device: PointerDevice.For(PointerDeviceType.Mouse),
pointerId: 0,
rawPosition: position,
position: position,
isInContact: properties.HasPressedButton,
properties: properties
)
)
)
);
}
catch (Exception e)
{
this.Log().Error("Failed to raise PointerWheelChanged", e);
);

break;
}
}

return IntPtr.Zero;
}
}
}

0 comments on commit 108aedd

Please sign in to comment.