Audio plugin host https://kx.studio/carla
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.

592 lines
20KB

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