예제 #1
0
        protected virtual bool PreprocessScrollWheel(NSEvent e)
        {
            var view = (ContentView.Superview ?? ContentView).HitTest(e.LocationInWindow);

            if (view != null && !view.IsSwfControl() && Control.FromChildHandle(view.Handle) is Control control)
            {
                // This is to allow SWF wrappers of native views to handle messages before the native view swallows them (WebView, for example).

                var p = driver.NativeToMonoScreen(this.ConvertPointToScreenSafe(e.LocationInWindow));
                if (Math.Abs(e.ScrollingDeltaY - nfloat.Epsilon) > 0)
                {
                    var delta  = e.ScaledAndQuantizedDeltaY();
                    var wParam = (IntPtr)(((int)e.ModifiersToWParam() & 0xFFFF) | (delta << 16));
                    var lParam = (IntPtr)((p.X & 0xFFFF) | (p.Y << 16));
                    var msg    = new MSG {
                        hwnd = control.Handle, message = Msg.WM_MOUSEWHEEL, wParam = wParam, lParam = lParam
                    };
                    if (Application.SendMessage(ref msg, out var drop, out var _).ToInt32() == 0 || drop)
                    {
                        return(true);
                    }
                }

                if (Math.Abs(e.ScrollingDeltaX - nfloat.Epsilon) > 0)
                {
                    int delta  = e.ScaledAndQuantizedDeltaX();
                    var wParam = (IntPtr)(((int)e.ModifiersToWParam() & 0xFFFF) | (delta << 16));
                    var lParam = (IntPtr)((p.X & 0xFFFF) | (p.Y << 16));
                    var msg    = new MSG {
                        hwnd = control.Handle, message = Msg.WM_MOUSEHWHEEL, wParam = wParam, lParam = lParam
                    };
                    if (Application.SendMessage(ref msg, out var drop, out var _).ToInt32() == 0 || drop)
                    {
                        return(true);
                    }
                }
            }
            return(false);
        }
예제 #2
0
        public override void ScrollWheel(NSEvent e)
        {
            var msg = TranslateMouseCore(e, out bool _);

            if (Math.Abs(e.ScrollingDeltaY - nfloat.Epsilon) > 0)
            {
                int delta = ScaleAndQuantizeDelta((float)e.ScrollingDeltaY, e.HasPreciseScrollingDeltas);
                msg.message = Msg.WM_MOUSEWHEEL;
                msg.wParam  = (IntPtr)(((int)e.ModifiersToWParam() & 0xFFFF) | (delta << 16));
                msg.lParam  = (IntPtr)((msg.pt.x & 0xFFFF) | (msg.pt.y << 16));
                Application.SendMessage(ref msg);
            }

            if (Math.Abs(e.ScrollingDeltaX - nfloat.Epsilon) > 0)
            {
                int delta = ScaleAndQuantizeDelta((float)-e.ScrollingDeltaX, e.HasPreciseScrollingDeltas);
                msg.message = Msg.WM_MOUSEHWHEEL;
                msg.wParam  = (IntPtr)(((int)e.ModifiersToWParam() & 0xFFFF) | (delta << 16));
                msg.lParam  = (IntPtr)((msg.pt.x & 0xFFFF) | (msg.pt.y << 16));
                Application.SendMessage(ref msg);
            }
        }
예제 #3
0
        public override void ScrollWheel(NSEvent e)
        {
            var msg = TranslateMouseCore(e, out bool _);

            if (e.ScrollingDeltaY != 0)
            {
                int delta = e.ScaledAndQuantizedDeltaY();
                msg.message = Msg.WM_MOUSEWHEEL;
                msg.wParam  = (IntPtr)(((int)e.ModifiersToWParam() & 0xFFFF) | (delta << 16));
                msg.lParam  = (IntPtr)((msg.pt.x & 0xFFFF) | (msg.pt.y << 16));
                Application.SendMessage(ref msg);
            }

            if (e.ScrollingDeltaX != 0)
            {
                int delta = e.ScaledAndQuantizedDeltaX();
                msg.message = Msg.WM_MOUSEHWHEEL;
                msg.wParam  = (IntPtr)(((int)e.ModifiersToWParam() & 0xFFFF) | (delta << 16));
                msg.lParam  = (IntPtr)((msg.pt.x & 0xFFFF) | (msg.pt.y << 16));
                Application.SendMessage(ref msg);
            }
        }
