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.

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