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

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