From d3390ac553a56533ae9532c224ac9222c66e874a Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 13 Sep 2017 14:38:32 +0300 Subject: [PATCH] Added managed BeginResizeDrag implementation --- .../ControlCatalog/DecoratedWindow.xaml.cs | 4 +- .../Avalonia.MonoMac/Avalonia.MonoMac.csproj | 3 + src/OSX/Avalonia.MonoMac/TopLevelImpl.cs | 13 ++- src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs | 21 ++++- src/Shared/WindowResizeDragHelper.cs | 85 +++++++++++++++++++ .../Avalonia.Win32.Shared.projitems | 1 + src/Windows/Avalonia.Win32/WindowImpl.cs | 21 +++++ 7 files changed, 140 insertions(+), 8 deletions(-) create mode 100644 src/Shared/WindowResizeDragHelper.cs diff --git a/samples/ControlCatalog/DecoratedWindow.xaml.cs b/samples/ControlCatalog/DecoratedWindow.xaml.cs index 4a11e45519..d28281e476 100644 --- a/samples/ControlCatalog/DecoratedWindow.xaml.cs +++ b/samples/ControlCatalog/DecoratedWindow.xaml.cs @@ -33,8 +33,8 @@ namespace ControlCatalog }; SetupSide("Left", StandardCursorType.LeftSide, WindowEdge.West); SetupSide("Right", StandardCursorType.RightSide, WindowEdge.East); - SetupSide("Top", StandardCursorType.TopSide, WindowEdge.South); - SetupSide("Bottom", StandardCursorType.BottomSize, WindowEdge.North); + SetupSide("Top", StandardCursorType.TopSide, WindowEdge.North); + SetupSide("Bottom", StandardCursorType.BottomSize, WindowEdge.South); SetupSide("TopLeft", StandardCursorType.TopLeftCorner, WindowEdge.NorthWest); SetupSide("TopRight", StandardCursorType.TopRightCorner, WindowEdge.NorthEast); SetupSide("BottomLeft", StandardCursorType.BottomLeftCorner, WindowEdge.SouthWest); diff --git a/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj b/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj index 43816fc801..d5aa038b0e 100644 --- a/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj +++ b/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj @@ -3,6 +3,9 @@ netstandard2.0 True + + + diff --git a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs index c2a5e15b44..2744474214 100644 --- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs +++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs @@ -22,6 +22,11 @@ namespace Avalonia.MonoMac View = new TopLevelView(this); } + protected virtual void OnInput(RawInputEventArgs args) + { + Input?.Invoke(args); + } + [Adopts("NSTextInputClient")] public class TopLevelView : NSView { @@ -135,11 +140,11 @@ namespace Avalonia.MonoMac if (delta.X == 0 && delta.Y == 0) return; // ReSharper restore CompareOfFloatsByEqualityOperator - _tl.Input?.Invoke(new RawMouseWheelEventArgs(_mouse, ts, _tl.InputRoot, loc, + _tl.OnInput(new RawMouseWheelEventArgs(_mouse, ts, _tl.InputRoot, loc, delta, mod)); } else - _tl.Input?.Invoke(new RawMouseEventArgs(_mouse, ts, _tl.InputRoot, type, loc, mod)); + _tl.OnInput(new RawMouseEventArgs(_mouse, ts, _tl.InputRoot, type, loc, mod)); } public override void MouseMoved(NSEvent theEvent) @@ -229,7 +234,7 @@ namespace Avalonia.MonoMac var code = KeyTransform.TransformKeyCode(ev.KeyCode); if (!code.HasValue) return; - _tl.Input?.Invoke(new RawKeyEventArgs(_keyboard, GetTimeStamp(ev), + _tl.OnInput(new RawKeyEventArgs(_keyboard, GetTimeStamp(ev), type, code.Value, GetModifiers(ev.ModifierFlags))); } @@ -294,7 +299,7 @@ namespace Avalonia.MonoMac public void InsertText(NSString str, NSRange range) { //TODO: timestamp - _tl.Input?.Invoke(new RawTextInputEventArgs(_keyboard, 0, str.ToString())); + _tl.OnInput(new RawTextInputEventArgs(_keyboard, 0, str.ToString())); } [Export("characterIndexForPoint:")] diff --git a/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs b/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs index 8e2569efb4..dbced9fd91 100644 --- a/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs +++ b/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Controls; +using Avalonia.Input.Raw; using Avalonia.Platform; using MonoMac.AppKit; using MonoMac.CoreGraphics; @@ -9,10 +10,12 @@ namespace Avalonia.MonoMac { class WindowBaseImpl : TopLevelImpl, IWindowBaseImpl { + private readonly ManagedWindowResizeDragHelper _managedDrag; public CustomWindow Window { get; private set; } public WindowBaseImpl() { + _managedDrag = new ManagedWindowResizeDragHelper(this, _ => { }, ResizeForManagedDrag); Window = new CustomWindow(this) { StyleMask = NSWindowStyle.Titled, @@ -92,7 +95,7 @@ namespace Avalonia.MonoMac public Point Position { get => Window.Frame.ToAvaloniaRect().BottomLeft.ConvertPointY(); - set => Window.CascadeTopLeftFromPoint(value.ToMonoMacPoint().ConvertPointY()); + set => Window.SetFrameTopLeftPoint(value.ToMonoMacPoint().ConvertPointY()); } @@ -126,11 +129,25 @@ namespace Avalonia.MonoMac public void BeginResizeDrag(WindowEdge edge) { - //TODO: Intercept mouse events and implement resize drag manually + var screenPoint = NSEvent.CurrentMouseLocation.ConvertPointY().ToAvaloniaPoint(); + _managedDrag.BeginResizeDrag(edge, PointToClient(screenPoint)); + } + + protected override void OnInput(RawInputEventArgs args) + { + if (_managedDrag.PreprocessInputEvent(ref args)) + return; + base.OnInput(args); } public void Activate() => Window.MakeKeyWindow(); + public void ResizeForManagedDrag(Rect rc) + { + var frame = new CGRect(rc.X, rc.Position.ConvertPointY().Y - rc.Height, rc.Width, rc.Height); + Window.SetFrame(frame, true); + } + public void Resize(Size clientSize) { var pos = Position; diff --git a/src/Shared/WindowResizeDragHelper.cs b/src/Shared/WindowResizeDragHelper.cs new file mode 100644 index 0000000000..c133e493fa --- /dev/null +++ b/src/Shared/WindowResizeDragHelper.cs @@ -0,0 +1,85 @@ +using System; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Input.Raw; +using Avalonia.Platform; + +namespace Avalonia +{ + internal class ManagedWindowResizeDragHelper + { + private readonly IWindowBaseImpl _window; + private readonly Action _captureMouse; + private readonly Action _resize; + private WindowEdge? _edge; + private Point _prevPoint; + + public ManagedWindowResizeDragHelper(IWindowBaseImpl window, Action captureMouse, Action resize = null) + { + _window = window; + _captureMouse = captureMouse; + _resize = resize; + } + + public void BeginResizeDrag(WindowEdge edge, Point currentMousePosition) + { + _captureMouse(true); + _prevPoint = currentMousePosition; + _edge = edge; + } + + public bool PreprocessInputEvent(ref RawInputEventArgs e) + { + if (_edge == null) + return false; + if (e is RawMouseEventArgs args) + { + if (args.Type == RawMouseEventType.LeftButtonUp) + { + _edge = null; + _captureMouse(false); + } + if (args.Type == RawMouseEventType.Move) + { + MoveWindow(args.Position); + return true; + } + + + _edge = null; + } + + return false; + } + + private void MoveWindow(Point position) + { + var diff = position - _prevPoint; + var edge = _edge.Value; + var rc = new Rect(_window.Position, _window.ClientSize); + if (edge == WindowEdge.East || edge == WindowEdge.NorthEast || edge == WindowEdge.SouthEast) + { + rc = rc.WithWidth(rc.Width + diff.X); + _prevPoint = _prevPoint.WithX(position.X); + } + if (edge == WindowEdge.West || edge == WindowEdge.NorthWest || edge == WindowEdge.SouthWest) + rc = rc.WithX(rc.X + diff.X).WithWidth(rc.Width - diff.X); + if (edge == WindowEdge.South || edge == WindowEdge.SouthWest || edge == WindowEdge.SouthEast) + { + rc = rc.WithHeight(rc.Height + diff.Y); + _prevPoint = _prevPoint.WithY(position.Y); + } + if (edge == WindowEdge.North || edge == WindowEdge.NorthWest || edge == WindowEdge.NorthEast) + rc = rc.WithY(rc.Y + diff.Y).WithHeight(rc.Height - diff.Y); + if (_resize != null) + _resize(rc); + else + { + if (_window.Position != rc.Position) + _window.Position = rc.Position; + if (_window.ClientSize != rc.Size) + _window.Resize(rc.Size); + } + } + } +} \ No newline at end of file diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.Shared.projitems b/src/Windows/Avalonia.Win32/Avalonia.Win32.Shared.projitems index 74a2b83efe..4599a0741e 100644 --- a/src/Windows/Avalonia.Win32/Avalonia.Win32.Shared.projitems +++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.Shared.projitems @@ -24,6 +24,7 @@ + Properties\SharedAssemblyInfo.cs diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 72485379a7..cdbb66d0fc 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -38,9 +38,21 @@ namespace Avalonia.Win32 private double _scaling = 1; private WindowState _showWindowState; private FramebufferManager _framebuffer; +#if USE_MANAGED_DRAG + private readonly ManagedWindowResizeDragHelper _managedDrag; +#endif public WindowImpl() { +#if USE_MANAGED_DRAG + _managedDrag = new ManagedWindowResizeDragHelper(this, capture => + { + if (capture) + UnmanagedMethods.SetCapture(Handle.Handle); + else + UnmanagedMethods.ReleaseCapture(); + }); +#endif CreateWindow(); _framebuffer = new FramebufferManager(_hwnd); s_instances.Add(this); @@ -327,8 +339,12 @@ namespace Avalonia.Win32 public void BeginResizeDrag(WindowEdge edge) { +#if USE_MANAGED_DRAG + _managedDrag.BeginResizeDrag(edge, ScreenToClient(MouseDevice.Position)); +#else UnmanagedMethods.DefWindowProc(_hwnd, (int)UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN, new IntPtr((int)EdgeDic[edge]), IntPtr.Zero); +#endif } public Point Position @@ -583,6 +599,11 @@ namespace Avalonia.Win32 PositionChanged?.Invoke(new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16))); return IntPtr.Zero; } +#if USE_MANAGED_DRAG + + if (_managedDrag.PreprocessInputEvent(ref e)) + return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam); +#endif if (e != null && Input != null) {