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.

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