예제 #4
0
        public override void ScrollWheel(NSEvent e)
        {
            var  msg        = TranslateMouseCore(e, out bool _);
            bool horizontal = e.ScrollingDeltaY == 0 && e.ScrollingDeltaX != 0;
            int  delta      = ScaleAndQuantizeDelta((float)(horizontal ? -e.ScrollingDeltaX : e.ScrollingDeltaY), e.HasPreciseScrollingDeltas);

            if (delta != 0 && e.Phase == NSEventPhase.None && e.MomentumPhase == NSEventPhase.None || e.Phase == NSEventPhase.Changed || e.MomentumPhase == NSEventPhase.Changed)
            {
                msg.message = horizontal ? Msg.WM_MOUSEHWHEEL : Msg.WM_MOUSEWHEEL;
                msg.wParam  = (IntPtr)(((int)e.ModifiersToWParam() & 0xFFFF) | (delta << 16));
                msg.lParam  = (IntPtr)((msg.pt.x & 0xFFFF) | (msg.pt.y << 16));
                Application.SendMessage(ref msg);
            }
        }
예제 #5
0
        internal void TranslateMouseDown(NSEvent e)
        {
            var msg = TranslateMouseCore(e, out bool client);

            msg.wParam = (IntPtr)(e.ModifiersToWParam() | e.ButtonNumberToWParam());
            // FIXME: Should be elsewhere
            if (e.ClickCount > 1)
            {
                msg.message = (client ? Msg.WM_LBUTTONDBLCLK : Msg.WM_NCLBUTTONDBLCLK) + MsgOffset4Button(e);
            }
            else
            {
                msg.message      = (client ? Msg.WM_LBUTTONDOWN : Msg.WM_NCLBUTTONDOWN) + MsgOffset4Button(e);
                LastMouseDownMsg = msg;
            }
            Application.SendMessage(ref msg);
        }
예제 #6
0
        internal void TranslateMouseDown(NSEvent e)
        {
            var msg = TranslateMouseCore(e, out bool client);

            msg.wParam = (IntPtr)(e.ModifiersToWParam() | e.ButtonNumberToWParam());

            if (e.ClickCount > 1 && (e.ClickCount & 1) == 0 && prevMouseDown?.Type == e.Type)
            {
                msg.message = (client ? Msg.WM_LBUTTONDBLCLK : Msg.WM_NCLBUTTONDBLCLK) + MsgOffset4Button(e);
            }
            else
            {
                msg.message      = (client ? Msg.WM_LBUTTONDOWN : Msg.WM_NCLBUTTONDOWN) + MsgOffset4Button(e);
                LastMouseDownMsg = msg;
            }

            prevMouseDown = e;
            Application.SendMessage(ref msg);
        }
