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

9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
10 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  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. 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, float newPressure, int64 time)
  69. {
  70. if (MouseInputSource* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (touchIndex))
  71. MouseInputSource (*mouse).handleEvent (*this, pos, time, newMods, newPressure);
  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. return handleKeyPress (KeyPress (keyCode,
  143. ModifierKeys::getCurrentModifiers().withoutMouseButtons(),
  144. textCharacter));
  145. }
  146. bool ComponentPeer::handleKeyPress (const KeyPress& keyInfo)
  147. {
  148. bool keyWasUsed = false;
  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 (c == &component || 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. Point<int> ComponentPeer::localToGlobal (Point<int> p) { return localToGlobal (p.toFloat()).roundToInt(); }
  316. Point<int> ComponentPeer::globalToLocal (Point<int> p) { return globalToLocal (p.toFloat()).roundToInt(); }
  317. Rectangle<int> ComponentPeer::localToGlobal (const Rectangle<int>& relativePosition)
  318. {
  319. return relativePosition.withPosition (localToGlobal (relativePosition.getPosition()));
  320. }
  321. Rectangle<int> ComponentPeer::globalToLocal (const Rectangle<int>& screenPosition)
  322. {
  323. return screenPosition.withPosition (globalToLocal (screenPosition.getPosition()));
  324. }
  325. Rectangle<int> ComponentPeer::getAreaCoveredBy (Component& subComponent) const
  326. {
  327. return ScalingHelpers::scaledScreenPosToUnscaled
  328. (component, component.getLocalArea (&subComponent, subComponent.getLocalBounds()));
  329. }
  330. //==============================================================================
  331. namespace DragHelpers
  332. {
  333. static bool isFileDrag (const ComponentPeer::DragInfo& info)
  334. {
  335. return info.files.size() > 0;
  336. }
  337. static bool isSuitableTarget (const ComponentPeer::DragInfo& info, Component* target)
  338. {
  339. return isFileDrag (info) ? dynamic_cast<FileDragAndDropTarget*> (target) != nullptr
  340. : dynamic_cast<TextDragAndDropTarget*> (target) != nullptr;
  341. }
  342. static bool isInterested (const ComponentPeer::DragInfo& info, Component* target)
  343. {
  344. return isFileDrag (info) ? dynamic_cast<FileDragAndDropTarget*> (target)->isInterestedInFileDrag (info.files)
  345. : dynamic_cast<TextDragAndDropTarget*> (target)->isInterestedInTextDrag (info.text);
  346. }
  347. static Component* findDragAndDropTarget (Component* c, const ComponentPeer::DragInfo& info, Component* const lastOne)
  348. {
  349. for (; c != nullptr; c = c->getParentComponent())
  350. if (isSuitableTarget (info, c) && (c == lastOne || isInterested (info, c)))
  351. return c;
  352. return nullptr;
  353. }
  354. // We'll use an async message to deliver the drop, because if the target decides
  355. // to run a modal loop, it can gum-up the operating system..
  356. class AsyncDropMessage : public CallbackMessage
  357. {
  358. public:
  359. AsyncDropMessage (Component* c, const ComponentPeer::DragInfo& d) : target (c), info (d) {}
  360. void messageCallback() override
  361. {
  362. if (Component* const c = target.get())
  363. {
  364. if (isFileDrag (info))
  365. dynamic_cast<FileDragAndDropTarget*> (c)->filesDropped (info.files, info.position.x, info.position.y);
  366. else
  367. dynamic_cast<TextDragAndDropTarget*> (c)->textDropped (info.text, info.position.x, info.position.y);
  368. }
  369. }
  370. private:
  371. WeakReference<Component> target;
  372. const ComponentPeer::DragInfo info;
  373. JUCE_DECLARE_NON_COPYABLE (AsyncDropMessage)
  374. };
  375. }
  376. bool ComponentPeer::handleDragMove (const ComponentPeer::DragInfo& info)
  377. {
  378. ModifierKeys::updateCurrentModifiers();
  379. Component* const compUnderMouse = component.getComponentAt (info.position);
  380. Component* const lastTarget = dragAndDropTargetComponent;
  381. Component* newTarget = nullptr;
  382. if (compUnderMouse != lastDragAndDropCompUnderMouse)
  383. {
  384. lastDragAndDropCompUnderMouse = compUnderMouse;
  385. newTarget = DragHelpers::findDragAndDropTarget (compUnderMouse, info, lastTarget);
  386. if (newTarget != lastTarget)
  387. {
  388. if (lastTarget != nullptr)
  389. {
  390. if (DragHelpers::isFileDrag (info))
  391. dynamic_cast<FileDragAndDropTarget*> (lastTarget)->fileDragExit (info.files);
  392. else
  393. dynamic_cast<TextDragAndDropTarget*> (lastTarget)->textDragExit (info.text);
  394. }
  395. dragAndDropTargetComponent = nullptr;
  396. if (DragHelpers::isSuitableTarget (info, newTarget))
  397. {
  398. dragAndDropTargetComponent = newTarget;
  399. const Point<int> pos (newTarget->getLocalPoint (&component, info.position));
  400. if (DragHelpers::isFileDrag (info))
  401. dynamic_cast<FileDragAndDropTarget*> (newTarget)->fileDragEnter (info.files, pos.x, pos.y);
  402. else
  403. dynamic_cast<TextDragAndDropTarget*> (newTarget)->textDragEnter (info.text, pos.x, pos.y);
  404. }
  405. }
  406. }
  407. else
  408. {
  409. newTarget = lastTarget;
  410. }
  411. if (! DragHelpers::isSuitableTarget (info, newTarget))
  412. return false;
  413. const Point<int> pos (newTarget->getLocalPoint (&component, info.position));
  414. if (DragHelpers::isFileDrag (info))
  415. dynamic_cast<FileDragAndDropTarget*> (newTarget)->fileDragMove (info.files, pos.x, pos.y);
  416. else
  417. dynamic_cast<TextDragAndDropTarget*> (newTarget)->textDragMove (info.text, pos.x, pos.y);
  418. return true;
  419. }
  420. bool ComponentPeer::handleDragExit (const ComponentPeer::DragInfo& info)
  421. {
  422. DragInfo info2 (info);
  423. info2.position.setXY (-1, -1);
  424. const bool used = handleDragMove (info2);
  425. jassert (dragAndDropTargetComponent == nullptr);
  426. lastDragAndDropCompUnderMouse = nullptr;
  427. return used;
  428. }
  429. bool ComponentPeer::handleDragDrop (const ComponentPeer::DragInfo& info)
  430. {
  431. handleDragMove (info);
  432. if (Component* const targetComp = dragAndDropTargetComponent)
  433. {
  434. dragAndDropTargetComponent = nullptr;
  435. lastDragAndDropCompUnderMouse = nullptr;
  436. if (DragHelpers::isSuitableTarget (info, targetComp))
  437. {
  438. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  439. {
  440. targetComp->internalModalInputAttempt();
  441. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  442. return true;
  443. }
  444. ComponentPeer::DragInfo info2 (info);
  445. info2.position = targetComp->getLocalPoint (&component, info.position);
  446. (new DragHelpers::AsyncDropMessage (targetComp, info2))->post();
  447. return true;
  448. }
  449. }
  450. return false;
  451. }
  452. //==============================================================================
  453. void ComponentPeer::handleUserClosingWindow()
  454. {
  455. ModifierKeys::updateCurrentModifiers();
  456. component.userTriedToCloseWindow();
  457. }
  458. bool ComponentPeer::setDocumentEditedStatus (bool)
  459. {
  460. return false;
  461. }
  462. void ComponentPeer::setRepresentedFile (const File&)
  463. {
  464. }
  465. //==============================================================================
  466. int ComponentPeer::getCurrentRenderingEngine() const { return 0; }
  467. void ComponentPeer::setCurrentRenderingEngine (int index) { jassert (index == 0); ignoreUnused (index); }