The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

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