예제 #7
0
        internal void TranslateMouseEnterExit(NSEvent e)
        {
            // We use overlapping track rectanges on every control, so the Entered/Exited events
            // are sent not only to the top-level visible view, but also to its superviews.
            // This has to be converted to the the model where only the top-level control receives
            // the messages.
            //
            // The conversion is by performing the hit test. Note that e.LocationInWindow is NOT
            // updated by the time the tracking events are received and thus e.Window.
            // MouseLocationOutsideOfEventStream has to be used instead.
            //
            // The Exited event has to be handled the same way as the entered event to cover the
            // following scenario:
            //  ___________________________
            // | Panel                     |
            // |  ______________           |
            // | |    Button    |          |
            //
            // When you move the mouse over the Button you will receive the Entered event for
            // it. At the same time the Panel already had Entered called for its own tracking area.
            // Now moving the mouse away from the Button into the Panel area will cause an
            // Exited event on the Button's tracking rectangle, but it will NOT generate any
            // Entered event. In the System.Windows.Forms world we want to receive WM_MOUSELEAVE
            // on the Button and WM_MOUSE_ENTERED on the Panel.
            //
            // Further reading:
            // https://stackoverflow.com/questions/9986267/mouse-enter-exit-events-on-partially-hidden-nsviews

            // While dragging, both e.Window and e.WindowNumber relate to the window in which the dragging session started,
            // but e.LocationInWindow always contains the location relative to the window under the cursor!
            var location         = NSEvent.CurrentMouseLocation;
            var number           = NSWindow.WindowNumberAtPoint(location, 0);
            var window           = NSApplication.SharedApplication.WindowWithWindowNumber(number) ?? e.Window;
            var locationInWindow = window.ConvertPointFromScreenSafe(location);

            var msg                = TranslateMouseCore(e, out bool client);
            var newMouseView       = window?.ContentView.HitTest(locationInWindow) ?? window?.ContentView.Superview?.HitTest(locationInWindow);
            var newMouseViewHandle = newMouseView is MonoView || newMouseView is IMacNativeControl ? newMouseView.Handle : IntPtr.Zero;

            if (newMouseViewHandle == IntPtr.Zero && newMouseView != null)
            {
                var c = Control.FromChildHandle(newMouseView.Handle);
                if (c != null)
                {
                    newMouseViewHandle = c.Handle;
                }
            }

#if DEBUG_MOUSE_ENTER_EXIT
            {
                // DEBUG
                Console.Write(e.Type == NSEventType.MouseEntered ? "ENTER " : "EXIT ");
                if (newMouseView != null)
                {
                    Console.Write("NEW: " + DebugUtility.ControlInfo(newMouseView));
                }
                else
                {
                    Console.Write("NO NEW");
                }
            }
#endif

            if (newMouseViewHandle == driver.LastEnteredHwnd)
            {
                return;
            }

            // Keep track of the last entered window during grab, but don't send messages. The
            // messages are sent by XplatUICocoa.GrabWindow and XplatUICocoa.UngrabWindow when
            // the grabbing state is entered/exited.
            if (driver.Grab.Hwnd != IntPtr.Zero)
            {
                driver.LastEnteredHwnd = newMouseViewHandle;
                return;
            }

            msg.wParam = (IntPtr)(e.ModifiersToWParam() | Mac.Extensions.ButtonMaskToWParam(NSEvent.CurrentPressedMouseButtons));
            // First notify the old window that mouse left it
            if (driver.LastEnteredHwnd != IntPtr.Zero)
            {
                msg.hwnd               = driver.LastEnteredHwnd;
                msg.message            = Msg.WM_MOUSELEAVE;
                driver.LastEnteredHwnd = IntPtr.Zero;
                Application.SendMessage(ref msg);
            }
            // And then notify the new window that mouse entered it
            if (newMouseViewHandle != IntPtr.Zero)
            {
                msg.hwnd               = newMouseViewHandle;
                msg.message            = Msg.WM_MOUSE_ENTER;
                driver.LastEnteredHwnd = newMouseViewHandle;
                Application.SendMessage(ref msg);
            }
        }
