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.

574 lines
22KB

  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 (MouseInputSource& s, const int i, const bool isMouse)
  22. : index (i), isMouseDevice (isMouse), source (s), lastPeer (nullptr),
  23. isUnboundedMouseModeOn (false), isCursorVisibleUntilOffscreen (false), currentCursorHandle (nullptr),
  24. mouseEventCounter (0), mouseMovedSignificantlySincePressed (false)
  25. {
  26. }
  27. //==============================================================================
  28. bool isDragging() const noexcept
  29. {
  30. return buttonState.isAnyMouseButtonDown();
  31. }
  32. Component* getComponentUnderMouse() const
  33. {
  34. return componentUnderMouse.get();
  35. }
  36. ModifierKeys getCurrentModifiers() const
  37. {
  38. return ModifierKeys::getCurrentModifiers().withoutMouseButtons().withFlags (buttonState.getRawFlags());
  39. }
  40. ComponentPeer* getPeer()
  41. {
  42. if (! ComponentPeer::isValidPeer (lastPeer))
  43. lastPeer = nullptr;
  44. return lastPeer;
  45. }
  46. static Point<int> screenPosToLocalPos (Component& comp, Point<int> pos)
  47. {
  48. if (ComponentPeer* const peer = comp.getPeer())
  49. {
  50. pos = peer->globalToLocal (pos);
  51. Component& peerComp = peer->getComponent();
  52. return comp.getLocalPoint (&peerComp, Component::ComponentHelpers::unscaledScreenPosToScaled (peerComp, pos));
  53. }
  54. return comp.getLocalPoint (nullptr, Component::ComponentHelpers::unscaledScreenPosToScaled (comp, pos));
  55. }
  56. Component* findComponentAt (Point<int> screenPos)
  57. {
  58. if (ComponentPeer* const peer = getPeer())
  59. {
  60. Point<int> relativePos (Component::ComponentHelpers::unscaledScreenPosToScaled (peer->getComponent(),
  61. peer->globalToLocal (screenPos)));
  62. Component& comp = peer->getComponent();
  63. // (the contains() call is needed to test for overlapping desktop windows)
  64. if (comp.contains (relativePos))
  65. return comp.getComponentAt (relativePos);
  66. }
  67. return nullptr;
  68. }
  69. Point<int> getScreenPosition() const
  70. {
  71. // This needs to return the live position if possible, but it mustn't update the lastScreenPos
  72. // value, because that can cause continuity problems.
  73. return Component::ComponentHelpers::unscaledScreenPosToScaled
  74. (unboundedMouseOffset + (isMouseDevice ? MouseInputSource::getCurrentRawMousePosition()
  75. : lastScreenPos));
  76. }
  77. void setScreenPosition (Point<int> p)
  78. {
  79. MouseInputSource::setRawMousePosition (Component::ComponentHelpers::scaledScreenPosToUnscaled (p));
  80. }
  81. //==============================================================================
  82. #if JUCE_DUMP_MOUSE_EVENTS
  83. #define JUCE_MOUSE_EVENT_DBG(desc) DBG ("Mouse " desc << " #" << source.getIndex() \
  84. << ": " << screenPosToLocalPos (comp, screenPos).toString() \
  85. << " - Comp: " << String::toHexString ((int) &comp));
  86. #else
  87. #define JUCE_MOUSE_EVENT_DBG(desc)
  88. #endif
  89. void sendMouseEnter (Component& comp, Point<int> screenPos, Time time)
  90. {
  91. JUCE_MOUSE_EVENT_DBG ("enter")
  92. comp.internalMouseEnter (source, screenPosToLocalPos (comp, screenPos), time);
  93. }
  94. void sendMouseExit (Component& comp, Point<int> screenPos, Time time)
  95. {
  96. JUCE_MOUSE_EVENT_DBG ("exit")
  97. comp.internalMouseExit (source, screenPosToLocalPos (comp, screenPos), time);
  98. }
  99. void sendMouseMove (Component& comp, Point<int> screenPos, Time time)
  100. {
  101. JUCE_MOUSE_EVENT_DBG ("move")
  102. comp.internalMouseMove (source, screenPosToLocalPos (comp, screenPos), time);
  103. }
  104. void sendMouseDown (Component& comp, Point<int> screenPos, Time time)
  105. {
  106. JUCE_MOUSE_EVENT_DBG ("down")
  107. comp.internalMouseDown (source, screenPosToLocalPos (comp, screenPos), time);
  108. }
  109. void sendMouseDrag (Component& comp, Point<int> screenPos, Time time)
  110. {
  111. JUCE_MOUSE_EVENT_DBG ("drag")
  112. comp.internalMouseDrag (source, screenPosToLocalPos (comp, screenPos), time);
  113. }
  114. void sendMouseUp (Component& comp, Point<int> screenPos, Time time, const ModifierKeys oldMods)
  115. {
  116. JUCE_MOUSE_EVENT_DBG ("up")
  117. comp.internalMouseUp (source, screenPosToLocalPos (comp, screenPos), time, oldMods);
  118. }
  119. void sendMouseWheel (Component& comp, Point<int> screenPos, Time time, const MouseWheelDetails& wheel)
  120. {
  121. JUCE_MOUSE_EVENT_DBG ("wheel")
  122. comp.internalMouseWheel (source, screenPosToLocalPos (comp, screenPos), time, wheel);
  123. }
  124. void sendMagnifyGesture (Component& comp, Point<int> screenPos, Time time, const float amount)
  125. {
  126. JUCE_MOUSE_EVENT_DBG ("magnify")
  127. comp.internalMagnifyGesture (source, screenPosToLocalPos (comp, screenPos), time, amount);
  128. }
  129. //==============================================================================
  130. // (returns true if the button change caused a modal event loop)
  131. bool setButtons (Point<int> screenPos, Time time, const ModifierKeys newButtonState)
  132. {
  133. if (buttonState == newButtonState)
  134. return false;
  135. // (avoid sending a spurious mouse-drag when we receive a mouse-up)
  136. if (! (isDragging() && ! newButtonState.isAnyMouseButtonDown()))
  137. setScreenPos (screenPos, time, false);
  138. // (ignore secondary clicks when there's already a button down)
  139. if (buttonState.isAnyMouseButtonDown() == newButtonState.isAnyMouseButtonDown())
  140. {
  141. buttonState = newButtonState;
  142. return false;
  143. }
  144. const int lastCounter = mouseEventCounter;
  145. if (buttonState.isAnyMouseButtonDown())
  146. {
  147. if (Component* const current = getComponentUnderMouse())
  148. {
  149. const ModifierKeys oldMods (getCurrentModifiers());
  150. buttonState = newButtonState; // must change this before calling sendMouseUp, in case it runs a modal loop
  151. sendMouseUp (*current, screenPos + unboundedMouseOffset, time, oldMods);
  152. if (lastCounter != mouseEventCounter)
  153. return true; // if a modal loop happened, then newButtonState is no longer valid.
  154. }
  155. enableUnboundedMouseMovement (false, false);
  156. }
  157. buttonState = newButtonState;
  158. if (buttonState.isAnyMouseButtonDown())
  159. {
  160. Desktop::getInstance().incrementMouseClickCounter();
  161. if (Component* const current = getComponentUnderMouse())
  162. {
  163. registerMouseDown (screenPos, time, *current, buttonState);
  164. sendMouseDown (*current, screenPos, time);
  165. }
  166. }
  167. return lastCounter != mouseEventCounter;
  168. }
  169. void setComponentUnderMouse (Component* const newComponent, Point<int> screenPos, Time time)
  170. {
  171. Component* current = getComponentUnderMouse();
  172. if (newComponent != current)
  173. {
  174. WeakReference<Component> safeNewComp (newComponent);
  175. const ModifierKeys originalButtonState (buttonState);
  176. if (current != nullptr)
  177. {
  178. WeakReference<Component> safeOldComp (current);
  179. setButtons (screenPos, time, ModifierKeys());
  180. if (safeOldComp != nullptr)
  181. {
  182. componentUnderMouse = safeNewComp;
  183. sendMouseExit (*safeOldComp, screenPos, time);
  184. }
  185. buttonState = originalButtonState;
  186. }
  187. current = componentUnderMouse = safeNewComp;
  188. if (current != nullptr)
  189. sendMouseEnter (*current, screenPos, time);
  190. revealCursor (false);
  191. setButtons (screenPos, time, originalButtonState);
  192. }
  193. }
  194. void setPeer (ComponentPeer& newPeer, Point<int> screenPos, Time time)
  195. {
  196. ModifierKeys::updateCurrentModifiers();
  197. if (&newPeer != lastPeer)
  198. {
  199. setComponentUnderMouse (nullptr, screenPos, time);
  200. lastPeer = &newPeer;
  201. setComponentUnderMouse (findComponentAt (screenPos), screenPos, time);
  202. }
  203. }
  204. void setScreenPos (Point<int> newScreenPos, Time time, const bool forceUpdate)
  205. {
  206. if (! isDragging())
  207. setComponentUnderMouse (findComponentAt (newScreenPos), newScreenPos, time);
  208. if (newScreenPos != lastScreenPos || forceUpdate)
  209. {
  210. cancelPendingUpdate();
  211. lastScreenPos = newScreenPos;
  212. if (Component* const current = getComponentUnderMouse())
  213. {
  214. if (isDragging())
  215. {
  216. registerMouseDrag (newScreenPos);
  217. sendMouseDrag (*current, newScreenPos + unboundedMouseOffset, time);
  218. if (isUnboundedMouseModeOn)
  219. handleUnboundedDrag (current);
  220. }
  221. else
  222. {
  223. sendMouseMove (*current, newScreenPos, time);
  224. }
  225. }
  226. revealCursor (false);
  227. }
  228. }
  229. //==============================================================================
  230. void handleEvent (ComponentPeer& newPeer, Point<int> positionWithinPeer, Time time, const ModifierKeys newMods)
  231. {
  232. lastTime = time;
  233. ++mouseEventCounter;
  234. const Point<int> screenPos (newPeer.localToGlobal (positionWithinPeer));
  235. if (isDragging() && newMods.isAnyMouseButtonDown())
  236. {
  237. setScreenPos (screenPos, time, false);
  238. }
  239. else
  240. {
  241. setPeer (newPeer, screenPos, time);
  242. if (ComponentPeer* peer = getPeer())
  243. {
  244. if (setButtons (screenPos, time, newMods))
  245. return; // some modal events have been dispatched, so the current event is now out-of-date
  246. peer = getPeer();
  247. if (peer != nullptr)
  248. setScreenPos (screenPos, time, false);
  249. }
  250. }
  251. }
  252. Component* getTargetForGesture (ComponentPeer& peer, Point<int> positionWithinPeer,
  253. Time time, Point<int>& screenPos)
  254. {
  255. lastTime = time;
  256. ++mouseEventCounter;
  257. screenPos = peer.localToGlobal (positionWithinPeer);
  258. setPeer (peer, screenPos, time);
  259. setScreenPos (screenPos, time, false);
  260. triggerFakeMove();
  261. return isDragging() ? nullptr : getComponentUnderMouse();
  262. }
  263. void handleWheel (ComponentPeer& peer, Point<int> positionWithinPeer,
  264. Time time, const MouseWheelDetails& wheel)
  265. {
  266. Desktop::getInstance().incrementMouseWheelCounter();
  267. Point<int> screenPos;
  268. if (Component* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos))
  269. sendMouseWheel (*current, screenPos, time, wheel);
  270. }
  271. void handleMagnifyGesture (ComponentPeer& peer, Point<int> positionWithinPeer,
  272. Time time, const float scaleFactor)
  273. {
  274. Point<int> screenPos;
  275. if (Component* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos))
  276. sendMagnifyGesture (*current, screenPos, time, scaleFactor);
  277. }
  278. //==============================================================================
  279. Time getLastMouseDownTime() const noexcept { return mouseDowns[0].time; }
  280. Point<int> getLastMouseDownPosition() const noexcept { return Component::ComponentHelpers::unscaledScreenPosToScaled (mouseDowns[0].position); }
  281. int getNumberOfMultipleClicks() const noexcept
  282. {
  283. int numClicks = 0;
  284. if (mouseDowns[0].time != Time())
  285. {
  286. if (! mouseMovedSignificantlySincePressed)
  287. ++numClicks;
  288. for (int i = 1; i < numElementsInArray (mouseDowns); ++i)
  289. {
  290. if (mouseDowns[0].canBePartOfMultipleClickWith (mouseDowns[i], MouseEvent::getDoubleClickTimeout() * jmin (i, 2)))
  291. ++numClicks;
  292. else
  293. break;
  294. }
  295. }
  296. return numClicks;
  297. }
  298. bool hasMouseMovedSignificantlySincePressed() const noexcept
  299. {
  300. return mouseMovedSignificantlySincePressed
  301. || lastTime > mouseDowns[0].time + RelativeTime::milliseconds (300);
  302. }
  303. //==============================================================================
  304. void triggerFakeMove()
  305. {
  306. triggerAsyncUpdate();
  307. }
  308. void handleAsyncUpdate() override
  309. {
  310. setScreenPos (lastScreenPos, jmax (lastTime, Time::getCurrentTime()), true);
  311. }
  312. //==============================================================================
  313. void enableUnboundedMouseMovement (bool enable, bool keepCursorVisibleUntilOffscreen)
  314. {
  315. enable = enable && isDragging();
  316. isCursorVisibleUntilOffscreen = keepCursorVisibleUntilOffscreen;
  317. if (enable != isUnboundedMouseModeOn)
  318. {
  319. if ((! enable) && ((! isCursorVisibleUntilOffscreen) || ! unboundedMouseOffset.isOrigin()))
  320. {
  321. // when released, return the mouse to within the component's bounds
  322. if (Component* current = getComponentUnderMouse())
  323. Desktop::setMousePosition (current->getScreenBounds()
  324. .getConstrainedPoint (lastScreenPos));
  325. }
  326. isUnboundedMouseModeOn = enable;
  327. unboundedMouseOffset = Point<int>();
  328. revealCursor (true);
  329. }
  330. }
  331. void handleUnboundedDrag (Component* current)
  332. {
  333. const Rectangle<int> screenArea (current->getParentMonitorArea().expanded (-2, -2));
  334. if (! screenArea.contains (lastScreenPos))
  335. {
  336. const Point<int> componentCentre (current->getScreenBounds().getCentre());
  337. unboundedMouseOffset += (lastScreenPos - componentCentre);
  338. Desktop::setMousePosition (componentCentre);
  339. }
  340. else if (isCursorVisibleUntilOffscreen
  341. && (! unboundedMouseOffset.isOrigin())
  342. && screenArea.contains (lastScreenPos + unboundedMouseOffset))
  343. {
  344. Desktop::setMousePosition (lastScreenPos + unboundedMouseOffset);
  345. unboundedMouseOffset = Point<int>();
  346. }
  347. }
  348. //==============================================================================
  349. void showMouseCursor (MouseCursor cursor, bool forcedUpdate)
  350. {
  351. if (isUnboundedMouseModeOn && ((! unboundedMouseOffset.isOrigin()) || ! isCursorVisibleUntilOffscreen))
  352. {
  353. cursor = MouseCursor::NoCursor;
  354. forcedUpdate = true;
  355. }
  356. if (forcedUpdate || cursor.getHandle() != currentCursorHandle)
  357. {
  358. currentCursorHandle = cursor.getHandle();
  359. cursor.showInWindow (getPeer());
  360. }
  361. }
  362. void hideCursor()
  363. {
  364. showMouseCursor (MouseCursor::NoCursor, true);
  365. }
  366. void revealCursor (bool forcedUpdate)
  367. {
  368. MouseCursor mc (MouseCursor::NormalCursor);
  369. if (Component* current = getComponentUnderMouse())
  370. mc = current->getLookAndFeel().getMouseCursorFor (*current);
  371. showMouseCursor (mc, forcedUpdate);
  372. }
  373. //==============================================================================
  374. const int index;
  375. const bool isMouseDevice;
  376. Point<int> lastScreenPos;
  377. ModifierKeys buttonState;
  378. private:
  379. MouseInputSource& source;
  380. WeakReference<Component> componentUnderMouse;
  381. ComponentPeer* lastPeer;
  382. Point<int> unboundedMouseOffset;
  383. bool isUnboundedMouseModeOn, isCursorVisibleUntilOffscreen;
  384. void* currentCursorHandle;
  385. int mouseEventCounter;
  386. struct RecentMouseDown
  387. {
  388. RecentMouseDown() noexcept : peerID (0) {}
  389. Point<int> position;
  390. Time time;
  391. ModifierKeys buttons;
  392. uint32 peerID;
  393. bool canBePartOfMultipleClickWith (const RecentMouseDown& other, const int maxTimeBetweenMs) const
  394. {
  395. return time - other.time < RelativeTime::milliseconds (maxTimeBetweenMs)
  396. && abs (position.x - other.position.x) < 8
  397. && abs (position.y - other.position.y) < 8
  398. && buttons == other.buttons
  399. && peerID == other.peerID;
  400. }
  401. };
  402. RecentMouseDown mouseDowns[4];
  403. Time lastTime;
  404. bool mouseMovedSignificantlySincePressed;
  405. void registerMouseDown (Point<int> screenPos, Time time,
  406. Component& component, const ModifierKeys modifiers) noexcept
  407. {
  408. for (int i = numElementsInArray (mouseDowns); --i > 0;)
  409. mouseDowns[i] = mouseDowns[i - 1];
  410. mouseDowns[0].position = screenPos;
  411. mouseDowns[0].time = time;
  412. mouseDowns[0].buttons = modifiers.withOnlyMouseButtons();
  413. if (ComponentPeer* const peer = component.getPeer())
  414. mouseDowns[0].peerID = peer->getUniqueID();
  415. else
  416. mouseDowns[0].peerID = 0;
  417. mouseMovedSignificantlySincePressed = false;
  418. }
  419. void registerMouseDrag (Point<int> screenPos) noexcept
  420. {
  421. mouseMovedSignificantlySincePressed = mouseMovedSignificantlySincePressed
  422. || mouseDowns[0].position.getDistanceFrom (screenPos) >= 4;
  423. }
  424. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MouseInputSourceInternal)
  425. };
  426. //==============================================================================
  427. MouseInputSource::MouseInputSource (const int index, const bool isMouseDevice)
  428. {
  429. pimpl = new MouseInputSourceInternal (*this, index, isMouseDevice);
  430. }
  431. MouseInputSource::~MouseInputSource() {}
  432. bool MouseInputSource::isMouse() const { return pimpl->isMouseDevice; }
  433. bool MouseInputSource::isTouch() const { return ! isMouse(); }
  434. bool MouseInputSource::canHover() const { return isMouse(); }
  435. bool MouseInputSource::hasMouseWheel() const { return isMouse(); }
  436. int MouseInputSource::getIndex() const { return pimpl->index; }
  437. bool MouseInputSource::isDragging() const { return pimpl->isDragging(); }
  438. Point<int> MouseInputSource::getScreenPosition() const { return pimpl->getScreenPosition(); }
  439. ModifierKeys MouseInputSource::getCurrentModifiers() const { return pimpl->getCurrentModifiers(); }
  440. Component* MouseInputSource::getComponentUnderMouse() const { return pimpl->getComponentUnderMouse(); }
  441. void MouseInputSource::triggerFakeMove() const { pimpl->triggerFakeMove(); }
  442. int MouseInputSource::getNumberOfMultipleClicks() const noexcept { return pimpl->getNumberOfMultipleClicks(); }
  443. Time MouseInputSource::getLastMouseDownTime() const noexcept { return pimpl->getLastMouseDownTime(); }
  444. Point<int> MouseInputSource::getLastMouseDownPosition() const noexcept { return pimpl->getLastMouseDownPosition(); }
  445. bool MouseInputSource::hasMouseMovedSignificantlySincePressed() const noexcept { return pimpl->hasMouseMovedSignificantlySincePressed(); }
  446. bool MouseInputSource::canDoUnboundedMovement() const noexcept { return isMouse(); }
  447. void MouseInputSource::enableUnboundedMouseMovement (bool isEnabled, bool keepCursorVisibleUntilOffscreen) { pimpl->enableUnboundedMouseMovement (isEnabled, keepCursorVisibleUntilOffscreen); }
  448. bool MouseInputSource::hasMouseCursor() const noexcept { return isMouse(); }
  449. void MouseInputSource::showMouseCursor (const MouseCursor& cursor) { pimpl->showMouseCursor (cursor, false); }
  450. void MouseInputSource::hideCursor() { pimpl->hideCursor(); }
  451. void MouseInputSource::revealCursor() { pimpl->revealCursor (false); }
  452. void MouseInputSource::forceMouseCursorUpdate() { pimpl->revealCursor (true); }
  453. void MouseInputSource::setScreenPosition (Point<int> p) { pimpl->setScreenPosition (p); }
  454. void MouseInputSource::handleEvent (ComponentPeer& peer, Point<int> positionWithinPeer,
  455. const int64 time, const ModifierKeys mods)
  456. {
  457. pimpl->handleEvent (peer, positionWithinPeer, Time (time), mods.withOnlyMouseButtons());
  458. }
  459. void MouseInputSource::handleWheel (ComponentPeer& peer, Point<int> positionWithinPeer,
  460. const int64 time, const MouseWheelDetails& wheel)
  461. {
  462. pimpl->handleWheel (peer, positionWithinPeer, Time (time), wheel);
  463. }
  464. void MouseInputSource::handleMagnifyGesture (ComponentPeer& peer, Point<int> positionWithinPeer,
  465. const int64 time, const float scaleFactor)
  466. {
  467. pimpl->handleMagnifyGesture (peer, positionWithinPeer, Time (time), scaleFactor);
  468. }