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.

775 lines
30KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For this technical preview, this file is not subject to commercial licensing.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. class MouseInputSourceInternal : private AsyncUpdater
  16. {
  17. public:
  18. MouseInputSourceInternal (int i, MouseInputSource::InputSourceType type) : index (i), inputType (type)
  19. {
  20. }
  21. //==============================================================================
  22. bool isDragging() const noexcept
  23. {
  24. return buttonState.isAnyMouseButtonDown();
  25. }
  26. Component* getComponentUnderMouse() const noexcept
  27. {
  28. return componentUnderMouse.get();
  29. }
  30. ModifierKeys getCurrentModifiers() const noexcept
  31. {
  32. return ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (buttonState.getRawFlags());
  33. }
  34. ComponentPeer* getPeer() noexcept
  35. {
  36. if (! ComponentPeer::isValidPeer (lastPeer))
  37. lastPeer = nullptr;
  38. return lastPeer;
  39. }
  40. static Point<float> screenPosToLocalPos (Component& comp, Point<float> pos)
  41. {
  42. if (auto* peer = comp.getPeer())
  43. {
  44. pos = peer->globalToLocal (pos);
  45. auto& peerComp = peer->getComponent();
  46. return comp.getLocalPoint (&peerComp, ScalingHelpers::unscaledScreenPosToScaled (peerComp, pos));
  47. }
  48. return comp.getLocalPoint (nullptr, ScalingHelpers::unscaledScreenPosToScaled (comp, pos));
  49. }
  50. Component* findComponentAt (Point<float> screenPos)
  51. {
  52. if (auto* peer = getPeer())
  53. {
  54. auto relativePos = ScalingHelpers::unscaledScreenPosToScaled (peer->getComponent(),
  55. peer->globalToLocal (screenPos));
  56. auto& comp = peer->getComponent();
  57. auto pos = relativePos.roundToInt();
  58. // (the contains() call is needed to test for overlapping desktop windows)
  59. if (comp.contains (pos))
  60. return comp.getComponentAt (pos);
  61. }
  62. return nullptr;
  63. }
  64. Point<float> getScreenPosition() const noexcept
  65. {
  66. // This needs to return the live position if possible, but it mustn't update the lastScreenPos
  67. // value, because that can cause continuity problems.
  68. return ScalingHelpers::unscaledScreenPosToScaled (getRawScreenPosition());
  69. }
  70. Point<float> getRawScreenPosition() const noexcept
  71. {
  72. return unboundedMouseOffset + (inputType != MouseInputSource::InputSourceType::touch ? MouseInputSource::getCurrentRawMousePosition()
  73. : lastScreenPos);
  74. }
  75. void setScreenPosition (Point<float> p)
  76. {
  77. MouseInputSource::setRawMousePosition (ScalingHelpers::scaledScreenPosToUnscaled (p));
  78. }
  79. bool isPressureValid() const noexcept { return pressure >= 0.0f && pressure <= 1.0f; }
  80. bool isOrientationValid() const noexcept { return orientation >= 0.0f && orientation <= MathConstants<float>::twoPi; }
  81. bool isRotationValid() const noexcept { return rotation >= 0.0f && rotation <= MathConstants<float>::twoPi; }
  82. bool isTiltValid (bool isX) const noexcept { return isX ? (tiltX >= -1.0f && tiltX <= 1.0f) : (tiltY >= -1.0f && tiltY <= 1.0f); }
  83. //==============================================================================
  84. #if JUCE_DUMP_MOUSE_EVENTS
  85. #define JUCE_MOUSE_EVENT_DBG(desc) DBG ("Mouse " << desc << " #" << index \
  86. << ": " << screenPosToLocalPos (comp, screenPos).toString() \
  87. << " - Comp: " << String::toHexString ((pointer_sized_int) &comp));
  88. #else
  89. #define JUCE_MOUSE_EVENT_DBG(desc)
  90. #endif
  91. void sendMouseEnter (Component& comp, Point<float> screenPos, Time time)
  92. {
  93. JUCE_MOUSE_EVENT_DBG ("enter")
  94. comp.internalMouseEnter (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time);
  95. }
  96. void sendMouseExit (Component& comp, Point<float> screenPos, Time time)
  97. {
  98. JUCE_MOUSE_EVENT_DBG ("exit")
  99. comp.internalMouseExit (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time);
  100. }
  101. void sendMouseMove (Component& comp, Point<float> screenPos, Time time)
  102. {
  103. JUCE_MOUSE_EVENT_DBG ("move")
  104. comp.internalMouseMove (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time);
  105. }
  106. void sendMouseDown (Component& comp, Point<float> screenPos, Time time)
  107. {
  108. JUCE_MOUSE_EVENT_DBG ("down")
  109. comp.internalMouseDown (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, pressure, orientation, rotation, tiltX, tiltY);
  110. }
  111. void sendMouseDrag (Component& comp, Point<float> screenPos, Time time)
  112. {
  113. JUCE_MOUSE_EVENT_DBG ("drag")
  114. comp.internalMouseDrag (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, pressure, orientation, rotation, tiltX, tiltY);
  115. }
  116. void sendMouseUp (Component& comp, Point<float> screenPos, Time time, ModifierKeys oldMods)
  117. {
  118. JUCE_MOUSE_EVENT_DBG ("up")
  119. comp.internalMouseUp (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, oldMods, pressure, orientation, rotation, tiltX, tiltY);
  120. }
  121. void sendMouseWheel (Component& comp, Point<float> screenPos, Time time, const MouseWheelDetails& wheel)
  122. {
  123. JUCE_MOUSE_EVENT_DBG ("wheel")
  124. comp.internalMouseWheel (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, wheel);
  125. }
  126. void sendMagnifyGesture (Component& comp, Point<float> screenPos, Time time, float amount)
  127. {
  128. JUCE_MOUSE_EVENT_DBG ("magnify")
  129. comp.internalMagnifyGesture (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, amount);
  130. }
  131. //==============================================================================
  132. // (returns true if the button change caused a modal event loop)
  133. bool setButtons (Point<float> screenPos, Time time, ModifierKeys newButtonState)
  134. {
  135. if (buttonState == newButtonState)
  136. return false;
  137. // (avoid sending a spurious mouse-drag when we receive a mouse-up)
  138. if (! (isDragging() && ! newButtonState.isAnyMouseButtonDown()))
  139. setScreenPos (screenPos, time, false);
  140. // (ignore secondary clicks when there's already a button down)
  141. if (buttonState.isAnyMouseButtonDown() == newButtonState.isAnyMouseButtonDown())
  142. {
  143. buttonState = newButtonState;
  144. return false;
  145. }
  146. auto lastCounter = mouseEventCounter;
  147. if (buttonState.isAnyMouseButtonDown())
  148. {
  149. if (auto* current = getComponentUnderMouse())
  150. {
  151. auto oldMods = getCurrentModifiers();
  152. buttonState = newButtonState; // must change this before calling sendMouseUp, in case it runs a modal loop
  153. sendMouseUp (*current, screenPos + unboundedMouseOffset, time, oldMods);
  154. if (lastCounter != mouseEventCounter)
  155. return true; // if a modal loop happened, then newButtonState is no longer valid.
  156. }
  157. enableUnboundedMouseMovement (false, false);
  158. }
  159. buttonState = newButtonState;
  160. if (buttonState.isAnyMouseButtonDown())
  161. {
  162. Desktop::getInstance().incrementMouseClickCounter();
  163. if (auto* current = getComponentUnderMouse())
  164. {
  165. registerMouseDown (screenPos, time, *current, buttonState,
  166. inputType == MouseInputSource::InputSourceType::touch);
  167. sendMouseDown (*current, screenPos, time);
  168. }
  169. }
  170. return lastCounter != mouseEventCounter;
  171. }
  172. void setComponentUnderMouse (Component* newComponent, Point<float> screenPos, Time time)
  173. {
  174. auto* current = getComponentUnderMouse();
  175. if (newComponent != current)
  176. {
  177. WeakReference<Component> safeNewComp (newComponent);
  178. auto originalButtonState = buttonState;
  179. if (current != nullptr)
  180. {
  181. WeakReference<Component> safeOldComp (current);
  182. setButtons (screenPos, time, ModifierKeys());
  183. if (auto oldComp = safeOldComp.get())
  184. {
  185. componentUnderMouse = safeNewComp;
  186. sendMouseExit (*oldComp, screenPos, time);
  187. }
  188. buttonState = originalButtonState;
  189. }
  190. componentUnderMouse = safeNewComp.get();
  191. current = safeNewComp.get();
  192. if (current != nullptr)
  193. sendMouseEnter (*current, screenPos, time);
  194. revealCursor (false);
  195. setButtons (screenPos, time, originalButtonState);
  196. }
  197. }
  198. void setPeer (ComponentPeer& newPeer, Point<float> screenPos, Time time)
  199. {
  200. if (&newPeer != lastPeer)
  201. {
  202. setComponentUnderMouse (nullptr, screenPos, time);
  203. lastPeer = &newPeer;
  204. setComponentUnderMouse (findComponentAt (screenPos), screenPos, time);
  205. }
  206. }
  207. void setScreenPos (Point<float> newScreenPos, Time time, bool forceUpdate)
  208. {
  209. if (! isDragging())
  210. setComponentUnderMouse (findComponentAt (newScreenPos), newScreenPos, time);
  211. if (newScreenPos != lastScreenPos || forceUpdate)
  212. {
  213. cancelPendingUpdate();
  214. if (newScreenPos != MouseInputSource::offscreenMousePos)
  215. lastScreenPos = newScreenPos;
  216. if (auto* current = getComponentUnderMouse())
  217. {
  218. if (isDragging())
  219. {
  220. registerMouseDrag (newScreenPos);
  221. sendMouseDrag (*current, newScreenPos + unboundedMouseOffset, time);
  222. if (isUnboundedMouseModeOn)
  223. handleUnboundedDrag (*current);
  224. }
  225. else
  226. {
  227. sendMouseMove (*current, newScreenPos, time);
  228. }
  229. }
  230. revealCursor (false);
  231. }
  232. }
  233. //==============================================================================
  234. void handleEvent (ComponentPeer& newPeer, Point<float> positionWithinPeer, Time time,
  235. const ModifierKeys newMods, float newPressure, float newOrientation, PenDetails pen)
  236. {
  237. lastTime = time;
  238. const bool pressureChanged = (pressure != newPressure);
  239. pressure = newPressure;
  240. const bool orientationChanged = (orientation != newOrientation);
  241. orientation = newOrientation;
  242. const bool rotationChanged = (rotation != pen.rotation);
  243. rotation = pen.rotation;
  244. const bool tiltChanged = (tiltX != pen.tiltX || tiltY != pen.tiltY);
  245. tiltX = pen.tiltX;
  246. tiltY = pen.tiltY;
  247. const bool shouldUpdate = (pressureChanged || orientationChanged || rotationChanged || tiltChanged);
  248. ++mouseEventCounter;
  249. auto screenPos = newPeer.localToGlobal (positionWithinPeer);
  250. if (isDragging() && newMods.isAnyMouseButtonDown())
  251. {
  252. setScreenPos (screenPos, time, shouldUpdate);
  253. }
  254. else
  255. {
  256. setPeer (newPeer, screenPos, time);
  257. if (auto* peer = getPeer())
  258. {
  259. if (setButtons (screenPos, time, newMods))
  260. return; // some modal events have been dispatched, so the current event is now out-of-date
  261. peer = getPeer();
  262. if (peer != nullptr)
  263. setScreenPos (screenPos, time, shouldUpdate);
  264. }
  265. }
  266. }
  267. Component* getTargetForGesture (ComponentPeer& peer, Point<float> positionWithinPeer,
  268. Time time, Point<float>& screenPos)
  269. {
  270. lastTime = time;
  271. ++mouseEventCounter;
  272. screenPos = peer.localToGlobal (positionWithinPeer);
  273. setPeer (peer, screenPos, time);
  274. setScreenPos (screenPos, time, false);
  275. triggerFakeMove();
  276. return getComponentUnderMouse();
  277. }
  278. void handleWheel (ComponentPeer& peer, Point<float> positionWithinPeer,
  279. Time time, const MouseWheelDetails& wheel)
  280. {
  281. Desktop::getInstance().incrementMouseWheelCounter();
  282. Point<float> screenPos;
  283. // This will make sure that when the wheel spins in its inertial phase, any events
  284. // continue to be sent to the last component that the mouse was over when it was being
  285. // actively controlled by the user. This avoids confusion when scrolling through nested
  286. // scrollable components.
  287. if (lastNonInertialWheelTarget == nullptr || ! wheel.isInertial)
  288. lastNonInertialWheelTarget = getTargetForGesture (peer, positionWithinPeer, time, screenPos);
  289. else
  290. screenPos = peer.localToGlobal (positionWithinPeer);
  291. if (auto target = lastNonInertialWheelTarget.get())
  292. sendMouseWheel (*target, screenPos, time, wheel);
  293. }
  294. void handleMagnifyGesture (ComponentPeer& peer, Point<float> positionWithinPeer,
  295. Time time, const float scaleFactor)
  296. {
  297. Point<float> screenPos;
  298. if (auto* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos))
  299. sendMagnifyGesture (*current, screenPos, time, scaleFactor);
  300. }
  301. //==============================================================================
  302. Time getLastMouseDownTime() const noexcept { return mouseDowns[0].time; }
  303. Point<float> getLastMouseDownPosition() const noexcept { return ScalingHelpers::unscaledScreenPosToScaled (mouseDowns[0].position); }
  304. int getNumberOfMultipleClicks() const noexcept
  305. {
  306. int numClicks = 1;
  307. if (! isLongPressOrDrag())
  308. {
  309. for (int i = 1; i < numElementsInArray (mouseDowns); ++i)
  310. {
  311. if (mouseDowns[0].canBePartOfMultipleClickWith (mouseDowns[i], MouseEvent::getDoubleClickTimeout() * jmin (i, 2)))
  312. ++numClicks;
  313. else
  314. break;
  315. }
  316. }
  317. return numClicks;
  318. }
  319. bool isLongPressOrDrag() const noexcept
  320. {
  321. return movedSignificantly || lastTime > mouseDowns[0].time + RelativeTime::milliseconds (300);
  322. }
  323. bool hasMovedSignificantlySincePressed() const noexcept
  324. {
  325. return movedSignificantly;
  326. }
  327. // Deprecated method
  328. bool hasMouseMovedSignificantlySincePressed() const noexcept
  329. {
  330. return isLongPressOrDrag();
  331. }
  332. //==============================================================================
  333. void triggerFakeMove()
  334. {
  335. triggerAsyncUpdate();
  336. }
  337. void handleAsyncUpdate() override
  338. {
  339. setScreenPos (lastScreenPos, jmax (lastTime, Time::getCurrentTime()), true);
  340. }
  341. //==============================================================================
  342. void enableUnboundedMouseMovement (bool enable, bool keepCursorVisibleUntilOffscreen)
  343. {
  344. enable = enable && isDragging();
  345. isCursorVisibleUntilOffscreen = keepCursorVisibleUntilOffscreen;
  346. if (enable != isUnboundedMouseModeOn)
  347. {
  348. if ((! enable) && ((! isCursorVisibleUntilOffscreen) || ! unboundedMouseOffset.isOrigin()))
  349. {
  350. // when released, return the mouse to within the component's bounds
  351. if (auto* current = getComponentUnderMouse())
  352. setScreenPosition (current->getScreenBounds().toFloat()
  353. .getConstrainedPoint (ScalingHelpers::unscaledScreenPosToScaled (lastScreenPos)));
  354. }
  355. isUnboundedMouseModeOn = enable;
  356. unboundedMouseOffset = {};
  357. revealCursor (true);
  358. }
  359. }
  360. void handleUnboundedDrag (Component& current)
  361. {
  362. auto componentScreenBounds = ScalingHelpers::scaledScreenPosToUnscaled (current.getParentMonitorArea().reduced (2, 2).toFloat());
  363. if (! componentScreenBounds.contains (lastScreenPos))
  364. {
  365. auto componentCentre = current.getScreenBounds().toFloat().getCentre();
  366. unboundedMouseOffset += (lastScreenPos - ScalingHelpers::scaledScreenPosToUnscaled (componentCentre));
  367. setScreenPosition (componentCentre);
  368. }
  369. else if (isCursorVisibleUntilOffscreen
  370. && (! unboundedMouseOffset.isOrigin())
  371. && componentScreenBounds.contains (lastScreenPos + unboundedMouseOffset))
  372. {
  373. MouseInputSource::setRawMousePosition (lastScreenPos + unboundedMouseOffset);
  374. unboundedMouseOffset = {};
  375. }
  376. }
  377. //==============================================================================
  378. void showMouseCursor (MouseCursor cursor, bool forcedUpdate)
  379. {
  380. if (isUnboundedMouseModeOn && ((! unboundedMouseOffset.isOrigin()) || ! isCursorVisibleUntilOffscreen))
  381. {
  382. cursor = MouseCursor::NoCursor;
  383. forcedUpdate = true;
  384. }
  385. if (forcedUpdate || cursor.getHandle() != currentCursorHandle)
  386. {
  387. currentCursorHandle = cursor.getHandle();
  388. cursor.showInWindow (getPeer());
  389. }
  390. }
  391. void hideCursor()
  392. {
  393. showMouseCursor (MouseCursor::NoCursor, true);
  394. }
  395. void revealCursor (bool forcedUpdate)
  396. {
  397. MouseCursor mc (MouseCursor::NormalCursor);
  398. if (auto* current = getComponentUnderMouse())
  399. mc = current->getLookAndFeel().getMouseCursorFor (*current);
  400. showMouseCursor (mc, forcedUpdate);
  401. }
  402. //==============================================================================
  403. const int index;
  404. const MouseInputSource::InputSourceType inputType;
  405. Point<float> lastScreenPos, unboundedMouseOffset; // NB: these are unscaled coords
  406. ModifierKeys buttonState;
  407. float pressure = 0;
  408. float orientation = 0;
  409. float rotation = 0;
  410. float tiltX = 0;
  411. float tiltY = 0;
  412. bool isUnboundedMouseModeOn = false, isCursorVisibleUntilOffscreen = false;
  413. private:
  414. WeakReference<Component> componentUnderMouse, lastNonInertialWheelTarget;
  415. ComponentPeer* lastPeer = nullptr;
  416. void* currentCursorHandle = nullptr;
  417. int mouseEventCounter = 0;
  418. struct RecentMouseDown
  419. {
  420. RecentMouseDown() = default;
  421. Point<float> position;
  422. Time time;
  423. ModifierKeys buttons;
  424. uint32 peerID = 0;
  425. bool isTouch = false;
  426. bool canBePartOfMultipleClickWith (const RecentMouseDown& other, int maxTimeBetweenMs) const noexcept
  427. {
  428. return time - other.time < RelativeTime::milliseconds (maxTimeBetweenMs)
  429. && std::abs (position.x - other.position.x) < getPositionToleranceForInputType()
  430. && std::abs (position.y - other.position.y) < getPositionToleranceForInputType()
  431. && buttons == other.buttons
  432. && peerID == other.peerID;
  433. }
  434. int getPositionToleranceForInputType() const noexcept { return isTouch ? 25 : 8; }
  435. };
  436. RecentMouseDown mouseDowns[4];
  437. Time lastTime;
  438. bool movedSignificantly = false;
  439. void registerMouseDown (Point<float> screenPos, Time time, Component& component,
  440. const ModifierKeys modifiers, bool isTouchSource) noexcept
  441. {
  442. for (int i = numElementsInArray (mouseDowns); --i > 0;)
  443. mouseDowns[i] = mouseDowns[i - 1];
  444. mouseDowns[0].position = screenPos;
  445. mouseDowns[0].time = time;
  446. mouseDowns[0].buttons = modifiers.withOnlyMouseButtons();
  447. mouseDowns[0].isTouch = isTouchSource;
  448. if (auto* peer = component.getPeer())
  449. mouseDowns[0].peerID = peer->getUniqueID();
  450. else
  451. mouseDowns[0].peerID = 0;
  452. movedSignificantly = false;
  453. lastNonInertialWheelTarget = nullptr;
  454. }
  455. void registerMouseDrag (Point<float> screenPos) noexcept
  456. {
  457. movedSignificantly = movedSignificantly || mouseDowns[0].position.getDistanceFrom (screenPos) >= 4;
  458. }
  459. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MouseInputSourceInternal)
  460. };
  461. //==============================================================================
  462. MouseInputSource::MouseInputSource (MouseInputSourceInternal* s) noexcept : pimpl (s) {}
  463. MouseInputSource::MouseInputSource (const MouseInputSource& other) noexcept : pimpl (other.pimpl) {}
  464. MouseInputSource::~MouseInputSource() noexcept {}
  465. MouseInputSource& MouseInputSource::operator= (const MouseInputSource& other) noexcept
  466. {
  467. pimpl = other.pimpl;
  468. return *this;
  469. }
  470. MouseInputSource::InputSourceType MouseInputSource::getType() const noexcept { return pimpl->inputType; }
  471. bool MouseInputSource::isMouse() const noexcept { return (getType() == MouseInputSource::InputSourceType::mouse); }
  472. bool MouseInputSource::isTouch() const noexcept { return (getType() == MouseInputSource::InputSourceType::touch); }
  473. bool MouseInputSource::isPen() const noexcept { return (getType() == MouseInputSource::InputSourceType::pen); }
  474. bool MouseInputSource::canHover() const noexcept { return ! isTouch(); }
  475. bool MouseInputSource::hasMouseWheel() const noexcept { return ! isTouch(); }
  476. int MouseInputSource::getIndex() const noexcept { return pimpl->index; }
  477. bool MouseInputSource::isDragging() const noexcept { return pimpl->isDragging(); }
  478. Point<float> MouseInputSource::getScreenPosition() const noexcept { return pimpl->getScreenPosition(); }
  479. Point<float> MouseInputSource::getRawScreenPosition() const noexcept { return pimpl->getRawScreenPosition(); }
  480. ModifierKeys MouseInputSource::getCurrentModifiers() const noexcept { return pimpl->getCurrentModifiers(); }
  481. float MouseInputSource::getCurrentPressure() const noexcept { return pimpl->pressure; }
  482. bool MouseInputSource::isPressureValid() const noexcept { return pimpl->isPressureValid(); }
  483. float MouseInputSource::getCurrentOrientation() const noexcept { return pimpl->orientation; }
  484. bool MouseInputSource::isOrientationValid() const noexcept { return pimpl->isOrientationValid(); }
  485. float MouseInputSource::getCurrentRotation() const noexcept { return pimpl->rotation; }
  486. bool MouseInputSource::isRotationValid() const noexcept { return pimpl->isRotationValid(); }
  487. float MouseInputSource::getCurrentTilt (bool tiltX) const noexcept { return tiltX ? pimpl->tiltX : pimpl->tiltY; }
  488. bool MouseInputSource::isTiltValid (bool isX) const noexcept { return pimpl->isTiltValid (isX); }
  489. Component* MouseInputSource::getComponentUnderMouse() const { return pimpl->getComponentUnderMouse(); }
  490. void MouseInputSource::triggerFakeMove() const { pimpl->triggerFakeMove(); }
  491. int MouseInputSource::getNumberOfMultipleClicks() const noexcept { return pimpl->getNumberOfMultipleClicks(); }
  492. Time MouseInputSource::getLastMouseDownTime() const noexcept { return pimpl->getLastMouseDownTime(); }
  493. Point<float> MouseInputSource::getLastMouseDownPosition() const noexcept { return pimpl->getLastMouseDownPosition(); }
  494. bool MouseInputSource::isLongPressOrDrag() const noexcept { return pimpl->isLongPressOrDrag(); }
  495. bool MouseInputSource::hasMovedSignificantlySincePressed() const noexcept { return pimpl->hasMovedSignificantlySincePressed(); }
  496. bool MouseInputSource::canDoUnboundedMovement() const noexcept { return ! isTouch(); }
  497. void MouseInputSource::enableUnboundedMouseMovement (bool isEnabled, bool keepCursorVisibleUntilOffscreen) const
  498. { pimpl->enableUnboundedMouseMovement (isEnabled, keepCursorVisibleUntilOffscreen); }
  499. bool MouseInputSource::isUnboundedMouseMovementEnabled() const { return pimpl->isUnboundedMouseModeOn; }
  500. bool MouseInputSource::hasMouseCursor() const noexcept { return ! isTouch(); }
  501. void MouseInputSource::showMouseCursor (const MouseCursor& cursor) { pimpl->showMouseCursor (cursor, false); }
  502. void MouseInputSource::hideCursor() { pimpl->hideCursor(); }
  503. void MouseInputSource::revealCursor() { pimpl->revealCursor (false); }
  504. void MouseInputSource::forceMouseCursorUpdate() { pimpl->revealCursor (true); }
  505. void MouseInputSource::setScreenPosition (Point<float> p) { pimpl->setScreenPosition (p); }
  506. void MouseInputSource::handleEvent (ComponentPeer& peer, Point<float> pos, int64 time, ModifierKeys mods,
  507. float pressure, float orientation, const PenDetails& penDetails)
  508. {
  509. pimpl->handleEvent (peer, pos, Time (time), mods.withOnlyMouseButtons(), pressure, orientation, penDetails);
  510. }
  511. void MouseInputSource::handleWheel (ComponentPeer& peer, Point<float> pos, int64 time, const MouseWheelDetails& wheel)
  512. {
  513. pimpl->handleWheel (peer, pos, Time (time), wheel);
  514. }
  515. void MouseInputSource::handleMagnifyGesture (ComponentPeer& peer, Point<float> pos, int64 time, float scaleFactor)
  516. {
  517. pimpl->handleMagnifyGesture (peer, pos, Time (time), scaleFactor);
  518. }
  519. const float MouseInputSource::invalidPressure = 0.0f;
  520. const float MouseInputSource::invalidOrientation = 0.0f;
  521. const float MouseInputSource::invalidRotation = 0.0f;
  522. const float MouseInputSource::invalidTiltX = 0.0f;
  523. const float MouseInputSource::invalidTiltY = 0.0f;
  524. const Point<float> MouseInputSource::offscreenMousePos { -10.0f, -10.0f };
  525. // Deprecated method
  526. bool MouseInputSource::hasMouseMovedSignificantlySincePressed() const noexcept { return pimpl->hasMouseMovedSignificantlySincePressed(); }
  527. //==============================================================================
  528. struct MouseInputSource::SourceList : public Timer
  529. {
  530. SourceList()
  531. {
  532. #if JUCE_ANDROID || JUCE_IOS
  533. auto mainMouseInputType = MouseInputSource::InputSourceType::touch;
  534. #else
  535. auto mainMouseInputType = MouseInputSource::InputSourceType::mouse;
  536. #endif
  537. addSource (0, mainMouseInputType);
  538. }
  539. bool addSource();
  540. bool canUseTouch();
  541. MouseInputSource* addSource (int index, MouseInputSource::InputSourceType type)
  542. {
  543. auto* s = new MouseInputSourceInternal (index, type);
  544. sources.add (s);
  545. sourceArray.add (MouseInputSource (s));
  546. return &sourceArray.getReference (sourceArray.size() - 1);
  547. }
  548. MouseInputSource* getMouseSource (int index) noexcept
  549. {
  550. return isPositiveAndBelow (index, sourceArray.size()) ? &sourceArray.getReference (index)
  551. : nullptr;
  552. }
  553. MouseInputSource* getOrCreateMouseInputSource (MouseInputSource::InputSourceType type, int touchIndex = 0)
  554. {
  555. if (type == MouseInputSource::InputSourceType::mouse || type == MouseInputSource::InputSourceType::pen)
  556. {
  557. for (auto& m : sourceArray)
  558. if (type == m.getType())
  559. return &m;
  560. addSource (0, type);
  561. }
  562. else if (type == MouseInputSource::InputSourceType::touch)
  563. {
  564. jassert (touchIndex >= 0 && touchIndex < 100); // sanity-check on number of fingers
  565. for (auto& m : sourceArray)
  566. if (type == m.getType() && touchIndex == m.getIndex())
  567. return &m;
  568. if (canUseTouch())
  569. return addSource (touchIndex, type);
  570. }
  571. return nullptr;
  572. }
  573. int getNumDraggingMouseSources() const noexcept
  574. {
  575. int num = 0;
  576. for (auto* s : sources)
  577. if (s->isDragging())
  578. ++num;
  579. return num;
  580. }
  581. MouseInputSource* getDraggingMouseSource (int index) noexcept
  582. {
  583. int num = 0;
  584. for (auto& s : sourceArray)
  585. {
  586. if (s.isDragging())
  587. {
  588. if (index == num)
  589. return &s;
  590. ++num;
  591. }
  592. }
  593. return nullptr;
  594. }
  595. void beginDragAutoRepeat (int interval)
  596. {
  597. if (interval > 0)
  598. {
  599. if (getTimerInterval() != interval)
  600. startTimer (interval);
  601. }
  602. else
  603. {
  604. stopTimer();
  605. }
  606. }
  607. void timerCallback() override
  608. {
  609. bool anyDragging = false;
  610. for (auto* s : sources)
  611. {
  612. // NB: when doing auto-repeat, we need to force an update of the current position and button state,
  613. // because on some OSes the queue can get overloaded with messages so that mouse-events don't get through..
  614. if (s->isDragging() && ComponentPeer::getCurrentModifiersRealtime().isAnyMouseButtonDown())
  615. {
  616. s->lastScreenPos = s->getRawScreenPosition();
  617. s->triggerFakeMove();
  618. anyDragging = true;
  619. }
  620. }
  621. if (! anyDragging)
  622. stopTimer();
  623. }
  624. OwnedArray<MouseInputSourceInternal> sources;
  625. Array<MouseInputSource> sourceArray;
  626. };
  627. } // namespace juce