diff --git a/extras/JuceDemo/Source/demos/RenderingTestComponent.cpp b/extras/JuceDemo/Source/demos/RenderingTestComponent.cpp index 9f3a63482c..94b7a29f0d 100644 --- a/extras/JuceDemo/Source/demos/RenderingTestComponent.cpp +++ b/extras/JuceDemo/Source/demos/RenderingTestComponent.cpp @@ -138,6 +138,11 @@ public: } } + void mouseMagnify (const MouseEvent&, float zoomFactor) + { + owner.sizeSlider->setValue (owner.sizeSlider->getValue() * zoomFactor); + } + private: RenderingTestComponent& owner; double averageTime; @@ -151,13 +156,13 @@ private: float speeds[8]; Time lastSVGLoadTime; - const AffineTransform getTransform() + AffineTransform getTransform() { return AffineTransform::rotation ((float) owner.angleSlider->getValue() / (180.0f / float_Pi)) - .scaled ((float) owner.sizeSlider->getValue(), - (float) owner.sizeSlider->getValue()) - .translated (getWidth() / 2 + (float) owner.xSlider->getValue(), - getHeight() / 2 + (float) owner.ySlider->getValue()); + .scaled ((float) owner.sizeSlider->getValue(), + (float) owner.sizeSlider->getValue()) + .translated (getWidth() / 2 + (float) owner.xSlider->getValue(), + getHeight() / 2 + (float) owner.ySlider->getValue()); } void clipToRectangle (Graphics& g) diff --git a/modules/juce_gui_basics/components/juce_Component.cpp b/modules/juce_gui_basics/components/juce_Component.cpp index 789763addb..cdb31d4eaa 100644 --- a/modules/juce_gui_basics/components/juce_Component.cpp +++ b/modules/juce_gui_basics/components/juce_Component.cpp @@ -2151,6 +2151,12 @@ void Component::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wh parentComponent->mouseWheelMove (e.getEventRelativeTo (parentComponent), wheel); } +void Component::mouseMagnify (const MouseEvent& e, float magnifyAmount) +{ + // the base class just passes this event up to its parent.. + if (parentComponent != nullptr) + parentComponent->mouseMagnify (e.getEventRelativeTo (parentComponent), magnifyAmount); +} //============================================================================== void Component::resized() {} @@ -2477,6 +2483,18 @@ void Component::internalMouseWheel (MouseInputSource& source, const Point& } } +void Component::internalMagnifyGesture (MouseInputSource& source, const Point& relativePos, + const Time& time, float amount) +{ + if (! isCurrentlyBlockedByAnotherModalComponent()) + { + const MouseEvent me (source, relativePos, source.getCurrentModifiers(), + this, this, time, relativePos, time, 0, false); + + mouseMagnify (me, amount); + } +} + void Component::sendFakeMouseMove() const { MouseInputSource& mainMouse = Desktop::getInstance().getMainMouseSource(); diff --git a/modules/juce_gui_basics/components/juce_Component.h b/modules/juce_gui_basics/components/juce_Component.h index fb29e13420..da3f95f9f6 100644 --- a/modules/juce_gui_basics/components/juce_Component.h +++ b/modules/juce_gui_basics/components/juce_Component.h @@ -1559,6 +1559,18 @@ public: virtual void mouseWheelMove (const MouseEvent& event, const MouseWheelDetails& wheel); + /** Called when a pinch-to-zoom mouse-gesture is used. + + If not overridden, a component will forward this message to its parent, so + that parent components can collect gesture messages that are unused by child + components. + + @param scaleFactor a multiplier to indicate by how much the size of the target + should be changed. A value of 1.0 would indicate no change, + values greater than 1.0 mean it should be enlarged. + */ + virtual void mouseMagnify (const MouseEvent& event, float scaleFactor); + //============================================================================== /** Ensures that a non-stop stream of mouse-drag events will be sent during the current mouse-drag operation. @@ -2281,6 +2293,7 @@ private: void internalMouseDrag (MouseInputSource&, const Point&, const Time&); void internalMouseMove (MouseInputSource&, const Point&, const Time&); void internalMouseWheel (MouseInputSource&, const Point&, const Time&, const MouseWheelDetails&); + void internalMagnifyGesture (MouseInputSource&, const Point&, const Time&, float); void internalBroughtToFront(); void internalFocusGain (const FocusChangeType, const WeakReference&); void internalFocusGain (const FocusChangeType); diff --git a/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp b/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp index 3faaa1717f..0c05361f2e 100644 --- a/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp +++ b/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp @@ -82,48 +82,62 @@ public: } //============================================================================== + #if JUCE_DUMP_MOUSE_EVENTS + #define JUCE_MOUSE_EVENT_DBG(desc) DBG ("Mouse " desc << " #" << source.getIndex() \ + << ": " << comp->getLocalPoint (nullptr, screenPos).toString() \ + << " - Comp: " << String::toHexString ((int) comp)); + #else + #define JUCE_MOUSE_EVENT_DBG(desc) + #endif + void sendMouseEnter (Component* const comp, const Point& screenPos, const Time& time) { - //DBG ("Mouse " + String (source.getIndex()) + " enter: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp)); + JUCE_MOUSE_EVENT_DBG ("enter") comp->internalMouseEnter (source, comp->getLocalPoint (nullptr, screenPos), time); } void sendMouseExit (Component* const comp, const Point& screenPos, const Time& time) { - //DBG ("Mouse " + String (source.getIndex()) + " exit: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp)); + JUCE_MOUSE_EVENT_DBG ("exit") comp->internalMouseExit (source, comp->getLocalPoint (nullptr, screenPos), time); } void sendMouseMove (Component* const comp, const Point& screenPos, const Time& time) { - //DBG ("Mouse " + String (source.getIndex()) + " move: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp)); + JUCE_MOUSE_EVENT_DBG ("move") comp->internalMouseMove (source, comp->getLocalPoint (nullptr, screenPos), time); } void sendMouseDown (Component* const comp, const Point& screenPos, const Time& time) { - //DBG ("Mouse " + String (source.getIndex()) + " down: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp)); + JUCE_MOUSE_EVENT_DBG ("down") comp->internalMouseDown (source, comp->getLocalPoint (nullptr, screenPos), time); } void sendMouseDrag (Component* const comp, const Point& screenPos, const Time& time) { - //DBG ("Mouse " + String (source.getIndex()) + " drag: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp)); + JUCE_MOUSE_EVENT_DBG ("drag") comp->internalMouseDrag (source, comp->getLocalPoint (nullptr, screenPos), time); } void sendMouseUp (Component* const comp, const Point& screenPos, const Time& time, const ModifierKeys& oldMods) { - //DBG ("Mouse " + String (source.getIndex()) + " up: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp)); + JUCE_MOUSE_EVENT_DBG ("up") comp->internalMouseUp (source, comp->getLocalPoint (nullptr, screenPos), time, oldMods); } void sendMouseWheel (Component* const comp, const Point& screenPos, const Time& time, const MouseWheelDetails& wheel) { - //DBG ("Mouse " + String (source.getIndex()) + " wheel: " + comp->getLocalPoint (nullptr, screenPos).toString() + " - Comp: " + String::toHexString ((int) comp)); + JUCE_MOUSE_EVENT_DBG ("wheel") comp->internalMouseWheel (source, comp->getLocalPoint (nullptr, screenPos), time, wheel); } + void sendMagnifyGesture (Component* const comp, const Point& screenPos, const Time& time, const float amount) + { + JUCE_MOUSE_EVENT_DBG ("magnify") + comp->internalMagnifyGesture (source, comp->getLocalPoint (nullptr, screenPos), time, amount); + } + //============================================================================== // (returns true if the button change caused a modal event loop) bool setButtons (const Point& screenPos, const Time& time, const ModifierKeys& newButtonState) @@ -276,24 +290,37 @@ public: } } - void handleWheel (ComponentPeer* const peer, const Point& positionWithinPeer, - const Time& time, const MouseWheelDetails& wheel) + Component* getTargetForGesture (ComponentPeer* const peer, const Point& positionWithinPeer, + const Time& time, Point& screenPos) { jassert (peer != nullptr); lastTime = time; ++mouseEventCounter; - Desktop::getInstance().incrementMouseWheelCounter(); - const Point screenPos (peer->localToGlobal (positionWithinPeer)); + screenPos = peer->localToGlobal (positionWithinPeer); setPeer (peer, screenPos, time); setScreenPos (screenPos, time, false); triggerFakeMove(); - if (! isDragging()) - { - if (Component* current = getComponentUnderMouse()) - sendMouseWheel (current, screenPos, time, wheel); - } + return isDragging() ? nullptr : getComponentUnderMouse(); + } + + void handleWheel (ComponentPeer* const peer, const Point& positionWithinPeer, + const Time& time, const MouseWheelDetails& wheel) + { + Desktop::getInstance().incrementMouseWheelCounter(); + + Point screenPos; + if (Component* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos)) + sendMouseWheel (current, screenPos, time, wheel); + } + + void handleMagnifyGesture (ComponentPeer* const peer, const Point& positionWithinPeer, + const Time& time, const float scaleFactor) + { + Point screenPos; + if (Component* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos)) + sendMagnifyGesture (current, screenPos, time, scaleFactor); } //============================================================================== @@ -518,3 +545,9 @@ void MouseInputSource::handleWheel (ComponentPeer* const peer, const Point& { pimpl->handleWheel (peer, positionWithinPeer, Time (time), wheel); } + +void MouseInputSource::handleMagnifyGesture (ComponentPeer* const peer, const Point& positionWithinPeer, + const int64 time, const float scaleFactor) +{ + pimpl->handleMagnifyGesture (peer, positionWithinPeer, Time (time), scaleFactor); +} diff --git a/modules/juce_gui_basics/mouse/juce_MouseInputSource.h b/modules/juce_gui_basics/mouse/juce_MouseInputSource.h index d6c2cb679f..29c36812b8 100644 --- a/modules/juce_gui_basics/mouse/juce_MouseInputSource.h +++ b/modules/juce_gui_basics/mouse/juce_MouseInputSource.h @@ -166,9 +166,11 @@ public: //============================================================================== /** @internal */ - void handleEvent (ComponentPeer*, const Point& positionWithinPeer, int64 time, const ModifierKeys&); + void handleEvent (ComponentPeer*, const Point&, int64 time, const ModifierKeys&); /** @internal */ - void handleWheel (ComponentPeer*, const Point& positionWithinPeer, int64 time, const MouseWheelDetails&); + void handleWheel (ComponentPeer*, const Point&, int64 time, const MouseWheelDetails&); + /** @internal */ + void handleMagnifyGesture (ComponentPeer*, const Point&, int64 time, float scaleFactor); private: //============================================================================== diff --git a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm index a245d19085..c4161622e2 100644 --- a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm +++ b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm @@ -626,6 +626,14 @@ public: handleMouseWheel (0, getMousePos (ev, view), getMouseTime (ev), wheel); } + void redirectMagnify (NSEvent* ev) + { + const float invScale = 1.0f - [ev magnification]; + + if (invScale != 0.0f) + handleMagnifyGesture (0, getMousePos (ev, view), getMouseTime (ev), 1.0f / invScale); + } + void sendMouseEvent (NSEvent* ev) { updateModifiers (ev); @@ -1347,6 +1355,7 @@ struct JuceNSViewClass : public ObjCClass addMethod (@selector (otherMouseDragged:), mouseDragged, "v@:@"); addMethod (@selector (otherMouseUp:), mouseUp, "v@:@"); addMethod (@selector (scrollWheel:), scrollWheel, "v@:@"); + addMethod (@selector (magnifyWithEvent:), magnify, "v@:@"); addMethod (@selector (acceptsFirstMouse:), acceptsFirstMouse, "v@:@"); addMethod (@selector (frameChanged:), frameChanged, "v@:@"); addMethod (@selector (viewDidMoveToWindow), viewDidMoveToWindow, "v@:"); @@ -1425,6 +1434,7 @@ private: static void mouseEntered (id self, SEL, NSEvent* ev) { if (NSViewComponentPeer* const p = getOwner (self)) p->redirectMouseEnter (ev); } static void mouseExited (id self, SEL, NSEvent* ev) { if (NSViewComponentPeer* const p = getOwner (self)) p->redirectMouseExit (ev); } static void scrollWheel (id self, SEL, NSEvent* ev) { if (NSViewComponentPeer* const p = getOwner (self)) p->redirectMouseWheel (ev); } + static void magnify (id self, SEL, NSEvent* ev) { if (NSViewComponentPeer* const p = getOwner (self)) p->redirectMagnify (ev); } static BOOL acceptsFirstMouse (id, SEL, NSEvent*) { return YES; } diff --git a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index bbfbb5cd82..08bd4f80ea 100644 --- a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -77,8 +77,9 @@ bool Desktop::canUseSemiTransparentWindows() noexcept #define TOUCHEVENTF_DOWN 0x0002 #define TOUCHEVENTF_UP 0x0004 DECLARE_HANDLE (HTOUCHINPUT); + DECLARE_HANDLE (HGESTUREINFO); - typedef struct tagTOUCHINPUT + struct TOUCHINPUT { LONG x; LONG y; @@ -90,16 +91,32 @@ bool Desktop::canUseSemiTransparentWindows() noexcept ULONG_PTR dwExtraInfo; DWORD cxContact; DWORD cyContact; - } TOUCHINPUT, *PTOUCHINPUT; + }; + + struct GESTUREINFO + { + UINT cbSize; + DWORD dwFlags; + DWORD dwID; + HWND hwndTarget; + POINTS ptsLocation; + DWORD dwInstanceID; + DWORD dwSequenceID; + ULONGLONG ullArguments; + UINT cbExtraArgs; + }; + #endif typedef BOOL (WINAPI* RegisterTouchWindowFunc) (HWND, ULONG); -typedef BOOL (WINAPI* GetTouchInputInfoFunc) (HTOUCHINPUT, UINT, PTOUCHINPUT, int); +typedef BOOL (WINAPI* GetTouchInputInfoFunc) (HTOUCHINPUT, UINT, TOUCHINPUT*, int); typedef BOOL (WINAPI* CloseTouchInputHandleFunc) (HTOUCHINPUT); +typedef BOOL (WINAPI* GetGestureInfoFunc) (HGESTUREINFO, GESTUREINFO*); static RegisterTouchWindowFunc registerTouchWindow = nullptr; static GetTouchInputInfoFunc getTouchInputInfo = nullptr; static CloseTouchInputHandleFunc closeTouchInputHandle = nullptr; +static GetGestureInfoFunc getGestureInfo = nullptr; static bool hasCheckedForMultiTouch = false; @@ -112,6 +129,7 @@ static bool canUseMultiTouch() registerTouchWindow = (RegisterTouchWindowFunc) getUser32Function ("RegisterTouchWindow"); getTouchInputInfo = (GetTouchInputInfoFunc) getUser32Function ("GetTouchInputInfo"); closeTouchInputHandle = (CloseTouchInputHandleFunc) getUser32Function ("CloseTouchInputHandle"); + getGestureInfo = (GetGestureInfoFunc) getUser32Function ("GetGestureInfo"); } return registerTouchWindow != nullptr; @@ -473,6 +491,7 @@ public: parentToAddTo (parent), currentRenderingEngine (softwareRenderingEngine), lastPaintTime (0), + lastMagnifySize (0), fullScreen (false), isDragging (false), isMouseOver (false), @@ -1070,6 +1089,7 @@ private: ScopedPointer direct2DContext; #endif uint32 lastPaintTime; + ULONGLONG lastMagnifySize; bool fullScreen, isDragging, isMouseOver, hasCreatedCaret, constrainerIsResizing; BorderSize windowBorder; HICON currentWindowIcon; @@ -1690,10 +1710,9 @@ private: doMouseEvent (getCurrentMousePos()); } - void doMouseWheel (const Point& globalPos, const WPARAM wParam, const bool isVertical) + ComponentPeer* findPeerUnderMouse (Point& localPos) { - updateKeyModifiers(); - const float amount = jlimit (-1000.0f, 1000.0f, 0.5f * (short) HIWORD (wParam)); + const Point globalPos (getCurrentMousePosGlobal()); // Because Windows stupidly sends all wheel events to the window with the keyboard // focus, we have to redirect them here according to the mouse pos.. @@ -1703,13 +1722,60 @@ private: if (peer == nullptr) peer = this; + localPos = peer->globalToLocal (globalPos); + return peer; + } + + void doMouseWheel (const WPARAM wParam, const bool isVertical) + { + updateKeyModifiers(); + const float amount = jlimit (-1000.0f, 1000.0f, 0.5f * (short) HIWORD (wParam)); + MouseWheelDetails wheel; wheel.deltaX = isVertical ? 0.0f : amount / -256.0f; wheel.deltaY = isVertical ? amount / 256.0f : 0.0f; wheel.isReversed = false; wheel.isSmooth = false; - peer->handleMouseWheel (0, peer->globalToLocal (globalPos), getMouseEventTime(), wheel); + Point localPos; + if (ComponentPeer* const peer = findPeerUnderMouse (localPos)) + peer->handleMouseWheel (0, localPos, getMouseEventTime(), wheel); + } + + bool doGestureEvent (LPARAM lParam) + { + GESTUREINFO gi; + zerostruct (gi); + gi.cbSize = sizeof (gi); + + if (getGestureInfo != nullptr && getGestureInfo ((HGESTUREINFO) lParam, &gi)) + { + updateKeyModifiers(); + Point localPos; + + if (ComponentPeer* const peer = findPeerUnderMouse (localPos)) + { + switch (gi.dwID) + { + case 3: /*GID_ZOOM*/ + if (gi.dwFlags != 1 /*GF_BEGIN*/ && lastMagnifySize > 0) + peer->handleMagnifyGesture (0, localPos, getMouseEventTime(), + (float) (gi.ullArguments / (double) lastMagnifySize)); + + lastMagnifySize = gi.ullArguments; + return true; + + case 4: /*GID_PAN*/ + case 5: /*GID_ROTATE*/ + case 6: /*GID_TWOFINGERTAP*/ + case 7: /*GID_PRESSANDTAP*/ + default: + break; + } + } + } + + return false; } void doTouchEvent (const int numInputs, HTOUCHINPUT eventHandle) @@ -2226,7 +2292,7 @@ private: case 0x020A: /* WM_MOUSEWHEEL */ case 0x020E: /* WM_MOUSEHWHEEL */ - doMouseWheel (getCurrentMousePosGlobal(), wParam, message == 0x020A); + doMouseWheel (wParam, message == 0x020A); return 0; case WM_TOUCH: @@ -2236,6 +2302,12 @@ private: doTouchEvent ((int) wParam, (HTOUCHINPUT) lParam); return 0; + case 0x119: /* WM_GESTURE */ + if (doGestureEvent (lParam)) + return 0; + + break; + //============================================================================== case WM_SIZING: return handleSizeConstraining (*(RECT*) lParam, wParam); case WM_WINDOWPOSCHANGING: return handlePositionChanging (*(WINDOWPOS*) lParam); diff --git a/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp b/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp index 759ff8f6cc..272c5e8042 100644 --- a/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp +++ b/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp @@ -113,6 +113,13 @@ void ComponentPeer::handleMouseWheel (const int touchIndex, const Point& po mouse->handleWheel (this, positionWithinPeer, time, wheel); } +void ComponentPeer::handleMagnifyGesture (const int touchIndex, const Point& positionWithinPeer, + const int64 time, const float scaleFactor) +{ + if (MouseInputSource* mouse = getOrCreateMouseInputSource (touchIndex)) + mouse->handleMagnifyGesture (this, positionWithinPeer, time, scaleFactor); +} + //============================================================================== void ComponentPeer::handlePaint (LowLevelGraphicsContext& contextToPaintTo) { diff --git a/modules/juce_gui_basics/windows/juce_ComponentPeer.h b/modules/juce_gui_basics/windows/juce_ComponentPeer.h index 76898b4e00..29d64dc426 100644 --- a/modules/juce_gui_basics/windows/juce_ComponentPeer.h +++ b/modules/juce_gui_basics/windows/juce_ComponentPeer.h @@ -315,6 +315,7 @@ public: //============================================================================== void handleMouseEvent (int touchIndex, const Point& positionWithinPeer, const ModifierKeys& newMods, int64 time); void handleMouseWheel (int touchIndex, const Point& positionWithinPeer, int64 time, const MouseWheelDetails&); + void handleMagnifyGesture (int touchIndex, const Point& positionWithinPeer, int64 time, float scaleFactor); void handleUserClosingWindow();