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 25KB

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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  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. if (Component* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos))
  271. sendMouseWheel (*current, screenPos, time, wheel);
  272. }
  273. void handleMagnifyGesture (ComponentPeer& peer, Point<float> positionWithinPeer,
  274. Time time, const float scaleFactor)
  275. {
  276. Point<float> screenPos;
  277. if (Component* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos))
  278. sendMagnifyGesture (*current, screenPos, time, scaleFactor);
  279. }
  280. //==============================================================================
  281. Time getLastMouseDownTime() const noexcept { return mouseDowns[0].time; }
  282. Point<float> getLastMouseDownPosition() const noexcept { return ScalingHelpers::unscaledScreenPosToScaled (mouseDowns[0].position); }
  283. int getNumberOfMultipleClicks() const noexcept
  284. {
  285. int numClicks = 0;
  286. if (mouseDowns[0].time != Time())
  287. {
  288. if (! mouseMovedSignificantlySincePressed)
  289. ++numClicks;
  290. for (int i = 1; i < numElementsInArray (mouseDowns); ++i)
  291. {
  292. if (mouseDowns[0].canBePartOfMultipleClickWith (mouseDowns[i], MouseEvent::getDoubleClickTimeout() * jmin (i, 2)))
  293. ++numClicks;
  294. else
  295. break;
  296. }
  297. }
  298. return numClicks;
  299. }
  300. bool hasMouseMovedSignificantlySincePressed() const noexcept
  301. {
  302. return mouseMovedSignificantlySincePressed
  303. || lastTime > mouseDowns[0].time + RelativeTime::milliseconds (300);
  304. }
  305. //==============================================================================
  306. void triggerFakeMove()
  307. {
  308. triggerAsyncUpdate();
  309. }
  310. void handleAsyncUpdate() override
  311. {
  312. setScreenPos (lastScreenPos, jmax (lastTime, Time::getCurrentTime()), true);
  313. }
  314. //==============================================================================
  315. void enableUnboundedMouseMovement (bool enable, bool keepCursorVisibleUntilOffscreen)
  316. {
  317. enable = enable && isDragging();
  318. isCursorVisibleUntilOffscreen = keepCursorVisibleUntilOffscreen;
  319. if (enable != isUnboundedMouseModeOn)
  320. {
  321. if ((! enable) && ((! isCursorVisibleUntilOffscreen) || ! unboundedMouseOffset.isOrigin()))
  322. {
  323. // when released, return the mouse to within the component's bounds
  324. if (Component* current = getComponentUnderMouse())
  325. setScreenPosition (current->getScreenBounds().toFloat()
  326. .getConstrainedPoint (ScalingHelpers::unscaledScreenPosToScaled (lastScreenPos)));
  327. }
  328. isUnboundedMouseModeOn = enable;
  329. unboundedMouseOffset = Point<float>();
  330. revealCursor (true);
  331. }
  332. }
  333. void handleUnboundedDrag (Component& current)
  334. {
  335. const Rectangle<float> componentScreenBounds
  336. = ScalingHelpers::scaledScreenPosToUnscaled (current.getParentMonitorArea().reduced (2, 2).toFloat());
  337. if (! componentScreenBounds.contains (lastScreenPos))
  338. {
  339. const Point<float> componentCentre (current.getScreenBounds().toFloat().getCentre());
  340. unboundedMouseOffset += (lastScreenPos - ScalingHelpers::scaledScreenPosToUnscaled (componentCentre));
  341. setScreenPosition (componentCentre);
  342. }
  343. else if (isCursorVisibleUntilOffscreen
  344. && (! unboundedMouseOffset.isOrigin())
  345. && componentScreenBounds.contains (lastScreenPos + unboundedMouseOffset))
  346. {
  347. MouseInputSource::setRawMousePosition (lastScreenPos + unboundedMouseOffset);
  348. unboundedMouseOffset = Point<float>();
  349. }
  350. }
  351. //==============================================================================
  352. void showMouseCursor (MouseCursor cursor, bool forcedUpdate)
  353. {
  354. if (isUnboundedMouseModeOn && ((! unboundedMouseOffset.isOrigin()) || ! isCursorVisibleUntilOffscreen))
  355. {
  356. cursor = MouseCursor::NoCursor;
  357. forcedUpdate = true;
  358. }
  359. if (forcedUpdate || cursor.getHandle() != currentCursorHandle)
  360. {
  361. currentCursorHandle = cursor.getHandle();
  362. cursor.showInWindow (getPeer());
  363. }
  364. }
  365. void hideCursor()
  366. {
  367. showMouseCursor (MouseCursor::NoCursor, true);
  368. }
  369. void revealCursor (bool forcedUpdate)
  370. {
  371. MouseCursor mc (MouseCursor::NormalCursor);
  372. if (Component* current = getComponentUnderMouse())
  373. mc = current->getLookAndFeel().getMouseCursorFor (*current);
  374. showMouseCursor (mc, forcedUpdate);
  375. }
  376. //==============================================================================
  377. const int index;
  378. const bool isMouseDevice;
  379. Point<float> lastScreenPos, unboundedMouseOffset; // NB: these are unscaled coords
  380. ModifierKeys buttonState;
  381. bool isUnboundedMouseModeOn, isCursorVisibleUntilOffscreen;
  382. private:
  383. WeakReference<Component> componentUnderMouse;
  384. ComponentPeer* lastPeer;
  385. void* currentCursorHandle;
  386. int mouseEventCounter;
  387. struct RecentMouseDown
  388. {
  389. RecentMouseDown() noexcept : peerID (0) {}
  390. Point<float> position;
  391. Time time;
  392. ModifierKeys buttons;
  393. uint32 peerID;
  394. bool canBePartOfMultipleClickWith (const RecentMouseDown& other, const int maxTimeBetweenMs) const
  395. {
  396. return time - other.time < RelativeTime::milliseconds (maxTimeBetweenMs)
  397. && std::abs (position.x - other.position.x) < 8
  398. && std::abs (position.y - other.position.y) < 8
  399. && buttons == other.buttons
  400. && peerID == other.peerID;
  401. }
  402. };
  403. RecentMouseDown mouseDowns[4];
  404. Time lastTime;
  405. bool mouseMovedSignificantlySincePressed;
  406. void registerMouseDown (Point<float> screenPos, Time time,
  407. Component& component, const ModifierKeys modifiers) noexcept
  408. {
  409. for (int i = numElementsInArray (mouseDowns); --i > 0;)
  410. mouseDowns[i] = mouseDowns[i - 1];
  411. mouseDowns[0].position = screenPos;
  412. mouseDowns[0].time = time;
  413. mouseDowns[0].buttons = modifiers.withOnlyMouseButtons();
  414. if (ComponentPeer* const peer = component.getPeer())
  415. mouseDowns[0].peerID = peer->getUniqueID();
  416. else
  417. mouseDowns[0].peerID = 0;
  418. mouseMovedSignificantlySincePressed = false;
  419. }
  420. void registerMouseDrag (Point<float> screenPos) noexcept
  421. {
  422. mouseMovedSignificantlySincePressed = mouseMovedSignificantlySincePressed
  423. || mouseDowns[0].position.getDistanceFrom (screenPos) >= 4;
  424. }
  425. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MouseInputSourceInternal)
  426. };
  427. //==============================================================================
  428. MouseInputSource::MouseInputSource (MouseInputSourceInternal* s) noexcept : pimpl (s) {}
  429. MouseInputSource::MouseInputSource (const MouseInputSource& other) noexcept : pimpl (other.pimpl) {}
  430. MouseInputSource::~MouseInputSource() noexcept {}
  431. MouseInputSource& MouseInputSource::operator= (const MouseInputSource& other) noexcept
  432. {
  433. pimpl = other.pimpl;
  434. return *this;
  435. }
  436. bool MouseInputSource::isMouse() const { return pimpl->isMouseDevice; }
  437. bool MouseInputSource::isTouch() const { return ! isMouse(); }
  438. bool MouseInputSource::canHover() const { return isMouse(); }
  439. bool MouseInputSource::hasMouseWheel() const { return isMouse(); }
  440. int MouseInputSource::getIndex() const { return pimpl->index; }
  441. bool MouseInputSource::isDragging() const { return pimpl->isDragging(); }
  442. Point<float> MouseInputSource::getScreenPosition() const { return pimpl->getScreenPosition(); }
  443. ModifierKeys MouseInputSource::getCurrentModifiers() const { return pimpl->getCurrentModifiers(); }
  444. Component* MouseInputSource::getComponentUnderMouse() const { return pimpl->getComponentUnderMouse(); }
  445. void MouseInputSource::triggerFakeMove() const { pimpl->triggerFakeMove(); }
  446. int MouseInputSource::getNumberOfMultipleClicks() const noexcept { return pimpl->getNumberOfMultipleClicks(); }
  447. Time MouseInputSource::getLastMouseDownTime() const noexcept { return pimpl->getLastMouseDownTime(); }
  448. Point<float> MouseInputSource::getLastMouseDownPosition() const noexcept { return pimpl->getLastMouseDownPosition(); }
  449. bool MouseInputSource::hasMouseMovedSignificantlySincePressed() const noexcept { return pimpl->hasMouseMovedSignificantlySincePressed(); }
  450. bool MouseInputSource::canDoUnboundedMovement() const noexcept { return isMouse(); }
  451. void MouseInputSource::enableUnboundedMouseMovement (bool isEnabled, bool keepCursorVisibleUntilOffscreen) const
  452. { pimpl->enableUnboundedMouseMovement (isEnabled, keepCursorVisibleUntilOffscreen); }
  453. bool MouseInputSource::isUnboundedMouseMovementEnabled() const { return pimpl->isUnboundedMouseModeOn; }
  454. bool MouseInputSource::hasMouseCursor() const noexcept { return isMouse(); }
  455. void MouseInputSource::showMouseCursor (const MouseCursor& cursor) { pimpl->showMouseCursor (cursor, false); }
  456. void MouseInputSource::hideCursor() { pimpl->hideCursor(); }
  457. void MouseInputSource::revealCursor() { pimpl->revealCursor (false); }
  458. void MouseInputSource::forceMouseCursorUpdate() { pimpl->revealCursor (true); }
  459. void MouseInputSource::setScreenPosition (Point<float> p) { pimpl->setScreenPosition (p); }
  460. void MouseInputSource::handleEvent (ComponentPeer& peer, Point<float> pos, int64 time, ModifierKeys mods)
  461. {
  462. pimpl->handleEvent (peer, pos, Time (time), mods.withOnlyMouseButtons());
  463. }
  464. void MouseInputSource::handleWheel (ComponentPeer& peer, Point<float> pos, int64 time, const MouseWheelDetails& wheel)
  465. {
  466. pimpl->handleWheel (peer, pos, Time (time), wheel);
  467. }
  468. void MouseInputSource::handleMagnifyGesture (ComponentPeer& peer, Point<float> pos, int64 time, float scaleFactor)
  469. {
  470. pimpl->handleMagnifyGesture (peer, pos, Time (time), scaleFactor);
  471. }
  472. //==============================================================================
  473. struct MouseInputSource::SourceList : public Timer
  474. {
  475. SourceList()
  476. {
  477. addSource();
  478. }
  479. bool addSource();
  480. void addSource (int index, bool isMouse)
  481. {
  482. MouseInputSourceInternal* s = new MouseInputSourceInternal (index, isMouse);
  483. sources.add (s);
  484. sourceArray.add (MouseInputSource (s));
  485. }
  486. MouseInputSource* getMouseSource (int index) const noexcept
  487. {
  488. return isPositiveAndBelow (index, sourceArray.size()) ? &sourceArray.getReference (index)
  489. : nullptr;
  490. }
  491. MouseInputSource* getOrCreateMouseInputSource (int touchIndex)
  492. {
  493. jassert (touchIndex >= 0 && touchIndex < 100); // sanity-check on number of fingers
  494. for (;;)
  495. {
  496. if (MouseInputSource* mouse = getMouseSource (touchIndex))
  497. return mouse;
  498. if (! addSource())
  499. {
  500. jassertfalse; // not enough mouse sources!
  501. return nullptr;
  502. }
  503. }
  504. }
  505. int getNumDraggingMouseSources() const noexcept
  506. {
  507. int num = 0;
  508. for (int i = 0; i < sources.size(); ++i)
  509. if (sources.getUnchecked(i)->isDragging())
  510. ++num;
  511. return num;
  512. }
  513. MouseInputSource* getDraggingMouseSource (int index) const noexcept
  514. {
  515. int num = 0;
  516. for (int i = 0; i < sources.size(); ++i)
  517. {
  518. MouseInputSource* const mi = &(sourceArray.getReference(i));
  519. if (mi->isDragging())
  520. {
  521. if (index == num)
  522. return mi;
  523. ++num;
  524. }
  525. }
  526. return nullptr;
  527. }
  528. void beginDragAutoRepeat (const int interval)
  529. {
  530. if (interval > 0)
  531. {
  532. if (getTimerInterval() != interval)
  533. startTimer (interval);
  534. }
  535. else
  536. {
  537. stopTimer();
  538. }
  539. }
  540. void timerCallback() override
  541. {
  542. int numMiceDown = 0;
  543. for (int i = 0; i < sources.size(); ++i)
  544. {
  545. MouseInputSourceInternal* const mi = sources.getUnchecked(i);
  546. if (mi->isDragging())
  547. {
  548. mi->triggerFakeMove();
  549. ++numMiceDown;
  550. }
  551. }
  552. if (numMiceDown == 0)
  553. stopTimer();
  554. }
  555. OwnedArray<MouseInputSourceInternal> sources;
  556. Array<MouseInputSource> sourceArray;
  557. };