The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

589 lines
22KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce::detail
  19. {
  20. class MouseInputSourceImpl : private AsyncUpdater
  21. {
  22. public:
  23. using SH = ScalingHelpers;
  24. MouseInputSourceImpl (int i, MouseInputSource::InputSourceType type)
  25. : index (i),
  26. inputType (type)
  27. {}
  28. //==============================================================================
  29. bool isDragging() const noexcept
  30. {
  31. return buttonState.isAnyMouseButtonDown();
  32. }
  33. Component* getComponentUnderMouse() const noexcept
  34. {
  35. return componentUnderMouse.get();
  36. }
  37. ModifierKeys getCurrentModifiers() const noexcept
  38. {
  39. return ModifierKeys::currentModifiers
  40. .withoutMouseButtons()
  41. .withFlags (buttonState.getRawFlags());
  42. }
  43. ComponentPeer* getPeer() noexcept
  44. {
  45. if (! ComponentPeer::isValidPeer (lastPeer))
  46. lastPeer = nullptr;
  47. return lastPeer;
  48. }
  49. static Component* findComponentAt (Point<float> screenPos, ComponentPeer* peer)
  50. {
  51. if (! ComponentPeer::isValidPeer (peer))
  52. return nullptr;
  53. auto relativePos = SH::unscaledScreenPosToScaled (peer->getComponent(),
  54. peer->globalToLocal (screenPos));
  55. auto& comp = peer->getComponent();
  56. // (the contains() call is needed to test for overlapping desktop windows)
  57. if (comp.contains (relativePos))
  58. return comp.getComponentAt (relativePos);
  59. return nullptr;
  60. }
  61. Point<float> getScreenPosition() const noexcept
  62. {
  63. // This needs to return the live position if possible, but it mustn't update the lastScreenPos
  64. // value, because that can cause continuity problems.
  65. return SH::unscaledScreenPosToScaled (getRawScreenPosition());
  66. }
  67. Point<float> getRawScreenPosition() const noexcept
  68. {
  69. return unboundedMouseOffset + (inputType != MouseInputSource::InputSourceType::touch ? MouseInputSource::getCurrentRawMousePosition()
  70. : lastPointerState.position);
  71. }
  72. void setScreenPosition (Point<float> p)
  73. {
  74. MouseInputSource::setRawMousePosition (SH::scaledScreenPosToUnscaled (p));
  75. }
  76. //==============================================================================
  77. #if JUCE_DUMP_MOUSE_EVENTS
  78. #define JUCE_MOUSE_EVENT_DBG(desc, screenPos) DBG ("Mouse " << desc << " #" << index \
  79. << ": " << SH::screenPosToLocalPos (comp, screenPos).toString() \
  80. << " - Comp: " << String::toHexString ((pointer_sized_int) &comp));
  81. #else
  82. #define JUCE_MOUSE_EVENT_DBG(desc, screenPos)
  83. #endif
  84. void sendMouseEnter (Component& comp, const detail::PointerState& pointerState, Time time)
  85. {
  86. JUCE_MOUSE_EVENT_DBG ("enter", pointerState.position)
  87. comp.internalMouseEnter (MouseInputSource (this),
  88. SH::screenPosToLocalPos (comp, pointerState.position),
  89. time);
  90. }
  91. void sendMouseExit (Component& comp, const detail::PointerState& pointerState, Time time)
  92. {
  93. JUCE_MOUSE_EVENT_DBG ("exit", pointerState.position)
  94. comp.internalMouseExit (MouseInputSource (this),
  95. SH::screenPosToLocalPos (comp, pointerState.position),
  96. time);
  97. }
  98. void sendMouseMove (Component& comp, const detail::PointerState& pointerState, Time time)
  99. {
  100. JUCE_MOUSE_EVENT_DBG ("move", pointerState.position)
  101. comp.internalMouseMove (MouseInputSource (this),
  102. SH::screenPosToLocalPos (comp, pointerState.position),
  103. time);
  104. }
  105. void sendMouseDown (Component& comp, const detail::PointerState& pointerState, Time time)
  106. {
  107. JUCE_MOUSE_EVENT_DBG ("down", pointerState.position)
  108. comp.internalMouseDown (MouseInputSource (this),
  109. pointerState.withPosition (SH::screenPosToLocalPos (comp, pointerState.position)),
  110. time);
  111. }
  112. void sendMouseDrag (Component& comp, const detail::PointerState& pointerState, Time time)
  113. {
  114. JUCE_MOUSE_EVENT_DBG ("drag", pointerState.position)
  115. comp.internalMouseDrag (MouseInputSource (this),
  116. pointerState.withPosition (SH::screenPosToLocalPos (comp, pointerState.position)),
  117. time);
  118. }
  119. void sendMouseUp (Component& comp, const detail::PointerState& pointerState, Time time, ModifierKeys oldMods)
  120. {
  121. JUCE_MOUSE_EVENT_DBG ("up", pointerState.position)
  122. comp.internalMouseUp (MouseInputSource (this),
  123. pointerState.withPosition (SH::screenPosToLocalPos (comp, pointerState.position)),
  124. time,
  125. oldMods);
  126. }
  127. void sendMouseWheel (Component& comp, Point<float> screenPos, Time time, const MouseWheelDetails& wheel)
  128. {
  129. JUCE_MOUSE_EVENT_DBG ("wheel", screenPos)
  130. comp.internalMouseWheel (MouseInputSource (this),
  131. SH::screenPosToLocalPos (comp, screenPos),
  132. time,
  133. wheel);
  134. }
  135. void sendMagnifyGesture (Component& comp, Point<float> screenPos, Time time, float amount)
  136. {
  137. JUCE_MOUSE_EVENT_DBG ("magnify", screenPos)
  138. comp.internalMagnifyGesture (MouseInputSource (this),
  139. SH::screenPosToLocalPos (comp, screenPos),
  140. time,
  141. amount);
  142. }
  143. #undef JUCE_MOUSE_EVENT_DBG
  144. //==============================================================================
  145. // (returns true if the button change caused a modal event loop)
  146. bool setButtons (const detail::PointerState& pointerState, Time time, ModifierKeys newButtonState)
  147. {
  148. if (buttonState == newButtonState)
  149. return false;
  150. // (avoid sending a spurious mouse-drag when we receive a mouse-up)
  151. if (! (isDragging() && ! newButtonState.isAnyMouseButtonDown()))
  152. setPointerState (pointerState, time, false);
  153. // (ignore secondary clicks when there's already a button down)
  154. if (buttonState.isAnyMouseButtonDown() == newButtonState.isAnyMouseButtonDown())
  155. {
  156. buttonState = newButtonState;
  157. return false;
  158. }
  159. auto lastCounter = mouseEventCounter;
  160. if (buttonState.isAnyMouseButtonDown())
  161. {
  162. if (auto* current = getComponentUnderMouse())
  163. {
  164. auto oldMods = getCurrentModifiers();
  165. buttonState = newButtonState; // must change this before calling sendMouseUp, in case it runs a modal loop
  166. sendMouseUp (*current, pointerState.withPositionOffset (unboundedMouseOffset), time, oldMods);
  167. if (lastCounter != mouseEventCounter)
  168. return true; // if a modal loop happened, then newButtonState is no longer valid.
  169. }
  170. enableUnboundedMouseMovement (false, false);
  171. }
  172. buttonState = newButtonState;
  173. if (buttonState.isAnyMouseButtonDown())
  174. {
  175. Desktop::getInstance().incrementMouseClickCounter();
  176. if (auto* current = getComponentUnderMouse())
  177. {
  178. registerMouseDown (pointerState.position, time, *current, buttonState,
  179. inputType == MouseInputSource::InputSourceType::touch);
  180. sendMouseDown (*current, pointerState, time);
  181. }
  182. }
  183. return lastCounter != mouseEventCounter;
  184. }
  185. void setComponentUnderMouse (Component* newComponent, const detail::PointerState& pointerState, Time time)
  186. {
  187. auto* current = getComponentUnderMouse();
  188. if (newComponent != current)
  189. {
  190. WeakReference<Component> safeNewComp (newComponent);
  191. auto originalButtonState = buttonState;
  192. if (current != nullptr)
  193. {
  194. WeakReference<Component> safeOldComp (current);
  195. setButtons (pointerState, time, ModifierKeys());
  196. if (auto oldComp = safeOldComp.get())
  197. {
  198. componentUnderMouse = safeNewComp;
  199. sendMouseExit (*oldComp, pointerState, time);
  200. }
  201. buttonState = originalButtonState;
  202. }
  203. componentUnderMouse = safeNewComp.get();
  204. current = safeNewComp.get();
  205. if (current != nullptr)
  206. sendMouseEnter (*current, pointerState, time);
  207. revealCursor (false);
  208. setButtons (pointerState, time, originalButtonState);
  209. }
  210. }
  211. void setPeer (ComponentPeer& newPeer, const detail::PointerState& pointerState, Time time)
  212. {
  213. if (&newPeer != lastPeer && ( findComponentAt (pointerState.position, &newPeer) != nullptr
  214. || findComponentAt (pointerState.position, lastPeer) == nullptr))
  215. {
  216. setComponentUnderMouse (nullptr, pointerState, time);
  217. lastPeer = &newPeer;
  218. setComponentUnderMouse (findComponentAt (pointerState.position, getPeer()), pointerState, time);
  219. }
  220. }
  221. void setPointerState (const detail::PointerState& newPointerState, Time time, bool forceUpdate)
  222. {
  223. const auto& newScreenPos = newPointerState.position;
  224. if (! isDragging())
  225. setComponentUnderMouse (findComponentAt (newScreenPos, getPeer()), newPointerState, time);
  226. if ((newPointerState != lastPointerState) || forceUpdate)
  227. {
  228. cancelPendingUpdate();
  229. lastPointerState = newPointerState;
  230. if (auto* current = getComponentUnderMouse())
  231. {
  232. if (isDragging())
  233. {
  234. registerMouseDrag (newScreenPos);
  235. sendMouseDrag (*current, newPointerState.withPositionOffset (unboundedMouseOffset), time);
  236. if (isUnboundedMouseModeOn)
  237. handleUnboundedDrag (*current);
  238. }
  239. else
  240. {
  241. sendMouseMove (*current, newPointerState, time);
  242. }
  243. }
  244. revealCursor (false);
  245. }
  246. }
  247. //==============================================================================
  248. void handleEvent (ComponentPeer& newPeer, Point<float> positionWithinPeer, Time time,
  249. const ModifierKeys newMods, float newPressure, float newOrientation, PenDetails pen)
  250. {
  251. lastTime = time;
  252. ++mouseEventCounter;
  253. const auto pointerState = detail::PointerState().withPosition (newPeer.localToGlobal (positionWithinPeer))
  254. .withPressure (newPressure)
  255. .withOrientation (newOrientation)
  256. .withRotation (MouseInputSource::defaultRotation)
  257. .withTiltX (pen.tiltX)
  258. .withTiltY (pen.tiltY);
  259. if (isDragging() && newMods.isAnyMouseButtonDown())
  260. {
  261. setPointerState (pointerState, time, false);
  262. }
  263. else
  264. {
  265. setPeer (newPeer, pointerState, time);
  266. if (auto* peer = getPeer())
  267. {
  268. if (setButtons (pointerState, time, newMods))
  269. return; // some modal events have been dispatched, so the current event is now out-of-date
  270. peer = getPeer();
  271. if (peer != nullptr)
  272. setPointerState (pointerState, time, false);
  273. }
  274. }
  275. }
  276. Component* getTargetForGesture (ComponentPeer& peer, Point<float> positionWithinPeer,
  277. Time time, Point<float>& screenPos)
  278. {
  279. lastTime = time;
  280. ++mouseEventCounter;
  281. screenPos = peer.localToGlobal (positionWithinPeer);
  282. const auto pointerState = lastPointerState.withPosition (screenPos);
  283. setPeer (peer, pointerState, time);
  284. setPointerState (pointerState, time, false);
  285. triggerFakeMove();
  286. return getComponentUnderMouse();
  287. }
  288. void handleWheel (ComponentPeer& peer, Point<float> positionWithinPeer,
  289. Time time, const MouseWheelDetails& wheel)
  290. {
  291. Desktop::getInstance().incrementMouseWheelCounter();
  292. Point<float> screenPos;
  293. // This will make sure that when the wheel spins in its inertial phase, any events
  294. // continue to be sent to the last component that the mouse was over when it was being
  295. // actively controlled by the user. This avoids confusion when scrolling through nested
  296. // scrollable components.
  297. if (lastNonInertialWheelTarget == nullptr || ! wheel.isInertial)
  298. lastNonInertialWheelTarget = getTargetForGesture (peer, positionWithinPeer, time, screenPos);
  299. else
  300. screenPos = peer.localToGlobal (positionWithinPeer);
  301. if (auto target = lastNonInertialWheelTarget.get())
  302. sendMouseWheel (*target, screenPos, time, wheel);
  303. }
  304. void handleMagnifyGesture (ComponentPeer& peer, Point<float> positionWithinPeer,
  305. Time time, const float scaleFactor)
  306. {
  307. Point<float> screenPos;
  308. if (auto* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos))
  309. sendMagnifyGesture (*current, screenPos, time, scaleFactor);
  310. }
  311. //==============================================================================
  312. Time getLastMouseDownTime() const noexcept
  313. {
  314. return mouseDowns[0].time;
  315. }
  316. Point<float> getLastMouseDownPosition() const noexcept
  317. {
  318. return SH::unscaledScreenPosToScaled (mouseDowns[0].position);
  319. }
  320. int getNumberOfMultipleClicks() const noexcept
  321. {
  322. int numClicks = 1;
  323. if (! isLongPressOrDrag())
  324. {
  325. for (int i = 1; i < numElementsInArray (mouseDowns); ++i)
  326. {
  327. if (mouseDowns[0].canBePartOfMultipleClickWith (mouseDowns[i], MouseEvent::getDoubleClickTimeout() * jmin (i, 2)))
  328. ++numClicks;
  329. else
  330. break;
  331. }
  332. }
  333. return numClicks;
  334. }
  335. bool isLongPressOrDrag() const noexcept
  336. {
  337. return movedSignificantly ||
  338. lastTime > (mouseDowns[0].time + RelativeTime::milliseconds (300));
  339. }
  340. bool hasMovedSignificantlySincePressed() const noexcept
  341. {
  342. return movedSignificantly;
  343. }
  344. // Deprecated method
  345. bool hasMouseMovedSignificantlySincePressed() const noexcept
  346. {
  347. return isLongPressOrDrag();
  348. }
  349. //==============================================================================
  350. void triggerFakeMove()
  351. {
  352. triggerAsyncUpdate();
  353. }
  354. void handleAsyncUpdate() override
  355. {
  356. setPointerState (lastPointerState,
  357. jmax (lastTime, Time::getCurrentTime()), true);
  358. }
  359. //==============================================================================
  360. void enableUnboundedMouseMovement (bool enable, bool keepCursorVisibleUntilOffscreen)
  361. {
  362. enable = enable && isDragging();
  363. isCursorVisibleUntilOffscreen = keepCursorVisibleUntilOffscreen;
  364. if (enable != isUnboundedMouseModeOn)
  365. {
  366. if ((! enable) && ((! isCursorVisibleUntilOffscreen) || ! unboundedMouseOffset.isOrigin()))
  367. {
  368. // when released, return the mouse to within the component's bounds
  369. if (auto* current = getComponentUnderMouse())
  370. setScreenPosition (current->getScreenBounds().toFloat()
  371. .getConstrainedPoint (SH::unscaledScreenPosToScaled (lastPointerState.position)));
  372. }
  373. isUnboundedMouseModeOn = enable;
  374. unboundedMouseOffset = {};
  375. revealCursor (true);
  376. }
  377. }
  378. void handleUnboundedDrag (Component& current)
  379. {
  380. auto componentScreenBounds = SH::scaledScreenPosToUnscaled (current.getParentMonitorArea()
  381. .reduced (2, 2)
  382. .toFloat());
  383. if (! componentScreenBounds.contains (lastPointerState.position))
  384. {
  385. auto componentCentre = current.getScreenBounds().toFloat().getCentre();
  386. unboundedMouseOffset += (lastPointerState.position - SH::scaledScreenPosToUnscaled (componentCentre));
  387. setScreenPosition (componentCentre);
  388. }
  389. else if (isCursorVisibleUntilOffscreen
  390. && (! unboundedMouseOffset.isOrigin())
  391. && componentScreenBounds.contains (lastPointerState.position + unboundedMouseOffset))
  392. {
  393. MouseInputSource::setRawMousePosition (lastPointerState.position + unboundedMouseOffset);
  394. unboundedMouseOffset = {};
  395. }
  396. }
  397. //==============================================================================
  398. void showMouseCursor (MouseCursor cursor, bool forcedUpdate)
  399. {
  400. if (isUnboundedMouseModeOn && ((! unboundedMouseOffset.isOrigin()) || ! isCursorVisibleUntilOffscreen))
  401. {
  402. cursor = MouseCursor::NoCursor;
  403. forcedUpdate = true;
  404. }
  405. if (forcedUpdate || cursor.getHandle() != currentCursorHandle)
  406. {
  407. currentCursorHandle = cursor.getHandle();
  408. cursor.showInWindow (getPeer());
  409. }
  410. }
  411. void hideCursor()
  412. {
  413. showMouseCursor (MouseCursor::NoCursor, true);
  414. }
  415. void revealCursor (bool forcedUpdate)
  416. {
  417. MouseCursor mc (MouseCursor::NormalCursor);
  418. if (auto* current = getComponentUnderMouse())
  419. mc = current->getLookAndFeel().getMouseCursorFor (*current);
  420. showMouseCursor (mc, forcedUpdate);
  421. }
  422. //==============================================================================
  423. const int index;
  424. const MouseInputSource::InputSourceType inputType;
  425. Point<float> unboundedMouseOffset; // NB: these are unscaled coords
  426. detail::PointerState lastPointerState;
  427. ModifierKeys buttonState;
  428. bool isUnboundedMouseModeOn = false, isCursorVisibleUntilOffscreen = false;
  429. private:
  430. WeakReference<Component> componentUnderMouse, lastNonInertialWheelTarget;
  431. ComponentPeer* lastPeer = nullptr;
  432. void* currentCursorHandle = nullptr;
  433. int mouseEventCounter = 0;
  434. struct RecentMouseDown
  435. {
  436. RecentMouseDown() = default;
  437. Point<float> position;
  438. Time time;
  439. ModifierKeys buttons;
  440. uint32 peerID = 0;
  441. bool isTouch = false;
  442. bool canBePartOfMultipleClickWith (const RecentMouseDown& other, int maxTimeBetweenMs) const noexcept
  443. {
  444. return time - other.time < RelativeTime::milliseconds (maxTimeBetweenMs)
  445. && std::abs (position.x - other.position.x) < (float) getPositionToleranceForInputType()
  446. && std::abs (position.y - other.position.y) < (float) getPositionToleranceForInputType()
  447. && buttons == other.buttons
  448. && peerID == other.peerID;
  449. }
  450. int getPositionToleranceForInputType() const noexcept { return isTouch ? 25 : 8; }
  451. };
  452. RecentMouseDown mouseDowns[4];
  453. Time lastTime;
  454. bool movedSignificantly = false;
  455. void registerMouseDown (Point<float> screenPos, Time time, Component& component,
  456. const ModifierKeys modifiers, bool isTouchSource) noexcept
  457. {
  458. for (int i = numElementsInArray (mouseDowns); --i > 0;)
  459. mouseDowns[i] = mouseDowns[i - 1];
  460. mouseDowns[0].position = screenPos;
  461. mouseDowns[0].time = time;
  462. mouseDowns[0].buttons = modifiers.withOnlyMouseButtons();
  463. mouseDowns[0].isTouch = isTouchSource;
  464. if (auto* peer = component.getPeer())
  465. mouseDowns[0].peerID = peer->getUniqueID();
  466. else
  467. mouseDowns[0].peerID = 0;
  468. movedSignificantly = false;
  469. lastNonInertialWheelTarget = nullptr;
  470. }
  471. void registerMouseDrag (Point<float> screenPos) noexcept
  472. {
  473. movedSignificantly = movedSignificantly || mouseDowns[0].position.getDistanceFrom (screenPos) >= 4;
  474. }
  475. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MouseInputSourceImpl)
  476. };
  477. } // namespace juce::detail