From 9b79610cb0bd1942210fbad76dab7bf8431cd04b Mon Sep 17 00:00:00 2001 From: jules Date: Mon, 22 Jun 2015 20:28:15 +0100 Subject: [PATCH] Added a flag MouseWheelEvent::isInertial (currently only implemented for OSX 10.7 or later), and used this to replace some clunky behaviour in the Viewport class that was there to avoid inertial wheel movements triggering nested scrollable components. --- .../juce_gui_basics/layout/juce_Viewport.cpp | 32 +------------------ .../juce_gui_basics/layout/juce_Viewport.h | 4 +-- .../juce_gui_basics/mouse/juce_MouseEvent.h | 4 +++ .../mouse/juce_MouseInputSource.cpp | 16 +++++++--- .../native/juce_linux_Windowing.cpp | 1 + .../native/juce_mac_NSViewComponentPeer.mm | 12 ++++--- .../native/juce_win32_Windowing.cpp | 1 + 7 files changed, 28 insertions(+), 42 deletions(-) diff --git a/modules/juce_gui_basics/layout/juce_Viewport.cpp b/modules/juce_gui_basics/layout/juce_Viewport.cpp index 21360a0419..edab5284da 100644 --- a/modules/juce_gui_basics/layout/juce_Viewport.cpp +++ b/modules/juce_gui_basics/layout/juce_Viewport.cpp @@ -24,13 +24,13 @@ Viewport::Viewport (const String& name) : Component (name), - customScrollBarThickness(false), scrollBarThickness (0), singleStepX (16), singleStepY (16), showHScrollbar (true), showVScrollbar (true), deleteContent (true), + customScrollBarThickness (false), allowScrollingWithoutScrollbarV (false), allowScrollingWithoutScrollbarH (false), verticalScrollBar (true), @@ -55,7 +55,6 @@ Viewport::Viewport (const String& name) Viewport::~Viewport() { deleteContentComp(); - mouseWheelTimer = nullptr; } //============================================================================== @@ -382,30 +381,6 @@ static int rescaleMouseWheelDistance (float distance, int singleStepSize) noexce : jmax (distance, 1.0f)); } -// This puts a temporary component overlay over the content component, to prevent -// wheel events from reaching components inside it, so that while spinning a wheel -// with momentum, it won't accidentally scroll any subcomponents of the viewport. -struct Viewport::MouseWheelTimer : public Timer -{ - MouseWheelTimer (Viewport& v) : viewport (v) - { - viewport.contentHolder.addAndMakeVisible (dummyOverlay); - dummyOverlay.setAlwaysOnTop (true); - dummyOverlay.setPaintingIsUnclipped (true); - dummyOverlay.setBounds (viewport.contentHolder.getLocalBounds()); - } - - void timerCallback() override - { - viewport.mouseWheelTimer = nullptr; - } - - Component dummyOverlay; - Viewport& viewport; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MouseWheelTimer) -}; - bool Viewport::useMouseWheelMoveIfNeeded (const MouseEvent& e, const MouseWheelDetails& wheel) { if (! (e.mods.isAltDown() || e.mods.isCtrlDown() || e.mods.isCommandDown())) @@ -436,11 +411,6 @@ bool Viewport::useMouseWheelMoveIfNeeded (const MouseEvent& e, const MouseWheelD if (pos != getViewPosition()) { - if (mouseWheelTimer == nullptr) - mouseWheelTimer = new MouseWheelTimer (*this); - - mouseWheelTimer->startTimer (300); - setViewPosition (pos); return true; } diff --git a/modules/juce_gui_basics/layout/juce_Viewport.h b/modules/juce_gui_basics/layout/juce_Viewport.h index 78aa4fa01f..0fa4aaf505 100644 --- a/modules/juce_gui_basics/layout/juce_Viewport.h +++ b/modules/juce_gui_basics/layout/juce_Viewport.h @@ -263,15 +263,13 @@ private: //============================================================================== WeakReference contentComp; Rectangle lastVisibleArea; - bool customScrollBarThickness; int scrollBarThickness; int singleStepX, singleStepY; bool showHScrollbar, showVScrollbar, deleteContent; + bool customScrollBarThickness; bool allowScrollingWithoutScrollbarV, allowScrollingWithoutScrollbarH; Component contentHolder; ScrollBar verticalScrollBar, horizontalScrollBar; - struct MouseWheelTimer; - ScopedPointer mouseWheelTimer; Point viewportPosToCompPos (Point) const; diff --git a/modules/juce_gui_basics/mouse/juce_MouseEvent.h b/modules/juce_gui_basics/mouse/juce_MouseEvent.h index 92ac754ed0..210dd009e0 100644 --- a/modules/juce_gui_basics/mouse/juce_MouseEvent.h +++ b/modules/juce_gui_basics/mouse/juce_MouseEvent.h @@ -354,6 +354,10 @@ struct MouseWheelDetails /** If true, then the wheel has continuous, un-stepped motion. */ bool isSmooth; + + /** If true, then this event is part of the intertial momentum phase that follows + the wheel being released. */ + bool isInertial; }; diff --git a/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp b/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp index 7df09b4b8f..45c8eba956 100644 --- a/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp +++ b/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp @@ -331,10 +331,17 @@ public: Time time, const MouseWheelDetails& wheel) { Desktop::getInstance().incrementMouseWheelCounter(); - Point screenPos; - if (Component* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos)) - sendMouseWheel (*current, screenPos, time, wheel); + + // This will make sure that when the wheel spins in its inertial phase, any events + // continue to be sent to the last component that the mouse was over when it was being + // actively controlled by the user. This avoids confusion when scrolling through nested + // scrollable components. + if (lastNonInertialWheelTarget == nullptr || ! wheel.isInertial) + lastNonInertialWheelTarget = getTargetForGesture (peer, positionWithinPeer, time, screenPos); + + if (Component* target = lastNonInertialWheelTarget) + sendMouseWheel (*target, screenPos, time, wheel); } void handleMagnifyGesture (ComponentPeer& peer, Point positionWithinPeer, @@ -467,7 +474,7 @@ public: bool isUnboundedMouseModeOn, isCursorVisibleUntilOffscreen; private: - WeakReference componentUnderMouse; + WeakReference componentUnderMouse, lastNonInertialWheelTarget; ComponentPeer* lastPeer; void* currentCursorHandle; @@ -512,6 +519,7 @@ private: mouseDowns[0].peerID = 0; mouseMovedSignificantlySincePressed = false; + lastNonInertialWheelTarget = nullptr; } void registerMouseDrag (Point screenPos) noexcept diff --git a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp index f597d5c2ab..03cb822817 100644 --- a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -2209,6 +2209,7 @@ public: wheel.deltaY = amount; wheel.isReversed = false; wheel.isSmooth = false; + wheel.isInertial = false; handleMouseWheel (0, getMousePos (buttonPressEvent), getEventTime (buttonPressEvent), wheel); } diff --git a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm index cb0cf5efd7..98c1b74925 100644 --- a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm +++ b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm @@ -497,10 +497,7 @@ public: void toBehind (ComponentPeer* other) override { - NSViewComponentPeer* const otherPeer = dynamic_cast (other); - jassert (otherPeer != nullptr); // wrong type of window? - - if (otherPeer != nullptr) + if (NSViewComponentPeer* const otherPeer = dynamic_cast (other)) { if (isSharedWindow) { @@ -514,6 +511,10 @@ public: relativeTo: [otherPeer->window windowNumber]]; } } + else + { + jassertfalse; // wrong type of window? + } } void setIcon (const Image&) override @@ -620,6 +621,7 @@ public: wheel.deltaY = 0; wheel.isReversed = false; wheel.isSmooth = false; + wheel.isInertial = false; #if ! JUCE_PPC @try @@ -628,6 +630,8 @@ public: if ([ev respondsToSelector: @selector (isDirectionInvertedFromDevice)]) wheel.isReversed = [ev isDirectionInvertedFromDevice]; + wheel.isInertial = ([ev momentumPhase] != NSEventPhaseNone); + if ([ev respondsToSelector: @selector (hasPreciseScrollingDeltas)]) { if ([ev hasPreciseScrollingDeltas]) diff --git a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index 0b55b2ece3..2d8f92d90a 100644 --- a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -1820,6 +1820,7 @@ private: wheel.deltaY = isVertical ? amount / 256.0f : 0.0f; wheel.isReversed = false; wheel.isSmooth = false; + wheel.isInertial = false; Point localPos; if (ComponentPeer* const peer = findPeerUnderMouse (localPos))