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))