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