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.

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