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.

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