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.

557 lines
22KB

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