예제 #8
0
        void TranslateMouseEvent(NSEvent e)
        {
            var  clientView = view as IClientView;
            bool client     = clientView == null;

            var nspoint        = view.ConvertPointFromView(e.LocationInWindow, null);
            var localMonoPoint = driver.NativeToMonoFramed(nspoint, view);

            if ((clientView != null && clientView.ClientBounds.ToRectangle().Contains(localMonoPoint)) ||
                driver.Grab.Hwnd != IntPtr.Zero)
            {
                client            = true;
                localMonoPoint.X -= (int)clientView.ClientBounds.X;
                localMonoPoint.Y -= (int)clientView.ClientBounds.Y;
            }

            MSG msg = new MSG();

            msg.hwnd   = view.Handle;
            msg.lParam = (IntPtr)(localMonoPoint.Y << 16 | (localMonoPoint.X & 0xFFFF));
            if (e.Window == null)
            {
                msg.pt = driver.NativeToMonoScreen(NSEvent.CurrentMouseLocation).ToPOINT();
            }
            else
            {
                msg.pt = driver.NativeToMonoScreen(e.Window.ConvertBaseToScreen(e.LocationInWindow)).ToPOINT();
            }
            msg.refobject = view;

            switch (e.Type)
            {
            case NSEventType.LeftMouseDown:
            case NSEventType.RightMouseDown:
            case NSEventType.OtherMouseDown:
                // FIXME: Should be elsewhere
                if (e.ClickCount > 1)
                {
                    msg.message = (client ? Msg.WM_LBUTTONDBLCLK : Msg.WM_NCLBUTTONDBLCLK) + MsgOffset4Button(e);
                }
                else
                {
                    msg.message = (client ? Msg.WM_LBUTTONDOWN : Msg.WM_NCLBUTTONDOWN) + MsgOffset4Button(e);
                }
                msg.wParam = (IntPtr)(e.ModifiersToWParam() | e.ButtonNumberToWParam());
                break;

            case NSEventType.LeftMouseUp:
            case NSEventType.RightMouseUp:
            case NSEventType.OtherMouseUp:
                msg.message = (client ? Msg.WM_LBUTTONUP : Msg.WM_NCLBUTTONUP) + MsgOffset4Button(e);
                msg.wParam  = (IntPtr)(e.ModifierFlags.ToWParam() | e.ButtonNumberToWParam());
                break;

            case NSEventType.MouseMoved:
                msg.wParam  = (IntPtr)(e.ModifierFlags.ToWParam());
                msg.message = (client ? Msg.WM_MOUSEMOVE : Msg.WM_NCMOUSEMOVE);
                break;

            case NSEventType.LeftMouseDragged:
                msg.wParam  = (IntPtr)(e.ModifierFlags.ToWParam() | (uint)MsgButtons.MK_LBUTTON);
                msg.message = (client ? Msg.WM_MOUSEMOVE : Msg.WM_NCMOUSEMOVE);
                break;

            case NSEventType.RightMouseDragged:
                msg.wParam  = (IntPtr)(e.ModifierFlags.ToWParam() | (uint)MsgButtons.MK_RBUTTON);
                msg.message = (client ? Msg.WM_MOUSEMOVE : Msg.WM_NCMOUSEMOVE);
                break;

            case NSEventType.OtherMouseDragged:
                msg.wParam  = (IntPtr)(e.ModifierFlags.ToWParam() | (uint)MsgButtons.MK_MBUTTON);
                msg.message = (client ? Msg.WM_MOUSEMOVE : Msg.WM_NCMOUSEMOVE);
                break;

            case NSEventType.ScrollWheel:
                bool horizontal = e.ScrollingDeltaY == 0 && e.ScrollingDeltaX != 0;
                int  delta      = ScaleAndQuantizeDelta((float)(horizontal ? e.ScrollingDeltaX : e.ScrollingDeltaY), e.HasPreciseScrollingDeltas);
                if (delta == 0)
                {
                    return;
                }

                if (e.Phase == NSEventPhase.None && e.MomentumPhase == NSEventPhase.None || e.Phase == NSEventPhase.Changed || e.MomentumPhase == NSEventPhase.Changed)
                {
                    msg.message = Msg.WM_MOUSEWHEEL;
                    msg.wParam  = (IntPtr)(e.ModifiersToWParam());
                    msg.wParam  = (IntPtr)(((int)msg.wParam & 0xFFFF) | (delta << 16));
                    msg.lParam  = (IntPtr)((msg.pt.x & 0xFFFF) | (msg.pt.y << 16));
                    break;
                }
                return;

            case NSEventType.MouseEntered:
            case NSEventType.MouseExited:
                // We use overlapping track rectanges on every control, so the Entered/Exited events
                // are sent not only to the top-level visible view, but also to its superviews.
                // This has to be converted to the the model where only the top-level control receives
                // the messages.
                //
                // The conversion is by performing the hit test. Note that e.LocationInWindow is NOT
                // updated by the time the tracking events are received and thus e.Window.
                // MouseLocationOutsideOfEventStream has to be used instead.
                //
                // The Exited event has to be handled the same way as the entered event to cover the
                // following scenario:
                //  ___________________________
                // | Panel                     |
                // |  ______________           |
                // | |    Button    |          |
                //
                // When you move the mouse over the Button you will receive the Entered event for
                // it. At the same time the Panel already had Entered called for its own tracking area.
                // Now moving the mouse away from the Button into the Panel area will cause an
                // Exited event on the Button's tracking rectangle, but it will NOT generate any
                // Entered event. In the System.Windows.Forms world we want to receive WM_MOUSELEAVE
                // on the Button and WM_MOUSE_ENTERED on the Panel.
                //
                // Further reading:
                // https://stackoverflow.com/questions/9986267/mouse-enter-exit-events-on-partially-hidden-nsviews

                var newMouseView       = e.Window?.ContentView.HitTest(e.LocationInWindow) ?? e.Window?.ContentView.Superview?.HitTest(e.LocationInWindow);
                var newMouseViewHandle = newMouseView is MonoView ? newMouseView.Handle : IntPtr.Zero;

#if DEBUG_MOUSE_ENTER_EXIT
                {
                    // DEBUG
                    Console.Write(e.Type == NSEventType.MouseEntered ? "ENTER " : "EXIT ");
                    if (newMouseView != null)
                    {
                        Console.Write("NEW: " + DebugUtility.ControlInfo(newMouseView));
                    }
                    else
                    {
                        Console.Write("NO NEW");
                    }
                }
#endif

                if (newMouseViewHandle == driver.LastEnteredHwnd)
                {
                    return;
                }

                // Keep track of the last entered window during grab, but don't send messages. The
                // messages are sent by XplatUICocoa.GrabWindow and XplatUICocoa.UngrabWindow when
                // the grabbing state is entered/exited.
                if (driver.Grab.Hwnd != IntPtr.Zero)
                {
                    driver.LastEnteredHwnd = newMouseViewHandle;
                    return;
                }

                msg.wParam = (IntPtr)(e.ModifiersToWParam() | Mac.Extensions.ButtonMaskToWParam(NSEvent.CurrentPressedMouseButtons));
                // First notify the old window that mouse left it
                if (driver.LastEnteredHwnd != IntPtr.Zero)
                {
                    msg.hwnd               = driver.LastEnteredHwnd;
                    msg.message            = Msg.WM_MOUSELEAVE;
                    driver.LastEnteredHwnd = IntPtr.Zero;
                    Application.SendMessage(ref msg);
                }
                // And then notify the new window that mouse entered it
                if (newMouseViewHandle != IntPtr.Zero)
                {
                    msg.hwnd               = newMouseViewHandle;
                    msg.message            = Msg.WM_MOUSE_ENTER;
                    driver.LastEnteredHwnd = newMouseViewHandle;
                    Application.SendMessage(ref msg);
                }
                return;

            //case NSEventType.TabletPoint:
            //case NSEventType.TabletProximity:
            default:
                return;
            }

            Application.SendMessage(ref msg);
        }