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.

554 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 (const 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, const Point<int>& screenPos, const 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, const Point<int>& screenPos, const 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, const Point<int>& screenPos, const 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, const Point<int>& screenPos, const 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, const Point<int>& screenPos, const 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, const Point<int>& screenPos, const 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, const Point<int>& screenPos, const 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, const Point<int>& screenPos, const 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 (const Point<int>& screenPos, const 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. }
  138. enableUnboundedMouseMovement (false, false);
  139. }
  140. buttonState = newButtonState;
  141. if (buttonState.isAnyMouseButtonDown())
  142. {
  143. Desktop::getInstance().incrementMouseClickCounter();
  144. if (Component* const current = getComponentUnderMouse())
  145. {
  146. registerMouseDown (screenPos, time, *current, buttonState);
  147. sendMouseDown (current, screenPos, time);
  148. }
  149. }
  150. return lastCounter != mouseEventCounter;
  151. }
  152. void setComponentUnderMouse (Component* const newComponent, const Point<int>& screenPos, const Time& time)
  153. {
  154. Component* current = getComponentUnderMouse();
  155. if (newComponent != current)
  156. {
  157. WeakReference<Component> safeNewComp (newComponent);
  158. const ModifierKeys originalButtonState (buttonState);
  159. if (current != nullptr)
  160. {
  161. WeakReference<Component> safeOldComp (current);
  162. setButtons (screenPos, time, ModifierKeys());
  163. if (safeOldComp != nullptr)
  164. {
  165. componentUnderMouse = safeNewComp;
  166. sendMouseExit (safeOldComp, screenPos, time);
  167. }
  168. buttonState = originalButtonState;
  169. }
  170. current = componentUnderMouse = safeNewComp;
  171. if (current != nullptr)
  172. sendMouseEnter (current, screenPos, time);
  173. revealCursor (false);
  174. setButtons (screenPos, time, originalButtonState);
  175. }
  176. }
  177. void setPeer (ComponentPeer* const newPeer, const Point<int>& screenPos, const Time& time)
  178. {
  179. ModifierKeys::updateCurrentModifiers();
  180. if (newPeer != lastPeer)
  181. {
  182. setComponentUnderMouse (nullptr, screenPos, time);
  183. lastPeer = newPeer;
  184. setComponentUnderMouse (findComponentAt (screenPos), screenPos, time);
  185. }
  186. }
  187. void setScreenPos (const Point<int>& newScreenPos, const Time& time, const bool forceUpdate)
  188. {
  189. if (! isDragging())
  190. setComponentUnderMouse (findComponentAt (newScreenPos), newScreenPos, time);
  191. if (newScreenPos != lastScreenPos || forceUpdate)
  192. {
  193. cancelPendingUpdate();
  194. lastScreenPos = newScreenPos;
  195. if (Component* const current = getComponentUnderMouse())
  196. {
  197. if (isDragging())
  198. {
  199. registerMouseDrag (newScreenPos);
  200. sendMouseDrag (current, newScreenPos + unboundedMouseOffset, time);
  201. if (isUnboundedMouseModeOn)
  202. handleUnboundedDrag (current);
  203. }
  204. else
  205. {
  206. sendMouseMove (current, newScreenPos, time);
  207. }
  208. }
  209. revealCursor (false);
  210. }
  211. }
  212. //==============================================================================
  213. void handleEvent (ComponentPeer* const newPeer, const Point<int>& positionWithinPeer, const Time& time, const ModifierKeys& newMods)
  214. {
  215. jassert (newPeer != nullptr);
  216. lastTime = time;
  217. ++mouseEventCounter;
  218. const Point<int> screenPos (newPeer->localToGlobal (positionWithinPeer));
  219. if (isDragging() && newMods.isAnyMouseButtonDown())
  220. {
  221. setScreenPos (screenPos, time, false);
  222. }
  223. else
  224. {
  225. setPeer (newPeer, screenPos, time);
  226. if (ComponentPeer* peer = getPeer())
  227. {
  228. if (setButtons (screenPos, time, newMods))
  229. return; // some modal events have been dispatched, so the current event is now out-of-date
  230. peer = getPeer();
  231. if (peer != nullptr)
  232. setScreenPos (screenPos, time, false);
  233. }
  234. }
  235. }
  236. Component* getTargetForGesture (ComponentPeer* const peer, const Point<int>& positionWithinPeer,
  237. const Time& time, Point<int>& screenPos)
  238. {
  239. jassert (peer != nullptr);
  240. lastTime = time;
  241. ++mouseEventCounter;
  242. screenPos = peer->localToGlobal (positionWithinPeer);
  243. setPeer (peer, screenPos, time);
  244. setScreenPos (screenPos, time, false);
  245. triggerFakeMove();
  246. return isDragging() ? nullptr : getComponentUnderMouse();
  247. }
  248. void handleWheel (ComponentPeer* const peer, const Point<int>& positionWithinPeer,
  249. const Time& time, const MouseWheelDetails& wheel)
  250. {
  251. Desktop::getInstance().incrementMouseWheelCounter();
  252. Point<int> screenPos;
  253. if (Component* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos))
  254. sendMouseWheel (current, screenPos, time, wheel);
  255. }
  256. void handleMagnifyGesture (ComponentPeer* const peer, const Point<int>& positionWithinPeer,
  257. const Time& time, const float scaleFactor)
  258. {
  259. Point<int> screenPos;
  260. if (Component* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos))
  261. sendMagnifyGesture (current, screenPos, time, scaleFactor);
  262. }
  263. //==============================================================================
  264. const Time& getLastMouseDownTime() const noexcept { return mouseDowns[0].time; }
  265. const Point<int>& getLastMouseDownPosition() const noexcept { return mouseDowns[0].position; }
  266. int getNumberOfMultipleClicks() const noexcept
  267. {
  268. int numClicks = 0;
  269. if (mouseDowns[0].time != Time())
  270. {
  271. if (! mouseMovedSignificantlySincePressed)
  272. ++numClicks;
  273. for (int i = 1; i < numElementsInArray (mouseDowns); ++i)
  274. {
  275. if (mouseDowns[0].canBePartOfMultipleClickWith (mouseDowns[i], MouseEvent::getDoubleClickTimeout() * jmin (i, 2)))
  276. ++numClicks;
  277. else
  278. break;
  279. }
  280. }
  281. return numClicks;
  282. }
  283. bool hasMouseMovedSignificantlySincePressed() const noexcept
  284. {
  285. return mouseMovedSignificantlySincePressed
  286. || lastTime > mouseDowns[0].time + RelativeTime::milliseconds (300);
  287. }
  288. //==============================================================================
  289. void triggerFakeMove()
  290. {
  291. triggerAsyncUpdate();
  292. }
  293. void handleAsyncUpdate()
  294. {
  295. setScreenPos (lastScreenPos, jmax (lastTime, Time::getCurrentTime()), true);
  296. }
  297. //==============================================================================
  298. void enableUnboundedMouseMovement (bool enable, bool keepCursorVisibleUntilOffscreen)
  299. {
  300. enable = enable && isDragging();
  301. isCursorVisibleUntilOffscreen = keepCursorVisibleUntilOffscreen;
  302. if (enable != isUnboundedMouseModeOn)
  303. {
  304. if ((! enable) && ((! isCursorVisibleUntilOffscreen) || ! unboundedMouseOffset.isOrigin()))
  305. {
  306. // when released, return the mouse to within the component's bounds
  307. if (Component* current = getComponentUnderMouse())
  308. Desktop::setMousePosition (current->getScreenBounds()
  309. .getConstrainedPoint (lastScreenPos));
  310. }
  311. isUnboundedMouseModeOn = enable;
  312. unboundedMouseOffset = Point<int>();
  313. revealCursor (true);
  314. }
  315. }
  316. void handleUnboundedDrag (Component* current)
  317. {
  318. const Rectangle<int> screenArea (current->getParentMonitorArea().expanded (-2, -2));
  319. if (! screenArea.contains (lastScreenPos))
  320. {
  321. const Point<int> componentCentre (current->getScreenBounds().getCentre());
  322. unboundedMouseOffset += (lastScreenPos - componentCentre);
  323. Desktop::setMousePosition (componentCentre);
  324. }
  325. else if (isCursorVisibleUntilOffscreen
  326. && (! unboundedMouseOffset.isOrigin())
  327. && screenArea.contains (lastScreenPos + unboundedMouseOffset))
  328. {
  329. Desktop::setMousePosition (lastScreenPos + unboundedMouseOffset);
  330. unboundedMouseOffset = Point<int>();
  331. }
  332. }
  333. //==============================================================================
  334. void showMouseCursor (MouseCursor cursor, bool forcedUpdate)
  335. {
  336. if (isUnboundedMouseModeOn && ((! unboundedMouseOffset.isOrigin()) || ! isCursorVisibleUntilOffscreen))
  337. {
  338. cursor = MouseCursor::NoCursor;
  339. forcedUpdate = true;
  340. }
  341. if (forcedUpdate || cursor.getHandle() != currentCursorHandle)
  342. {
  343. currentCursorHandle = cursor.getHandle();
  344. cursor.showInWindow (getPeer());
  345. }
  346. }
  347. void hideCursor()
  348. {
  349. showMouseCursor (MouseCursor::NoCursor, true);
  350. }
  351. void revealCursor (bool forcedUpdate)
  352. {
  353. MouseCursor mc (MouseCursor::NormalCursor);
  354. if (Component* current = getComponentUnderMouse())
  355. mc = current->getLookAndFeel().getMouseCursorFor (*current);
  356. showMouseCursor (mc, forcedUpdate);
  357. }
  358. //==============================================================================
  359. const int index;
  360. const bool isMouseDevice;
  361. Point<int> lastScreenPos;
  362. ModifierKeys buttonState;
  363. private:
  364. MouseInputSource& source;
  365. WeakReference<Component> componentUnderMouse;
  366. ComponentPeer* lastPeer;
  367. Point<int> unboundedMouseOffset;
  368. bool isUnboundedMouseModeOn, isCursorVisibleUntilOffscreen;
  369. void* currentCursorHandle;
  370. int mouseEventCounter;
  371. struct RecentMouseDown
  372. {
  373. RecentMouseDown() noexcept : peerID (0) {}
  374. Point<int> position;
  375. Time time;
  376. ModifierKeys buttons;
  377. uint32 peerID;
  378. bool canBePartOfMultipleClickWith (const RecentMouseDown& other, const int maxTimeBetweenMs) const
  379. {
  380. return time - other.time < RelativeTime::milliseconds (maxTimeBetweenMs)
  381. && abs (position.x - other.position.x) < 8
  382. && abs (position.y - other.position.y) < 8
  383. && buttons == other.buttons
  384. && peerID == other.peerID;
  385. }
  386. };
  387. RecentMouseDown mouseDowns[4];
  388. Time lastTime;
  389. bool mouseMovedSignificantlySincePressed;
  390. void registerMouseDown (const Point<int>& screenPos, const Time& time,
  391. Component& component, const ModifierKeys& modifiers) noexcept
  392. {
  393. for (int i = numElementsInArray (mouseDowns); --i > 0;)
  394. mouseDowns[i] = mouseDowns[i - 1];
  395. mouseDowns[0].position = screenPos;
  396. mouseDowns[0].time = time;
  397. mouseDowns[0].buttons = modifiers.withOnlyMouseButtons();
  398. if (ComponentPeer* const peer = component.getPeer())
  399. mouseDowns[0].peerID = peer->getUniqueID();
  400. else
  401. mouseDowns[0].peerID = 0;
  402. mouseMovedSignificantlySincePressed = false;
  403. }
  404. void registerMouseDrag (const Point<int>& screenPos) noexcept
  405. {
  406. mouseMovedSignificantlySincePressed = mouseMovedSignificantlySincePressed
  407. || mouseDowns[0].position.getDistanceFrom (screenPos) >= 4;
  408. }
  409. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MouseInputSourceInternal)
  410. };
  411. //==============================================================================
  412. MouseInputSource::MouseInputSource (const int index, const bool isMouseDevice)
  413. {
  414. pimpl = new MouseInputSourceInternal (*this, index, isMouseDevice);
  415. }
  416. MouseInputSource::~MouseInputSource() {}
  417. bool MouseInputSource::isMouse() const { return pimpl->isMouseDevice; }
  418. bool MouseInputSource::isTouch() const { return ! isMouse(); }
  419. bool MouseInputSource::canHover() const { return isMouse(); }
  420. bool MouseInputSource::hasMouseWheel() const { return isMouse(); }
  421. int MouseInputSource::getIndex() const { return pimpl->index; }
  422. bool MouseInputSource::isDragging() const { return pimpl->isDragging(); }
  423. Point<int> MouseInputSource::getScreenPosition() const { return pimpl->getScreenPosition(); }
  424. ModifierKeys MouseInputSource::getCurrentModifiers() const { return pimpl->getCurrentModifiers(); }
  425. Component* MouseInputSource::getComponentUnderMouse() const { return pimpl->getComponentUnderMouse(); }
  426. void MouseInputSource::triggerFakeMove() const { pimpl->triggerFakeMove(); }
  427. int MouseInputSource::getNumberOfMultipleClicks() const noexcept { return pimpl->getNumberOfMultipleClicks(); }
  428. Time MouseInputSource::getLastMouseDownTime() const noexcept { return pimpl->getLastMouseDownTime(); }
  429. Point<int> MouseInputSource::getLastMouseDownPosition() const noexcept { return pimpl->getLastMouseDownPosition(); }
  430. bool MouseInputSource::hasMouseMovedSignificantlySincePressed() const noexcept { return pimpl->hasMouseMovedSignificantlySincePressed(); }
  431. bool MouseInputSource::canDoUnboundedMovement() const noexcept { return isMouse(); }
  432. void MouseInputSource::enableUnboundedMouseMovement (bool isEnabled, bool keepCursorVisibleUntilOffscreen) { pimpl->enableUnboundedMouseMovement (isEnabled, keepCursorVisibleUntilOffscreen); }
  433. bool MouseInputSource::hasMouseCursor() const noexcept { return isMouse(); }
  434. void MouseInputSource::showMouseCursor (const MouseCursor& cursor) { pimpl->showMouseCursor (cursor, false); }
  435. void MouseInputSource::hideCursor() { pimpl->hideCursor(); }
  436. void MouseInputSource::revealCursor() { pimpl->revealCursor (false); }
  437. void MouseInputSource::forceMouseCursorUpdate() { pimpl->revealCursor (true); }
  438. void MouseInputSource::handleEvent (ComponentPeer* peer, const Point<int>& positionWithinPeer,
  439. const int64 time, const ModifierKeys& mods)
  440. {
  441. pimpl->handleEvent (peer, positionWithinPeer, Time (time), mods.withOnlyMouseButtons());
  442. }
  443. void MouseInputSource::handleWheel (ComponentPeer* const peer, const Point<int>& positionWithinPeer,
  444. const int64 time, const MouseWheelDetails& wheel)
  445. {
  446. pimpl->handleWheel (peer, positionWithinPeer, Time (time), wheel);
  447. }
  448. void MouseInputSource::handleMagnifyGesture (ComponentPeer* const peer, const Point<int>& positionWithinPeer,
  449. const int64 time, const float scaleFactor)
  450. {
  451. pimpl->handleMagnifyGesture (peer, positionWithinPeer, Time (time), scaleFactor);
  452. }