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.

576 lines
19KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI 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. static uint32 lastUniquePeerID = 1;
  18. //==============================================================================
  19. ComponentPeer::ComponentPeer (Component& comp, const int flags)
  20. : component (comp),
  21. styleFlags (flags),
  22. uniqueID (lastUniquePeerID += 2) // increment by 2 so that this can never hit 0
  23. {
  24. Desktop::getInstance().peers.add (this);
  25. }
  26. ComponentPeer::~ComponentPeer()
  27. {
  28. auto& desktop = Desktop::getInstance();
  29. desktop.peers.removeFirstMatchingValue (this);
  30. desktop.triggerFocusCallback();
  31. }
  32. //==============================================================================
  33. int ComponentPeer::getNumPeers() noexcept
  34. {
  35. return Desktop::getInstance().peers.size();
  36. }
  37. ComponentPeer* ComponentPeer::getPeer (const int index) noexcept
  38. {
  39. return Desktop::getInstance().peers [index];
  40. }
  41. ComponentPeer* ComponentPeer::getPeerFor (const Component* const component) noexcept
  42. {
  43. for (auto* peer : Desktop::getInstance().peers)
  44. if (&(peer->getComponent()) == component)
  45. return peer;
  46. return nullptr;
  47. }
  48. bool ComponentPeer::isValidPeer (const ComponentPeer* const peer) noexcept
  49. {
  50. return Desktop::getInstance().peers.contains (const_cast<ComponentPeer*> (peer));
  51. }
  52. void ComponentPeer::updateBounds()
  53. {
  54. setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component, component.getBoundsInParent()), false);
  55. }
  56. bool ComponentPeer::isKioskMode() const
  57. {
  58. return Desktop::getInstance().getKioskModeComponent() == &component;
  59. }
  60. //==============================================================================
  61. void ComponentPeer::handleMouseEvent (MouseInputSource::InputSourceType type, Point<float> pos, ModifierKeys newMods,
  62. float newPressure, float newOrientation, int64 time, PenDetails pen, int touchIndex)
  63. {
  64. if (auto* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (type, touchIndex))
  65. MouseInputSource (*mouse).handleEvent (*this, pos, time, newMods, newPressure, newOrientation, pen);
  66. }
  67. void ComponentPeer::handleMouseWheel (MouseInputSource::InputSourceType type, Point<float> pos, int64 time, const MouseWheelDetails& wheel, int touchIndex)
  68. {
  69. if (auto* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (type, touchIndex))
  70. MouseInputSource (*mouse).handleWheel (*this, pos, time, wheel);
  71. }
  72. void ComponentPeer::handleMagnifyGesture (MouseInputSource::InputSourceType type, Point<float> pos, int64 time, float scaleFactor, int touchIndex)
  73. {
  74. if (auto* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (type, touchIndex))
  75. MouseInputSource (*mouse).handleMagnifyGesture (*this, pos, time, scaleFactor);
  76. }
  77. //==============================================================================
  78. void ComponentPeer::handlePaint (LowLevelGraphicsContext& contextToPaintTo)
  79. {
  80. ModifierKeys::updateCurrentModifiers();
  81. Graphics g (contextToPaintTo);
  82. if (component.isTransformed())
  83. g.addTransform (component.getTransform());
  84. auto peerBounds = getBounds();
  85. if (peerBounds.getWidth() != component.getWidth() || peerBounds.getHeight() != component.getHeight())
  86. // Tweak the scaling so that the component's integer size exactly aligns with the peer's scaled size
  87. g.addTransform (AffineTransform::scale (peerBounds.getWidth() / (float) component.getWidth(),
  88. peerBounds.getHeight() / (float) component.getHeight()));
  89. #if JUCE_ENABLE_REPAINT_DEBUGGING
  90. #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
  91. if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
  92. #endif
  93. {
  94. g.saveState();
  95. }
  96. #endif
  97. JUCE_TRY
  98. {
  99. component.paintEntireComponent (g, true);
  100. }
  101. JUCE_CATCH_EXCEPTION
  102. #if JUCE_ENABLE_REPAINT_DEBUGGING
  103. #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
  104. if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
  105. #endif
  106. {
  107. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  108. // clearly when things are being repainted.
  109. g.restoreState();
  110. static Random rng;
  111. g.fillAll (Colour ((uint8) rng.nextInt (255),
  112. (uint8) rng.nextInt (255),
  113. (uint8) rng.nextInt (255),
  114. (uint8) 0x50));
  115. }
  116. #endif
  117. /** If this fails, it's probably be because your CPU floating-point precision mode has
  118. been set to low.. This setting is sometimes changed by things like Direct3D, and can
  119. mess up a lot of the calculations that the library needs to do.
  120. */
  121. jassert (roundToInt (10.1f) == 10);
  122. }
  123. Component* ComponentPeer::getTargetForKeyPress()
  124. {
  125. Component* c = Component::getCurrentlyFocusedComponent();
  126. if (c == nullptr)
  127. c = &component;
  128. if (c->isCurrentlyBlockedByAnotherModalComponent())
  129. if (auto* currentModalComp = Component::getCurrentlyModalComponent())
  130. c = currentModalComp;
  131. return c;
  132. }
  133. bool ComponentPeer::handleKeyPress (const int keyCode, const juce_wchar textCharacter)
  134. {
  135. ModifierKeys::updateCurrentModifiers();
  136. return handleKeyPress (KeyPress (keyCode,
  137. ModifierKeys::getCurrentModifiers().withoutMouseButtons(),
  138. textCharacter));
  139. }
  140. bool ComponentPeer::handleKeyPress (const KeyPress& keyInfo)
  141. {
  142. bool keyWasUsed = false;
  143. for (Component* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
  144. {
  145. const WeakReference<Component> deletionChecker (target);
  146. if (auto* keyListeners = target->keyListeners.get())
  147. {
  148. for (int i = keyListeners->size(); --i >= 0;)
  149. {
  150. keyWasUsed = keyListeners->getUnchecked(i)->keyPressed (keyInfo, target);
  151. if (keyWasUsed || deletionChecker == nullptr)
  152. return keyWasUsed;
  153. i = jmin (i, keyListeners->size());
  154. }
  155. }
  156. keyWasUsed = target->keyPressed (keyInfo);
  157. if (keyWasUsed || deletionChecker == nullptr)
  158. break;
  159. if (auto* currentlyFocused = Component::getCurrentlyFocusedComponent())
  160. {
  161. const bool isTab = (keyInfo == KeyPress::tabKey);
  162. const bool isShiftTab = (keyInfo == KeyPress (KeyPress::tabKey, ModifierKeys::shiftModifier, 0));
  163. if (isTab || isShiftTab)
  164. {
  165. currentlyFocused->moveKeyboardFocusToSibling (isTab);
  166. keyWasUsed = (currentlyFocused != Component::getCurrentlyFocusedComponent());
  167. if (keyWasUsed || deletionChecker == nullptr)
  168. break;
  169. }
  170. }
  171. }
  172. return keyWasUsed;
  173. }
  174. bool ComponentPeer::handleKeyUpOrDown (const bool isKeyDown)
  175. {
  176. ModifierKeys::updateCurrentModifiers();
  177. bool keyWasUsed = false;
  178. for (Component* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
  179. {
  180. const WeakReference<Component> deletionChecker (target);
  181. keyWasUsed = target->keyStateChanged (isKeyDown);
  182. if (keyWasUsed || deletionChecker == nullptr)
  183. break;
  184. if (const Array<KeyListener*>* const keyListeners = target->keyListeners)
  185. {
  186. for (int i = keyListeners->size(); --i >= 0;)
  187. {
  188. keyWasUsed = keyListeners->getUnchecked(i)->keyStateChanged (isKeyDown, target);
  189. if (keyWasUsed || deletionChecker == nullptr)
  190. return keyWasUsed;
  191. i = jmin (i, keyListeners->size());
  192. }
  193. }
  194. }
  195. return keyWasUsed;
  196. }
  197. void ComponentPeer::handleModifierKeysChange()
  198. {
  199. ModifierKeys::updateCurrentModifiers();
  200. auto* target = Desktop::getInstance().getMainMouseSource().getComponentUnderMouse();
  201. if (target == nullptr)
  202. target = Component::getCurrentlyFocusedComponent();
  203. if (target == nullptr)
  204. target = &component;
  205. if (target != nullptr)
  206. target->internalModifierKeysChanged();
  207. }
  208. TextInputTarget* ComponentPeer::findCurrentTextInputTarget()
  209. {
  210. auto* c = Component::getCurrentlyFocusedComponent();
  211. if (c == &component || component.isParentOf (c))
  212. if (TextInputTarget* const ti = dynamic_cast<TextInputTarget*> (c))
  213. if (ti->isTextInputActive())
  214. return ti;
  215. return nullptr;
  216. }
  217. void ComponentPeer::dismissPendingTextInput() {}
  218. //==============================================================================
  219. void ComponentPeer::handleBroughtToFront()
  220. {
  221. ModifierKeys::updateCurrentModifiers();
  222. component.internalBroughtToFront();
  223. }
  224. void ComponentPeer::setConstrainer (ComponentBoundsConstrainer* const newConstrainer) noexcept
  225. {
  226. constrainer = newConstrainer;
  227. }
  228. void ComponentPeer::handleMovedOrResized()
  229. {
  230. ModifierKeys::updateCurrentModifiers();
  231. const bool nowMinimised = isMinimised();
  232. if (component.flags.hasHeavyweightPeerFlag && ! nowMinimised)
  233. {
  234. const WeakReference<Component> deletionChecker (&component);
  235. auto newBounds = Component::ComponentHelpers::rawPeerPositionToLocal (component, getBounds());
  236. auto oldBounds = component.getBounds();
  237. const bool wasMoved = (oldBounds.getPosition() != newBounds.getPosition());
  238. const bool wasResized = (oldBounds.getWidth() != newBounds.getWidth() || oldBounds.getHeight() != newBounds.getHeight());
  239. if (wasMoved || wasResized)
  240. {
  241. component.boundsRelativeToParent = newBounds;
  242. if (wasResized)
  243. component.repaint();
  244. component.sendMovedResizedMessages (wasMoved, wasResized);
  245. if (deletionChecker == nullptr)
  246. return;
  247. }
  248. }
  249. if (isWindowMinimised != nowMinimised)
  250. {
  251. isWindowMinimised = nowMinimised;
  252. component.minimisationStateChanged (nowMinimised);
  253. component.sendVisibilityChangeMessage();
  254. }
  255. if (! isFullScreen())
  256. lastNonFullscreenBounds = component.getBounds();
  257. }
  258. void ComponentPeer::handleFocusGain()
  259. {
  260. ModifierKeys::updateCurrentModifiers();
  261. if (component.isParentOf (lastFocusedComponent))
  262. {
  263. Component::currentlyFocusedComponent = lastFocusedComponent;
  264. Desktop::getInstance().triggerFocusCallback();
  265. lastFocusedComponent->internalFocusGain (Component::focusChangedDirectly);
  266. }
  267. else
  268. {
  269. if (! component.isCurrentlyBlockedByAnotherModalComponent())
  270. component.grabKeyboardFocus();
  271. else
  272. ModalComponentManager::getInstance()->bringModalComponentsToFront();
  273. }
  274. }
  275. void ComponentPeer::handleFocusLoss()
  276. {
  277. ModifierKeys::updateCurrentModifiers();
  278. if (component.hasKeyboardFocus (true))
  279. {
  280. lastFocusedComponent = Component::currentlyFocusedComponent;
  281. if (lastFocusedComponent != nullptr)
  282. {
  283. Component::currentlyFocusedComponent = nullptr;
  284. Desktop::getInstance().triggerFocusCallback();
  285. lastFocusedComponent->internalFocusLoss (Component::focusChangedByMouseClick);
  286. }
  287. }
  288. }
  289. Component* ComponentPeer::getLastFocusedSubcomponent() const noexcept
  290. {
  291. return (component.isParentOf (lastFocusedComponent) && lastFocusedComponent->isShowing())
  292. ? static_cast<Component*> (lastFocusedComponent)
  293. : &component;
  294. }
  295. void ComponentPeer::handleScreenSizeChange()
  296. {
  297. ModifierKeys::updateCurrentModifiers();
  298. component.parentSizeChanged();
  299. handleMovedOrResized();
  300. }
  301. void ComponentPeer::setNonFullScreenBounds (const Rectangle<int>& newBounds) noexcept
  302. {
  303. lastNonFullscreenBounds = newBounds;
  304. }
  305. const Rectangle<int>& ComponentPeer::getNonFullScreenBounds() const noexcept
  306. {
  307. return lastNonFullscreenBounds;
  308. }
  309. Point<int> ComponentPeer::localToGlobal (Point<int> p) { return localToGlobal (p.toFloat()).roundToInt(); }
  310. Point<int> ComponentPeer::globalToLocal (Point<int> p) { return globalToLocal (p.toFloat()).roundToInt(); }
  311. Rectangle<int> ComponentPeer::localToGlobal (const Rectangle<int>& relativePosition)
  312. {
  313. return relativePosition.withPosition (localToGlobal (relativePosition.getPosition()));
  314. }
  315. Rectangle<int> ComponentPeer::globalToLocal (const Rectangle<int>& screenPosition)
  316. {
  317. return screenPosition.withPosition (globalToLocal (screenPosition.getPosition()));
  318. }
  319. Rectangle<int> ComponentPeer::getAreaCoveredBy (Component& subComponent) const
  320. {
  321. return ScalingHelpers::scaledScreenPosToUnscaled
  322. (component, component.getLocalArea (&subComponent, subComponent.getLocalBounds()));
  323. }
  324. //==============================================================================
  325. namespace DragHelpers
  326. {
  327. static bool isFileDrag (const ComponentPeer::DragInfo& info)
  328. {
  329. return info.files.size() > 0;
  330. }
  331. static bool isSuitableTarget (const ComponentPeer::DragInfo& info, Component* target)
  332. {
  333. return isFileDrag (info) ? dynamic_cast<FileDragAndDropTarget*> (target) != nullptr
  334. : dynamic_cast<TextDragAndDropTarget*> (target) != nullptr;
  335. }
  336. static bool isInterested (const ComponentPeer::DragInfo& info, Component* target)
  337. {
  338. return isFileDrag (info) ? dynamic_cast<FileDragAndDropTarget*> (target)->isInterestedInFileDrag (info.files)
  339. : dynamic_cast<TextDragAndDropTarget*> (target)->isInterestedInTextDrag (info.text);
  340. }
  341. static Component* findDragAndDropTarget (Component* c, const ComponentPeer::DragInfo& info, Component* const lastOne)
  342. {
  343. for (; c != nullptr; c = c->getParentComponent())
  344. if (isSuitableTarget (info, c) && (c == lastOne || isInterested (info, c)))
  345. return c;
  346. return nullptr;
  347. }
  348. }
  349. bool ComponentPeer::handleDragMove (const ComponentPeer::DragInfo& info)
  350. {
  351. ModifierKeys::updateCurrentModifiers();
  352. Component* const compUnderMouse = component.getComponentAt (info.position);
  353. Component* const lastTarget = dragAndDropTargetComponent;
  354. Component* newTarget = nullptr;
  355. if (compUnderMouse != lastDragAndDropCompUnderMouse)
  356. {
  357. lastDragAndDropCompUnderMouse = compUnderMouse;
  358. newTarget = DragHelpers::findDragAndDropTarget (compUnderMouse, info, lastTarget);
  359. if (newTarget != lastTarget)
  360. {
  361. if (lastTarget != nullptr)
  362. {
  363. if (DragHelpers::isFileDrag (info))
  364. dynamic_cast<FileDragAndDropTarget*> (lastTarget)->fileDragExit (info.files);
  365. else
  366. dynamic_cast<TextDragAndDropTarget*> (lastTarget)->textDragExit (info.text);
  367. }
  368. dragAndDropTargetComponent = nullptr;
  369. if (DragHelpers::isSuitableTarget (info, newTarget))
  370. {
  371. dragAndDropTargetComponent = newTarget;
  372. auto pos = newTarget->getLocalPoint (&component, info.position);
  373. if (DragHelpers::isFileDrag (info))
  374. dynamic_cast<FileDragAndDropTarget*> (newTarget)->fileDragEnter (info.files, pos.x, pos.y);
  375. else
  376. dynamic_cast<TextDragAndDropTarget*> (newTarget)->textDragEnter (info.text, pos.x, pos.y);
  377. }
  378. }
  379. }
  380. else
  381. {
  382. newTarget = lastTarget;
  383. }
  384. if (! DragHelpers::isSuitableTarget (info, newTarget))
  385. return false;
  386. auto pos = newTarget->getLocalPoint (&component, info.position);
  387. if (DragHelpers::isFileDrag (info))
  388. dynamic_cast<FileDragAndDropTarget*> (newTarget)->fileDragMove (info.files, pos.x, pos.y);
  389. else
  390. dynamic_cast<TextDragAndDropTarget*> (newTarget)->textDragMove (info.text, pos.x, pos.y);
  391. return true;
  392. }
  393. bool ComponentPeer::handleDragExit (const ComponentPeer::DragInfo& info)
  394. {
  395. DragInfo info2 (info);
  396. info2.position.setXY (-1, -1);
  397. const bool used = handleDragMove (info2);
  398. jassert (dragAndDropTargetComponent == nullptr);
  399. lastDragAndDropCompUnderMouse = nullptr;
  400. return used;
  401. }
  402. bool ComponentPeer::handleDragDrop (const ComponentPeer::DragInfo& info)
  403. {
  404. handleDragMove (info);
  405. if (WeakReference<Component> targetComp = dragAndDropTargetComponent)
  406. {
  407. dragAndDropTargetComponent = nullptr;
  408. lastDragAndDropCompUnderMouse = nullptr;
  409. if (DragHelpers::isSuitableTarget (info, targetComp))
  410. {
  411. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  412. {
  413. targetComp->internalModalInputAttempt();
  414. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  415. return true;
  416. }
  417. ComponentPeer::DragInfo infoCopy (info);
  418. infoCopy.position = targetComp->getLocalPoint (&component, info.position);
  419. // We'll use an async message to deliver the drop, because if the target decides
  420. // to run a modal loop, it can gum-up the operating system..
  421. MessageManager::callAsync ([=]()
  422. {
  423. if (auto* c = targetComp.get())
  424. {
  425. if (DragHelpers::isFileDrag (info))
  426. dynamic_cast<FileDragAndDropTarget*> (c)->filesDropped (infoCopy.files, infoCopy.position.x, infoCopy.position.y);
  427. else
  428. dynamic_cast<TextDragAndDropTarget*> (c)->textDropped (infoCopy.text, infoCopy.position.x, infoCopy.position.y);
  429. }
  430. });
  431. return true;
  432. }
  433. }
  434. return false;
  435. }
  436. //==============================================================================
  437. void ComponentPeer::handleUserClosingWindow()
  438. {
  439. ModifierKeys::updateCurrentModifiers();
  440. component.userTriedToCloseWindow();
  441. }
  442. bool ComponentPeer::setDocumentEditedStatus (bool)
  443. {
  444. return false;
  445. }
  446. void ComponentPeer::setRepresentedFile (const File&)
  447. {
  448. }
  449. //==============================================================================
  450. int ComponentPeer::getCurrentRenderingEngine() const { return 0; }
  451. void ComponentPeer::setCurrentRenderingEngine (int index) { jassert (index == 0); ignoreUnused (index); }