Audio plugin host https://kx.studio/carla
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.

juce_MouseInputSource.cpp 26KB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. class MouseInputSourceInternal : private AsyncUpdater
  18. {
  19. public:
  20. //==============================================================================
  21. MouseInputSourceInternal (const int i, const bool isMouse)
  22. : index (i), isMouseDevice (isMouse),
  23. isUnboundedMouseModeOn (false), isCursorVisibleUntilOffscreen (false),
  24. lastPeer (nullptr), currentCursorHandle (nullptr),
  25. mouseEventCounter (0), mouseMovedSignificantlySincePressed (false)
  26. {
  27. }
  28. //==============================================================================
  29. bool isDragging() const noexcept
  30. {
  31. return buttonState.isAnyMouseButtonDown();
  32. }
  33. Component* getComponentUnderMouse() const
  34. {
  35. return componentUnderMouse.get();
  36. }
  37. ModifierKeys getCurrentModifiers() const
  38. {
  39. return ModifierKeys::getCurrentModifiers().withoutMouseButtons().withFlags (buttonState.getRawFlags());
  40. }
  41. ComponentPeer* getPeer()
  42. {
  43. if (! ComponentPeer::isValidPeer (lastPeer))
  44. lastPeer = nullptr;
  45. return lastPeer;
  46. }
  47. static Point<float> screenPosToLocalPos (Component& comp, Point<float> pos)
  48. {
  49. if (ComponentPeer* const peer = comp.getPeer())
  50. {
  51. pos = peer->globalToLocal (pos);
  52. Component& peerComp = peer->getComponent();
  53. return comp.getLocalPoint (&peerComp, ScalingHelpers::unscaledScreenPosToScaled (peerComp, pos));
  54. }
  55. return comp.getLocalPoint (nullptr, ScalingHelpers::unscaledScreenPosToScaled (comp, pos));
  56. }
  57. Component* findComponentAt (Point<float> screenPos)
  58. {
  59. if (ComponentPeer* const peer = getPeer())
  60. {
  61. Point<float> relativePos (ScalingHelpers::unscaledScreenPosToScaled (peer->getComponent(),
  62. peer->globalToLocal (screenPos)));
  63. Component& comp = peer->getComponent();
  64. const Point<int> pos (relativePos.roundToInt());
  65. // (the contains() call is needed to test for overlapping desktop windows)
  66. if (comp.contains (pos))
  67. return comp.getComponentAt (pos);
  68. }
  69. return nullptr;
  70. }
  71. Point<float> getScreenPosition() const
  72. {
  73. // This needs to return the live position if possible, but it mustn't update the lastScreenPos
  74. // value, because that can cause continuity problems.
  75. return ScalingHelpers::unscaledScreenPosToScaled
  76. (unboundedMouseOffset + (isMouseDevice ? MouseInputSource::getCurrentRawMousePosition()
  77. : lastScreenPos));
  78. }
  79. void setScreenPosition (Point<float> p)
  80. {
  81. MouseInputSource::setRawMousePosition (ScalingHelpers::scaledScreenPosToUnscaled (p));
  82. }
  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);
  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);
  115. }
  116. void sendMouseUp (Component& comp, Point<float> screenPos, Time time, const ModifierKeys oldMods)
  117. {
  118. JUCE_MOUSE_EVENT_DBG ("up")
  119. comp.internalMouseUp (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, oldMods);
  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, const 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, const 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. const int lastCounter = mouseEventCounter;
  147. if (buttonState.isAnyMouseButtonDown())
  148. {
  149. if (Component* const current = getComponentUnderMouse())
  150. {
  151. const ModifierKeys 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 (Component* const current = getComponentUnderMouse())
  164. {
  165. registerMouseDown (screenPos, time, *current, buttonState);
  166. sendMouseDown (*current, screenPos, time);
  167. }
  168. }
  169. return lastCounter != mouseEventCounter;
  170. }
  171. void setComponentUnderMouse (Component* const newComponent, Point<float> screenPos, Time time)
  172. {
  173. Component* current = getComponentUnderMouse();
  174. if (newComponent != current)
  175. {
  176. WeakReference<Component> safeNewComp (newComponent);
  177. const ModifierKeys originalButtonState (buttonState);
  178. if (current != nullptr)
  179. {
  180. WeakReference<Component> safeOldComp (current);
  181. setButtons (screenPos, time, ModifierKeys());
  182. if (safeOldComp != nullptr)
  183. {
  184. componentUnderMouse = safeNewComp;
  185. sendMouseExit (*safeOldComp, screenPos, time);
  186. }
  187. buttonState = originalButtonState;
  188. }
  189. current = componentUnderMouse = safeNewComp;
  190. if (current != nullptr)
  191. sendMouseEnter (*current, screenPos, time);
  192. revealCursor (false);
  193. setButtons (screenPos, time, originalButtonState);
  194. }
  195. }
  196. void setPeer (ComponentPeer& newPeer, Point<float> screenPos, Time time)
  197. {
  198. ModifierKeys::updateCurrentModifiers();
  199. if (&newPeer != lastPeer)
  200. {
  201. setComponentUnderMouse (nullptr, screenPos, time);
  202. lastPeer = &newPeer;
  203. setComponentUnderMouse (findComponentAt (screenPos), screenPos, time);
  204. }
  205. }
  206. void setScreenPos (Point<float> newScreenPos, Time time, const bool forceUpdate)
  207. {
  208. if (! isDragging())
  209. setComponentUnderMouse (findComponentAt (newScreenPos), newScreenPos, time);
  210. if (newScreenPos != lastScreenPos || forceUpdate)
  211. {
  212. cancelPendingUpdate();
  213. lastScreenPos = newScreenPos;
  214. if (Component* const current = getComponentUnderMouse())
  215. {
  216. if (isDragging())
  217. {
  218. registerMouseDrag (newScreenPos);
  219. sendMouseDrag (*current, newScreenPos + unboundedMouseOffset, time);
  220. if (isUnboundedMouseModeOn)
  221. handleUnboundedDrag (*current);
  222. }
  223. else
  224. {
  225. sendMouseMove (*current, newScreenPos, time);
  226. }
  227. }
  228. revealCursor (false);
  229. }
  230. }
  231. //==============================================================================
  232. void handleEvent (ComponentPeer& newPeer, Point<float> positionWithinPeer, Time time, const ModifierKeys newMods)
  233. {
  234. lastTime = time;
  235. ++mouseEventCounter;
  236. const Point<float> screenPos (newPeer.localToGlobal (positionWithinPeer));
  237. if (isDragging() && newMods.isAnyMouseButtonDown())
  238. {
  239. setScreenPos (screenPos, time, false);
  240. }
  241. else
  242. {
  243. setPeer (newPeer, screenPos, time);
  244. if (ComponentPeer* peer = getPeer())
  245. {
  246. if (setButtons (screenPos, time, newMods))
  247. return; // some modal events have been dispatched, so the current event is now out-of-date
  248. peer = getPeer();
  249. if (peer != nullptr)
  250. setScreenPos (screenPos, time, false);
  251. }
  252. }
  253. }
  254. Component* getTargetForGesture (ComponentPeer& peer, Point<float> positionWithinPeer,
  255. Time time, Point<float>& screenPos)
  256. {
  257. lastTime = time;
  258. ++mouseEventCounter;
  259. screenPos = peer.localToGlobal (positionWithinPeer);
  260. setPeer (peer, screenPos, time);
  261. setScreenPos (screenPos, time, false);
  262. triggerFakeMove();
  263. return isDragging() ? nullptr : getComponentUnderMouse();
  264. }
  265. void handleWheel (ComponentPeer& peer, Point<float> positionWithinPeer,
  266. Time time, const MouseWheelDetails& wheel)
  267. {
  268. Desktop::getInstance().incrementMouseWheelCounter();
  269. Point<float> screenPos;
  270. // This will make sure that when the wheel spins in its inertial phase, any events
  271. // continue to be sent to the last component that the mouse was over when it was being
  272. // actively controlled by the user. This avoids confusion when scrolling through nested
  273. // scrollable components.
  274. if (lastNonInertialWheelTarget == nullptr || ! wheel.isInertial)
  275. lastNonInertialWheelTarget = getTargetForGesture (peer, positionWithinPeer, time, screenPos);
  276. if (Component* target = lastNonInertialWheelTarget)
  277. sendMouseWheel (*target, screenPos, time, wheel);
  278. }
  279. void handleMagnifyGesture (ComponentPeer& peer, Point<float> positionWithinPeer,
  280. Time time, const float scaleFactor)
  281. {
  282. Point<float> screenPos;
  283. if (Component* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos))
  284. sendMagnifyGesture (*current, screenPos, time, scaleFactor);
  285. }
  286. //==============================================================================
  287. Time getLastMouseDownTime() const noexcept { return mouseDowns[0].time; }
  288. Point<float> getLastMouseDownPosition() const noexcept { return ScalingHelpers::unscaledScreenPosToScaled (mouseDowns[0].position); }
  289. int getNumberOfMultipleClicks() const noexcept
  290. {
  291. int numClicks = 1;
  292. if (! hasMouseMovedSignificantlySincePressed())
  293. {
  294. for (int i = 1; i < numElementsInArray (mouseDowns); ++i)
  295. {
  296. if (mouseDowns[0].canBePartOfMultipleClickWith (mouseDowns[i], MouseEvent::getDoubleClickTimeout() * jmin (i, 2)))
  297. ++numClicks;
  298. else
  299. break;
  300. }
  301. }
  302. return numClicks;
  303. }
  304. bool hasMouseMovedSignificantlySincePressed() const noexcept
  305. {
  306. return mouseMovedSignificantlySincePressed
  307. || lastTime > mouseDowns[0].time + RelativeTime::milliseconds (300);
  308. }
  309. //==============================================================================
  310. void triggerFakeMove()
  311. {
  312. triggerAsyncUpdate();
  313. }
  314. void handleAsyncUpdate() override
  315. {
  316. setScreenPos (lastScreenPos, jmax (lastTime, Time::getCurrentTime()), true);
  317. }
  318. //==============================================================================
  319. void enableUnboundedMouseMovement (bool enable, bool keepCursorVisibleUntilOffscreen)
  320. {
  321. enable = enable && isDragging();
  322. isCursorVisibleUntilOffscreen = keepCursorVisibleUntilOffscreen;
  323. if (enable != isUnboundedMouseModeOn)
  324. {
  325. if ((! enable) && ((! isCursorVisibleUntilOffscreen) || ! unboundedMouseOffset.isOrigin()))
  326. {
  327. // when released, return the mouse to within the component's bounds
  328. if (Component* current = getComponentUnderMouse())
  329. setScreenPosition (current->getScreenBounds().toFloat()
  330. .getConstrainedPoint (ScalingHelpers::unscaledScreenPosToScaled (lastScreenPos)));
  331. }
  332. isUnboundedMouseModeOn = enable;
  333. unboundedMouseOffset = Point<float>();
  334. revealCursor (true);
  335. }
  336. }
  337. void handleUnboundedDrag (Component& current)
  338. {
  339. const Rectangle<float> componentScreenBounds
  340. = ScalingHelpers::scaledScreenPosToUnscaled (current.getParentMonitorArea().reduced (2, 2).toFloat());
  341. if (! componentScreenBounds.contains (lastScreenPos))
  342. {
  343. const Point<float> componentCentre (current.getScreenBounds().toFloat().getCentre());
  344. unboundedMouseOffset += (lastScreenPos - ScalingHelpers::scaledScreenPosToUnscaled (componentCentre));
  345. setScreenPosition (componentCentre);
  346. }
  347. else if (isCursorVisibleUntilOffscreen
  348. && (! unboundedMouseOffset.isOrigin())
  349. && componentScreenBounds.contains (lastScreenPos + unboundedMouseOffset))
  350. {
  351. MouseInputSource::setRawMousePosition (lastScreenPos + unboundedMouseOffset);
  352. unboundedMouseOffset = Point<float>();
  353. }
  354. }
  355. //==============================================================================
  356. void showMouseCursor (MouseCursor cursor, bool forcedUpdate)
  357. {
  358. if (isUnboundedMouseModeOn && ((! unboundedMouseOffset.isOrigin()) || ! isCursorVisibleUntilOffscreen))
  359. {
  360. cursor = MouseCursor::NoCursor;
  361. forcedUpdate = true;
  362. }
  363. if (forcedUpdate || cursor.getHandle() != currentCursorHandle)
  364. {
  365. currentCursorHandle = cursor.getHandle();
  366. cursor.showInWindow (getPeer());
  367. }
  368. }
  369. void hideCursor()
  370. {
  371. showMouseCursor (MouseCursor::NoCursor, true);
  372. }
  373. void revealCursor (bool forcedUpdate)
  374. {
  375. MouseCursor mc (MouseCursor::NormalCursor);
  376. if (Component* current = getComponentUnderMouse())
  377. mc = current->getLookAndFeel().getMouseCursorFor (*current);
  378. showMouseCursor (mc, forcedUpdate);
  379. }
  380. //==============================================================================
  381. const int index;
  382. const bool isMouseDevice;
  383. Point<float> lastScreenPos, unboundedMouseOffset; // NB: these are unscaled coords
  384. ModifierKeys buttonState;
  385. bool isUnboundedMouseModeOn, isCursorVisibleUntilOffscreen;
  386. private:
  387. WeakReference<Component> componentUnderMouse, lastNonInertialWheelTarget;
  388. ComponentPeer* lastPeer;
  389. void* currentCursorHandle;
  390. int mouseEventCounter;
  391. struct RecentMouseDown
  392. {
  393. RecentMouseDown() noexcept : peerID (0) {}
  394. Point<float> position;
  395. Time time;
  396. ModifierKeys buttons;
  397. uint32 peerID;
  398. bool canBePartOfMultipleClickWith (const RecentMouseDown& other, const int maxTimeBetweenMs) const
  399. {
  400. return time - other.time < RelativeTime::milliseconds (maxTimeBetweenMs)
  401. && std::abs (position.x - other.position.x) < 8
  402. && std::abs (position.y - other.position.y) < 8
  403. && buttons == other.buttons
  404. && peerID == other.peerID;
  405. }
  406. };
  407. RecentMouseDown mouseDowns[4];
  408. Time lastTime;
  409. bool mouseMovedSignificantlySincePressed;
  410. void registerMouseDown (Point<float> screenPos, Time time,
  411. Component& component, const ModifierKeys modifiers) noexcept
  412. {
  413. for (int i = numElementsInArray (mouseDowns); --i > 0;)
  414. mouseDowns[i] = mouseDowns[i - 1];
  415. mouseDowns[0].position = screenPos;
  416. mouseDowns[0].time = time;
  417. mouseDowns[0].buttons = modifiers.withOnlyMouseButtons();
  418. if (ComponentPeer* const peer = component.getPeer())
  419. mouseDowns[0].peerID = peer->getUniqueID();
  420. else
  421. mouseDowns[0].peerID = 0;
  422. mouseMovedSignificantlySincePressed = false;
  423. lastNonInertialWheelTarget = nullptr;
  424. }
  425. void registerMouseDrag (Point<float> screenPos) noexcept
  426. {
  427. mouseMovedSignificantlySincePressed = mouseMovedSignificantlySincePressed
  428. || mouseDowns[0].position.getDistanceFrom (screenPos) >= 4;
  429. }
  430. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MouseInputSourceInternal)
  431. };
  432. //==============================================================================
  433. MouseInputSource::MouseInputSource (MouseInputSourceInternal* s) noexcept : pimpl (s) {}
  434. MouseInputSource::MouseInputSource (const MouseInputSource& other) noexcept : pimpl (other.pimpl) {}
  435. MouseInputSource::~MouseInputSource() noexcept {}
  436. MouseInputSource& MouseInputSource::operator= (const MouseInputSource& other) noexcept
  437. {
  438. pimpl = other.pimpl;
  439. return *this;
  440. }
  441. bool MouseInputSource::isMouse() const { return pimpl->isMouseDevice; }
  442. bool MouseInputSource::isTouch() const { return ! isMouse(); }
  443. bool MouseInputSource::canHover() const { return isMouse(); }
  444. bool MouseInputSource::hasMouseWheel() const { return isMouse(); }
  445. int MouseInputSource::getIndex() const { return pimpl->index; }
  446. bool MouseInputSource::isDragging() const { return pimpl->isDragging(); }
  447. Point<float> MouseInputSource::getScreenPosition() const { return pimpl->getScreenPosition(); }
  448. ModifierKeys MouseInputSource::getCurrentModifiers() const { return pimpl->getCurrentModifiers(); }
  449. Component* MouseInputSource::getComponentUnderMouse() const { return pimpl->getComponentUnderMouse(); }
  450. void MouseInputSource::triggerFakeMove() const { pimpl->triggerFakeMove(); }
  451. int MouseInputSource::getNumberOfMultipleClicks() const noexcept { return pimpl->getNumberOfMultipleClicks(); }
  452. Time MouseInputSource::getLastMouseDownTime() const noexcept { return pimpl->getLastMouseDownTime(); }
  453. Point<float> MouseInputSource::getLastMouseDownPosition() const noexcept { return pimpl->getLastMouseDownPosition(); }
  454. bool MouseInputSource::hasMouseMovedSignificantlySincePressed() const noexcept { return pimpl->hasMouseMovedSignificantlySincePressed(); }
  455. bool MouseInputSource::canDoUnboundedMovement() const noexcept { return isMouse(); }
  456. void MouseInputSource::enableUnboundedMouseMovement (bool isEnabled, bool keepCursorVisibleUntilOffscreen) const
  457. { pimpl->enableUnboundedMouseMovement (isEnabled, keepCursorVisibleUntilOffscreen); }
  458. bool MouseInputSource::isUnboundedMouseMovementEnabled() const { return pimpl->isUnboundedMouseModeOn; }
  459. bool MouseInputSource::hasMouseCursor() const noexcept { return isMouse(); }
  460. void MouseInputSource::showMouseCursor (const MouseCursor& cursor) { pimpl->showMouseCursor (cursor, false); }
  461. void MouseInputSource::hideCursor() { pimpl->hideCursor(); }
  462. void MouseInputSource::revealCursor() { pimpl->revealCursor (false); }
  463. void MouseInputSource::forceMouseCursorUpdate() { pimpl->revealCursor (true); }
  464. void MouseInputSource::setScreenPosition (Point<float> p) { pimpl->setScreenPosition (p); }
  465. void MouseInputSource::handleEvent (ComponentPeer& peer, Point<float> pos, int64 time, ModifierKeys mods)
  466. {
  467. pimpl->handleEvent (peer, pos, Time (time), mods.withOnlyMouseButtons());
  468. }
  469. void MouseInputSource::handleWheel (ComponentPeer& peer, Point<float> pos, int64 time, const MouseWheelDetails& wheel)
  470. {
  471. pimpl->handleWheel (peer, pos, Time (time), wheel);
  472. }
  473. void MouseInputSource::handleMagnifyGesture (ComponentPeer& peer, Point<float> pos, int64 time, float scaleFactor)
  474. {
  475. pimpl->handleMagnifyGesture (peer, pos, Time (time), scaleFactor);
  476. }
  477. //==============================================================================
  478. struct MouseInputSource::SourceList : public Timer
  479. {
  480. SourceList()
  481. {
  482. addSource();
  483. }
  484. bool addSource();
  485. void addSource (int index, bool isMouse)
  486. {
  487. MouseInputSourceInternal* s = new MouseInputSourceInternal (index, isMouse);
  488. sources.add (s);
  489. sourceArray.add (MouseInputSource (s));
  490. }
  491. MouseInputSource* getMouseSource (int index) const noexcept
  492. {
  493. return isPositiveAndBelow (index, sourceArray.size()) ? &sourceArray.getReference (index)
  494. : nullptr;
  495. }
  496. MouseInputSource* getOrCreateMouseInputSource (int touchIndex)
  497. {
  498. jassert (touchIndex >= 0 && touchIndex < 100); // sanity-check on number of fingers
  499. for (;;)
  500. {
  501. if (MouseInputSource* mouse = getMouseSource (touchIndex))
  502. return mouse;
  503. if (! addSource())
  504. {
  505. jassertfalse; // not enough mouse sources!
  506. return nullptr;
  507. }
  508. }
  509. }
  510. int getNumDraggingMouseSources() const noexcept
  511. {
  512. int num = 0;
  513. for (int i = 0; i < sources.size(); ++i)
  514. if (sources.getUnchecked(i)->isDragging())
  515. ++num;
  516. return num;
  517. }
  518. MouseInputSource* getDraggingMouseSource (int index) const noexcept
  519. {
  520. int num = 0;
  521. for (int i = 0; i < sources.size(); ++i)
  522. {
  523. MouseInputSource* const mi = &(sourceArray.getReference(i));
  524. if (mi->isDragging())
  525. {
  526. if (index == num)
  527. return mi;
  528. ++num;
  529. }
  530. }
  531. return nullptr;
  532. }
  533. void beginDragAutoRepeat (const int interval)
  534. {
  535. if (interval > 0)
  536. {
  537. if (getTimerInterval() != interval)
  538. startTimer (interval);
  539. }
  540. else
  541. {
  542. stopTimer();
  543. }
  544. }
  545. void timerCallback() override
  546. {
  547. int numMiceDown = 0;
  548. for (int i = 0; i < sources.size(); ++i)
  549. {
  550. MouseInputSourceInternal* const mi = sources.getUnchecked(i);
  551. if (mi->isDragging())
  552. {
  553. mi->triggerFakeMove();
  554. ++numMiceDown;
  555. }
  556. }
  557. if (numMiceDown == 0)
  558. stopTimer();
  559. }
  560. OwnedArray<MouseInputSourceInternal> sources;
  561. Array<MouseInputSource> sourceArray;
  562. };