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.

juce_ComponentPeer.cpp 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  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. ModifierKeys::updateCurrentModifiers();
  85. Graphics g (contextToPaintTo);
  86. if (component.isTransformed())
  87. g.addTransform (component.getTransform());
  88. auto peerBounds = getBounds();
  89. if (peerBounds.getWidth() != component.getWidth() || peerBounds.getHeight() != component.getHeight())
  90. // Tweak the scaling so that the component's integer size exactly aligns with the peer's scaled size
  91. g.addTransform (AffineTransform::scale (peerBounds.getWidth() / (float) component.getWidth(),
  92. peerBounds.getHeight() / (float) component.getHeight()));
  93. #if JUCE_ENABLE_REPAINT_DEBUGGING
  94. #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
  95. if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
  96. #endif
  97. {
  98. g.saveState();
  99. }
  100. #endif
  101. JUCE_TRY
  102. {
  103. component.paintEntireComponent (g, true);
  104. }
  105. JUCE_CATCH_EXCEPTION
  106. #if JUCE_ENABLE_REPAINT_DEBUGGING
  107. #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
  108. if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
  109. #endif
  110. {
  111. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  112. // clearly when things are being repainted.
  113. g.restoreState();
  114. static Random rng;
  115. g.fillAll (Colour ((uint8) rng.nextInt (255),
  116. (uint8) rng.nextInt (255),
  117. (uint8) rng.nextInt (255),
  118. (uint8) 0x50));
  119. }
  120. #endif
  121. /** If this fails, it's probably be because your CPU floating-point precision mode has
  122. been set to low.. This setting is sometimes changed by things like Direct3D, and can
  123. mess up a lot of the calculations that the library needs to do.
  124. */
  125. jassert (roundToInt (10.1f) == 10);
  126. }
  127. Component* ComponentPeer::getTargetForKeyPress()
  128. {
  129. auto* c = Component::getCurrentlyFocusedComponent();
  130. if (c == nullptr)
  131. c = &component;
  132. if (c->isCurrentlyBlockedByAnotherModalComponent())
  133. if (auto* currentModalComp = Component::getCurrentlyModalComponent())
  134. c = currentModalComp;
  135. return c;
  136. }
  137. bool ComponentPeer::handleKeyPress (const int keyCode, const juce_wchar textCharacter)
  138. {
  139. ModifierKeys::updateCurrentModifiers();
  140. return handleKeyPress (KeyPress (keyCode,
  141. ModifierKeys::getCurrentModifiers().withoutMouseButtons(),
  142. textCharacter));
  143. }
  144. bool ComponentPeer::handleKeyPress (const KeyPress& keyInfo)
  145. {
  146. bool keyWasUsed = false;
  147. for (auto* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
  148. {
  149. const WeakReference<Component> deletionChecker (target);
  150. if (auto* keyListeners = target->keyListeners.get())
  151. {
  152. for (int i = keyListeners->size(); --i >= 0;)
  153. {
  154. keyWasUsed = keyListeners->getUnchecked(i)->keyPressed (keyInfo, target);
  155. if (keyWasUsed || deletionChecker == nullptr)
  156. return keyWasUsed;
  157. i = jmin (i, keyListeners->size());
  158. }
  159. }
  160. keyWasUsed = target->keyPressed (keyInfo);
  161. if (keyWasUsed || deletionChecker == nullptr)
  162. break;
  163. if (auto* currentlyFocused = Component::getCurrentlyFocusedComponent())
  164. {
  165. const bool isTab = (keyInfo == KeyPress::tabKey);
  166. const bool isShiftTab = (keyInfo == KeyPress (KeyPress::tabKey, ModifierKeys::shiftModifier, 0));
  167. if (isTab || isShiftTab)
  168. {
  169. currentlyFocused->moveKeyboardFocusToSibling (isTab);
  170. keyWasUsed = (currentlyFocused != Component::getCurrentlyFocusedComponent());
  171. if (keyWasUsed || deletionChecker == nullptr)
  172. break;
  173. }
  174. }
  175. }
  176. return keyWasUsed;
  177. }
  178. bool ComponentPeer::handleKeyUpOrDown (const bool isKeyDown)
  179. {
  180. ModifierKeys::updateCurrentModifiers();
  181. bool keyWasUsed = false;
  182. for (auto* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
  183. {
  184. const WeakReference<Component> deletionChecker (target);
  185. keyWasUsed = target->keyStateChanged (isKeyDown);
  186. if (keyWasUsed || deletionChecker == nullptr)
  187. break;
  188. if (auto* keyListeners = target->keyListeners.get())
  189. {
  190. for (int i = keyListeners->size(); --i >= 0;)
  191. {
  192. keyWasUsed = keyListeners->getUnchecked(i)->keyStateChanged (isKeyDown, target);
  193. if (keyWasUsed || deletionChecker == nullptr)
  194. return keyWasUsed;
  195. i = jmin (i, keyListeners->size());
  196. }
  197. }
  198. }
  199. return keyWasUsed;
  200. }
  201. void ComponentPeer::handleModifierKeysChange()
  202. {
  203. ModifierKeys::updateCurrentModifiers();
  204. auto* target = Desktop::getInstance().getMainMouseSource().getComponentUnderMouse();
  205. if (target == nullptr)
  206. target = Component::getCurrentlyFocusedComponent();
  207. if (target == nullptr)
  208. target = &component;
  209. target->internalModifierKeysChanged();
  210. }
  211. TextInputTarget* ComponentPeer::findCurrentTextInputTarget()
  212. {
  213. auto* c = Component::getCurrentlyFocusedComponent();
  214. if (c == &component || component.isParentOf (c))
  215. if (auto* 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. auto newBounds = Component::ComponentHelpers::rawPeerPositionToLocal (component, getBounds());
  239. auto 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.boundsRelativeToParent = 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. }
  352. bool ComponentPeer::handleDragMove (const ComponentPeer::DragInfo& info)
  353. {
  354. ModifierKeys::updateCurrentModifiers();
  355. Component* const compUnderMouse = component.getComponentAt (info.position);
  356. Component* const lastTarget = dragAndDropTargetComponent;
  357. Component* newTarget = nullptr;
  358. if (compUnderMouse != lastDragAndDropCompUnderMouse)
  359. {
  360. lastDragAndDropCompUnderMouse = compUnderMouse;
  361. newTarget = DragHelpers::findDragAndDropTarget (compUnderMouse, info, lastTarget);
  362. if (newTarget != lastTarget)
  363. {
  364. if (lastTarget != nullptr)
  365. {
  366. if (DragHelpers::isFileDrag (info))
  367. dynamic_cast<FileDragAndDropTarget*> (lastTarget)->fileDragExit (info.files);
  368. else
  369. dynamic_cast<TextDragAndDropTarget*> (lastTarget)->textDragExit (info.text);
  370. }
  371. dragAndDropTargetComponent = nullptr;
  372. if (DragHelpers::isSuitableTarget (info, newTarget))
  373. {
  374. dragAndDropTargetComponent = newTarget;
  375. auto pos = newTarget->getLocalPoint (&component, info.position);
  376. if (DragHelpers::isFileDrag (info))
  377. dynamic_cast<FileDragAndDropTarget*> (newTarget)->fileDragEnter (info.files, pos.x, pos.y);
  378. else
  379. dynamic_cast<TextDragAndDropTarget*> (newTarget)->textDragEnter (info.text, pos.x, pos.y);
  380. }
  381. }
  382. }
  383. else
  384. {
  385. newTarget = lastTarget;
  386. }
  387. if (! DragHelpers::isSuitableTarget (info, newTarget))
  388. return false;
  389. auto pos = newTarget->getLocalPoint (&component, info.position);
  390. if (DragHelpers::isFileDrag (info))
  391. dynamic_cast<FileDragAndDropTarget*> (newTarget)->fileDragMove (info.files, pos.x, pos.y);
  392. else
  393. dynamic_cast<TextDragAndDropTarget*> (newTarget)->textDragMove (info.text, pos.x, pos.y);
  394. return true;
  395. }
  396. bool ComponentPeer::handleDragExit (const ComponentPeer::DragInfo& info)
  397. {
  398. DragInfo info2 (info);
  399. info2.position.setXY (-1, -1);
  400. const bool used = handleDragMove (info2);
  401. jassert (dragAndDropTargetComponent == nullptr);
  402. lastDragAndDropCompUnderMouse = nullptr;
  403. return used;
  404. }
  405. bool ComponentPeer::handleDragDrop (const ComponentPeer::DragInfo& info)
  406. {
  407. handleDragMove (info);
  408. if (WeakReference<Component> targetComp = dragAndDropTargetComponent)
  409. {
  410. dragAndDropTargetComponent = nullptr;
  411. lastDragAndDropCompUnderMouse = nullptr;
  412. if (DragHelpers::isSuitableTarget (info, targetComp))
  413. {
  414. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  415. {
  416. targetComp->internalModalInputAttempt();
  417. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  418. return true;
  419. }
  420. ComponentPeer::DragInfo infoCopy (info);
  421. infoCopy.position = targetComp->getLocalPoint (&component, info.position);
  422. // We'll use an async message to deliver the drop, because if the target decides
  423. // to run a modal loop, it can gum-up the operating system..
  424. MessageManager::callAsync ([=]()
  425. {
  426. if (auto* c = targetComp.get())
  427. {
  428. if (DragHelpers::isFileDrag (info))
  429. dynamic_cast<FileDragAndDropTarget*> (c)->filesDropped (infoCopy.files, infoCopy.position.x, infoCopy.position.y);
  430. else
  431. dynamic_cast<TextDragAndDropTarget*> (c)->textDropped (infoCopy.text, infoCopy.position.x, infoCopy.position.y);
  432. }
  433. });
  434. return true;
  435. }
  436. }
  437. return false;
  438. }
  439. //==============================================================================
  440. void ComponentPeer::handleUserClosingWindow()
  441. {
  442. ModifierKeys::updateCurrentModifiers();
  443. component.userTriedToCloseWindow();
  444. }
  445. bool ComponentPeer::setDocumentEditedStatus (bool)
  446. {
  447. return false;
  448. }
  449. void ComponentPeer::setRepresentedFile (const File&)
  450. {
  451. }
  452. //==============================================================================
  453. int ComponentPeer::getCurrentRenderingEngine() const { return 0; }
  454. void ComponentPeer::setCurrentRenderingEngine (int index) { jassert (index == 0); ignoreUnused (index); }
  455. } // namespace juce