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 20KB

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