From 7f90f5c8e6242c1bd57fcf7f0200e7fa9c06e016 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 4 Dec 2014 22:28:42 +0100 Subject: [PATCH] Added mousewheel support to ScrollViewer. --- .../Presenters/ScrollContentPresenter.cs | 18 ++++++++++-- Perspex.Controls/ScrollViewer.cs | 7 +++++ Perspex.Input/InputElement.cs | 14 +++++++++ Perspex.Input/MouseDevice.cs | 29 +++++++++++++++++++ Perspex.Input/Perspex.Input.csproj | 2 ++ Perspex.Input/PointerWheelEventArgs.cs | 13 +++++++++ Perspex.Input/Raw/RawMouseEventArgs.cs | 1 + Perspex.Input/Raw/RawMouseWheelEventArgs.cs | 26 +++++++++++++++++ Windows/Perspex.Win32/WindowImpl.cs | 17 +++++++++++ 9 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 Perspex.Input/PointerWheelEventArgs.cs create mode 100644 Perspex.Input/Raw/RawMouseWheelEventArgs.cs diff --git a/Perspex.Controls/Presenters/ScrollContentPresenter.cs b/Perspex.Controls/Presenters/ScrollContentPresenter.cs index a33f4af7a8..857c8753fb 100644 --- a/Perspex.Controls/Presenters/ScrollContentPresenter.cs +++ b/Perspex.Controls/Presenters/ScrollContentPresenter.cs @@ -6,7 +6,9 @@ namespace Perspex.Controls.Presenters { + using System; using System.Linq; + using Perspex.Input; using Perspex.Layout; public class ScrollContentPresenter : ContentPresenter @@ -20,9 +22,9 @@ namespace Perspex.Controls.Presenters public static readonly PerspexProperty ViewportProperty = ScrollViewer.ViewportProperty.AddOwner(); - public ScrollContentPresenter() + static ScrollContentPresenter() { - Control.AffectsRender(OffsetProperty); + Control.AffectsArrange(OffsetProperty); } public Size Extent @@ -74,5 +76,17 @@ namespace Perspex.Controls.Presenters return new Size(); } + + protected override void OnPointerWheelChanged(PointerWheelEventArgs e) + { + if (this.Extent.Height > this.Viewport.Height) + { + var y = this.Offset.Y + (-e.Delta.Y * 50); + y = Math.Max(y, 0); + y = Math.Min(y, Extent.Height - Viewport.Height); + this.Offset = new Vector(this.Offset.X, y); + e.Handled = true; + } + } } } diff --git a/Perspex.Controls/ScrollViewer.cs b/Perspex.Controls/ScrollViewer.cs index 5a384e2d87..e26ca99831 100644 --- a/Perspex.Controls/ScrollViewer.cs +++ b/Perspex.Controls/ScrollViewer.cs @@ -6,6 +6,7 @@ namespace Perspex.Controls { + using System; using System.Reactive.Linq; using Perspex.Controls.Presenters; using Perspex.Controls.Primitives; @@ -89,6 +90,12 @@ namespace Perspex.Controls this.verticalScrollBar.GetObservable(ScrollBar.ValueProperty)) .Select(x => new Vector(x[0], x[1])); + this.presenter.GetObservable(ScrollContentPresenter.OffsetProperty).Subscribe(x => + { + this.horizontalScrollBar.Value = x.X; + this.verticalScrollBar.Value = x.Y; + }); + this.Bind(OffsetProperty, offset); } } diff --git a/Perspex.Input/InputElement.cs b/Perspex.Input/InputElement.cs index 9d9978d8ea..f83dfe8202 100644 --- a/Perspex.Input/InputElement.cs +++ b/Perspex.Input/InputElement.cs @@ -48,6 +48,9 @@ namespace Perspex.Input public static readonly RoutedEvent PointerReleasedEvent = RoutedEvent.Register("PointerReleased", RoutingStrategy.Bubble); + public static readonly RoutedEvent PointerWheelChangedEvent = + RoutedEvent.Register("PointerWheelChanged", RoutingStrategy.Bubble); + public InputElement() { this.GotFocus += (_, e) => this.OnGotFocus(e); @@ -59,6 +62,7 @@ namespace Perspex.Input this.PointerMoved += (_, e) => this.OnPointerMoved(e); this.PointerPressed += (_, e) => this.OnPointerPressed(e); this.PointerReleased += (_, e) => this.OnPointerReleased(e); + this.PointerWheelChanged += (_, e) => this.OnPointerWheelChanged(e); } public event EventHandler GotFocus @@ -115,6 +119,12 @@ namespace Perspex.Input remove { this.RemoveHandler(PointerReleasedEvent, value); } } + public event EventHandler PointerWheelChanged + { + add { this.AddHandler(PointerWheelChangedEvent, value); } + remove { this.RemoveHandler(PointerWheelChangedEvent, value); } + } + public bool Focusable { get { return this.GetValue(FocusableProperty); } @@ -177,5 +187,9 @@ namespace Perspex.Input protected virtual void OnPointerReleased(PointerEventArgs e) { } + + protected virtual void OnPointerWheelChanged(PointerWheelEventArgs e) + { + } } } diff --git a/Perspex.Input/MouseDevice.cs b/Perspex.Input/MouseDevice.cs index 4ef82f543b..c3ea4cefdf 100644 --- a/Perspex.Input/MouseDevice.cs +++ b/Perspex.Input/MouseDevice.cs @@ -76,6 +76,13 @@ namespace Perspex.Input case RawMouseEventType.LeftButtonUp: this.MouseUp((IMouseDevice)e.Device, (IVisual)e.Root, e.Position); break; + case RawMouseEventType.Wheel: + this.MouseWheel( + (IMouseDevice)e.Device, + (IVisual)e.Root, + e.Position, + ((RawMouseWheelEventArgs)e).Delta); + break; } } @@ -162,6 +169,28 @@ namespace Perspex.Input } } + private void MouseWheel(IMouseDevice device, IVisual visual, Point p, Vector delta) + { + IVisual hit = visual.GetVisualAt(p); + + if (hit != null) + { + IInteractive source = this.GetSource(hit); + + if (source != null) + { + source.RaiseEvent(new PointerWheelEventArgs + { + Device = this, + RoutedEvent = InputElement.PointerWheelChangedEvent, + OriginalSource = source, + Source = source, + Delta = delta, + }); + } + } + } + private IInputElement GetFocusable(IVisual hit) { return this.Captured as IInputElement ?? diff --git a/Perspex.Input/Perspex.Input.csproj b/Perspex.Input/Perspex.Input.csproj index ef7d2078b6..3dbee40898 100644 --- a/Perspex.Input/Perspex.Input.csproj +++ b/Perspex.Input/Perspex.Input.csproj @@ -68,6 +68,8 @@ + + diff --git a/Perspex.Input/PointerWheelEventArgs.cs b/Perspex.Input/PointerWheelEventArgs.cs new file mode 100644 index 0000000000..a021bff1b9 --- /dev/null +++ b/Perspex.Input/PointerWheelEventArgs.cs @@ -0,0 +1,13 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2013 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Input +{ + public class PointerWheelEventArgs : PointerEventArgs + { + public Vector Delta { get; set; } + } +} diff --git a/Perspex.Input/Raw/RawMouseEventArgs.cs b/Perspex.Input/Raw/RawMouseEventArgs.cs index c13f41d564..5b0283c626 100644 --- a/Perspex.Input/Raw/RawMouseEventArgs.cs +++ b/Perspex.Input/Raw/RawMouseEventArgs.cs @@ -14,6 +14,7 @@ namespace Perspex.Input.Raw Move, LeftButtonDown, LeftButtonUp, + Wheel, } public class RawMouseEventArgs : RawInputEventArgs diff --git a/Perspex.Input/Raw/RawMouseWheelEventArgs.cs b/Perspex.Input/Raw/RawMouseWheelEventArgs.cs new file mode 100644 index 0000000000..fa04240905 --- /dev/null +++ b/Perspex.Input/Raw/RawMouseWheelEventArgs.cs @@ -0,0 +1,26 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2013 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Input.Raw +{ + using System; + using Perspex.Layout; + + public class RawMouseWheelEventArgs : RawMouseEventArgs + { + public RawMouseWheelEventArgs( + IInputDevice device, + ILayoutRoot root, + Point position, + Vector delta) + : base(device, root, RawMouseEventType.Wheel, position) + { + this.Delta = delta; + } + + public Vector Delta { get; private set; } + } +} diff --git a/Windows/Perspex.Win32/WindowImpl.cs b/Windows/Perspex.Win32/WindowImpl.cs index afc72a9284..339969d2a2 100644 --- a/Windows/Perspex.Win32/WindowImpl.cs +++ b/Windows/Perspex.Win32/WindowImpl.cs @@ -134,9 +134,18 @@ namespace Perspex.Win32 this.Handle = new PlatformHandle(this.hwnd, "HWND"); } + private Point ScreenToClient(uint x, uint y) + { + var p = new UnmanagedMethods.POINT { X = (int)x, Y = (int)y }; + UnmanagedMethods.ScreenToClient(this.hwnd, ref p); + return new Point(p.X, p.Y); + } + [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")] private IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) { + const double WheelDelta = 120.0; + RawInputEventArgs e = null; WindowsMouseDevice.Instance.CurrentWindow = this; @@ -184,6 +193,14 @@ namespace Perspex.Win32 new Point((uint)lParam & 0xffff, (uint)lParam >> 16)); break; + case UnmanagedMethods.WindowsMessage.WM_MOUSEWHEEL: + e = new RawMouseWheelEventArgs( + WindowsMouseDevice.Instance, + this.owner, + this.ScreenToClient((uint)lParam & 0xffff, (uint)lParam >> 16), + new Vector(0, ((int)wParam >> 16) / WheelDelta)); + break; + case UnmanagedMethods.WindowsMessage.WM_PAINT: UnmanagedMethods.PAINTSTRUCT ps;