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.

593 lines
19KB

  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 (Component::ComponentHelpers::scaledScreenPosToUnscaled (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().getOrCreateMouseInputSource (touchIndex))
  68. 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().getOrCreateMouseInputSource (touchIndex))
  74. 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().getOrCreateMouseInputSource (touchIndex))
  80. 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. float masterScale = Desktop::getInstance().masterScaleFactor;
  90. if (masterScale != 1.0f)
  91. g.addTransform (AffineTransform::scale (masterScale));
  92. #if JUCE_ENABLE_REPAINT_DEBUGGING
  93. g.saveState();
  94. #endif
  95. JUCE_TRY
  96. {
  97. component.paintEntireComponent (g, true);
  98. }
  99. JUCE_CATCH_EXCEPTION
  100. #if JUCE_ENABLE_REPAINT_DEBUGGING
  101. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  102. // clearly when things are being repainted.
  103. g.restoreState();
  104. static Random rng;
  105. g.fillAll (Colour ((uint8) rng.nextInt (255),
  106. (uint8) rng.nextInt (255),
  107. (uint8) rng.nextInt (255),
  108. (uint8) 0x50));
  109. #endif
  110. /** If this fails, it's probably be because your CPU floating-point precision mode has
  111. been set to low.. This setting is sometimes changed by things like Direct3D, and can
  112. mess up a lot of the calculations that the library needs to do.
  113. */
  114. jassert (roundToInt (10.1f) == 10);
  115. }
  116. Component* ComponentPeer::getTargetForKeyPress()
  117. {
  118. Component* c = Component::getCurrentlyFocusedComponent();
  119. if (c == nullptr)
  120. c = &component;
  121. if (c->isCurrentlyBlockedByAnotherModalComponent())
  122. if (Component* const currentModalComp = Component::getCurrentlyModalComponent())
  123. c = currentModalComp;
  124. return c;
  125. }
  126. bool ComponentPeer::handleKeyPress (const int keyCode, const juce_wchar textCharacter)
  127. {
  128. ModifierKeys::updateCurrentModifiers();
  129. bool keyWasUsed = false;
  130. const KeyPress keyInfo (keyCode,
  131. ModifierKeys::getCurrentModifiers().withoutMouseButtons(),
  132. textCharacter);
  133. for (Component* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
  134. {
  135. const WeakReference<Component> deletionChecker (target);
  136. if (const Array <KeyListener*>* const keyListeners = target->keyListeners)
  137. {
  138. for (int i = keyListeners->size(); --i >= 0;)
  139. {
  140. keyWasUsed = keyListeners->getUnchecked(i)->keyPressed (keyInfo, target);
  141. if (keyWasUsed || deletionChecker == nullptr)
  142. return keyWasUsed;
  143. i = jmin (i, keyListeners->size());
  144. }
  145. }
  146. keyWasUsed = target->keyPressed (keyInfo);
  147. if (keyWasUsed || deletionChecker == nullptr)
  148. break;
  149. if (Component* const currentlyFocused = Component::getCurrentlyFocusedComponent())
  150. {
  151. const bool isTab = (keyInfo == KeyPress::tabKey);
  152. const bool isShiftTab = (keyInfo == KeyPress (KeyPress::tabKey, ModifierKeys::shiftModifier, 0));
  153. if (isTab || isShiftTab)
  154. {
  155. currentlyFocused->moveKeyboardFocusToSibling (isTab);
  156. keyWasUsed = (currentlyFocused != Component::getCurrentlyFocusedComponent());
  157. if (keyWasUsed || deletionChecker == nullptr)
  158. break;
  159. }
  160. }
  161. }
  162. return keyWasUsed;
  163. }
  164. bool ComponentPeer::handleKeyUpOrDown (const bool isKeyDown)
  165. {
  166. ModifierKeys::updateCurrentModifiers();
  167. bool keyWasUsed = false;
  168. for (Component* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
  169. {
  170. const WeakReference<Component> deletionChecker (target);
  171. keyWasUsed = target->keyStateChanged (isKeyDown);
  172. if (keyWasUsed || deletionChecker == nullptr)
  173. break;
  174. if (const Array <KeyListener*>* const keyListeners = target->keyListeners)
  175. {
  176. for (int i = keyListeners->size(); --i >= 0;)
  177. {
  178. keyWasUsed = keyListeners->getUnchecked(i)->keyStateChanged (isKeyDown, target);
  179. if (keyWasUsed || deletionChecker == nullptr)
  180. return keyWasUsed;
  181. i = jmin (i, keyListeners->size());
  182. }
  183. }
  184. }
  185. return keyWasUsed;
  186. }
  187. void ComponentPeer::handleModifierKeysChange()
  188. {
  189. ModifierKeys::updateCurrentModifiers();
  190. Component* target = Desktop::getInstance().getMainMouseSource().getComponentUnderMouse();
  191. if (target == nullptr)
  192. target = Component::getCurrentlyFocusedComponent();
  193. if (target == nullptr)
  194. target = &component;
  195. if (target != nullptr)
  196. target->internalModifierKeysChanged();
  197. }
  198. TextInputTarget* ComponentPeer::findCurrentTextInputTarget()
  199. {
  200. Component* const c = Component::getCurrentlyFocusedComponent();
  201. if (component.isParentOf (c))
  202. if (TextInputTarget* const ti = dynamic_cast <TextInputTarget*> (c))
  203. if (ti->isTextInputActive())
  204. return ti;
  205. return nullptr;
  206. }
  207. void ComponentPeer::dismissPendingTextInput() {}
  208. //==============================================================================
  209. void ComponentPeer::handleBroughtToFront()
  210. {
  211. ModifierKeys::updateCurrentModifiers();
  212. component.internalBroughtToFront();
  213. }
  214. void ComponentPeer::setConstrainer (ComponentBoundsConstrainer* const newConstrainer) noexcept
  215. {
  216. constrainer = newConstrainer;
  217. }
  218. void ComponentPeer::handleMovedOrResized()
  219. {
  220. ModifierKeys::updateCurrentModifiers();
  221. const bool nowMinimised = isMinimised();
  222. if (component.flags.hasHeavyweightPeerFlag && ! nowMinimised)
  223. {
  224. const WeakReference<Component> deletionChecker (&component);
  225. Rectangle<int> newBounds (getBounds());
  226. Rectangle<int> oldBounds (component.getBounds());
  227. oldBounds = Component::ComponentHelpers::localPositionToRawPeerPos (component, oldBounds);
  228. const bool wasMoved = (oldBounds.getPosition() != newBounds.getPosition());
  229. const bool wasResized = (oldBounds.getWidth() != newBounds.getWidth() || oldBounds.getHeight() != newBounds.getHeight());
  230. if (wasMoved || wasResized)
  231. {
  232. newBounds = Component::ComponentHelpers::rawPeerPositionToLocal (component, newBounds);
  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. jassert (component.isParentOf (&subComponent)); // this can only be used for child components.
  312. return Component::ComponentHelpers::scaledScreenPosToUnscaled
  313. (component.getLocalArea (&subComponent, subComponent.getLocalBounds()));
  314. }
  315. //==============================================================================
  316. namespace DragHelpers
  317. {
  318. static bool isFileDrag (const ComponentPeer::DragInfo& info)
  319. {
  320. return info.files.size() > 0;
  321. }
  322. static bool isSuitableTarget (const ComponentPeer::DragInfo& info, Component* target)
  323. {
  324. return isFileDrag (info) ? dynamic_cast <FileDragAndDropTarget*> (target) != nullptr
  325. : dynamic_cast <TextDragAndDropTarget*> (target) != nullptr;
  326. }
  327. static bool isInterested (const ComponentPeer::DragInfo& info, Component* target)
  328. {
  329. return isFileDrag (info) ? dynamic_cast <FileDragAndDropTarget*> (target)->isInterestedInFileDrag (info.files)
  330. : dynamic_cast <TextDragAndDropTarget*> (target)->isInterestedInTextDrag (info.text);
  331. }
  332. static Component* findDragAndDropTarget (Component* c, const ComponentPeer::DragInfo& info, Component* const lastOne)
  333. {
  334. for (; c != nullptr; c = c->getParentComponent())
  335. if (isSuitableTarget (info, c) && (c == lastOne || isInterested (info, c)))
  336. return c;
  337. return nullptr;
  338. }
  339. // We'll use an async message to deliver the drop, because if the target decides
  340. // to run a modal loop, it can gum-up the operating system..
  341. class AsyncDropMessage : public CallbackMessage
  342. {
  343. public:
  344. AsyncDropMessage (Component* c, const ComponentPeer::DragInfo& d) : target (c), info (d) {}
  345. void messageCallback() override
  346. {
  347. if (Component* const c = target.get())
  348. {
  349. if (isFileDrag (info))
  350. dynamic_cast <FileDragAndDropTarget*> (c)->filesDropped (info.files, info.position.x, info.position.y);
  351. else
  352. dynamic_cast <TextDragAndDropTarget*> (c)->textDropped (info.text, info.position.x, info.position.y);
  353. }
  354. }
  355. private:
  356. WeakReference<Component> target;
  357. const ComponentPeer::DragInfo info;
  358. JUCE_DECLARE_NON_COPYABLE (AsyncDropMessage)
  359. };
  360. }
  361. bool ComponentPeer::handleDragMove (const ComponentPeer::DragInfo& info)
  362. {
  363. ModifierKeys::updateCurrentModifiers();
  364. Component* const compUnderMouse = component.getComponentAt (info.position);
  365. Component* const lastTarget = dragAndDropTargetComponent;
  366. Component* newTarget = nullptr;
  367. if (compUnderMouse != lastDragAndDropCompUnderMouse)
  368. {
  369. lastDragAndDropCompUnderMouse = compUnderMouse;
  370. newTarget = DragHelpers::findDragAndDropTarget (compUnderMouse, info, lastTarget);
  371. if (newTarget != lastTarget)
  372. {
  373. if (lastTarget != nullptr)
  374. {
  375. if (DragHelpers::isFileDrag (info))
  376. dynamic_cast <FileDragAndDropTarget*> (lastTarget)->fileDragExit (info.files);
  377. else
  378. dynamic_cast <TextDragAndDropTarget*> (lastTarget)->textDragExit (info.text);
  379. }
  380. dragAndDropTargetComponent = nullptr;
  381. if (DragHelpers::isSuitableTarget (info, newTarget))
  382. {
  383. dragAndDropTargetComponent = newTarget;
  384. const Point<int> pos (newTarget->getLocalPoint (&component, info.position));
  385. if (DragHelpers::isFileDrag (info))
  386. dynamic_cast <FileDragAndDropTarget*> (newTarget)->fileDragEnter (info.files, pos.x, pos.y);
  387. else
  388. dynamic_cast <TextDragAndDropTarget*> (newTarget)->textDragEnter (info.text, pos.x, pos.y);
  389. }
  390. }
  391. }
  392. else
  393. {
  394. newTarget = lastTarget;
  395. }
  396. if (! DragHelpers::isSuitableTarget (info, newTarget))
  397. return false;
  398. const Point<int> pos (newTarget->getLocalPoint (&component, info.position));
  399. if (DragHelpers::isFileDrag (info))
  400. dynamic_cast <FileDragAndDropTarget*> (newTarget)->fileDragMove (info.files, pos.x, pos.y);
  401. else
  402. dynamic_cast <TextDragAndDropTarget*> (newTarget)->textDragMove (info.text, pos.x, pos.y);
  403. return true;
  404. }
  405. bool ComponentPeer::handleDragExit (const ComponentPeer::DragInfo& info)
  406. {
  407. DragInfo info2 (info);
  408. info2.position.setXY (-1, -1);
  409. const bool used = handleDragMove (info2);
  410. jassert (dragAndDropTargetComponent == nullptr);
  411. lastDragAndDropCompUnderMouse = nullptr;
  412. return used;
  413. }
  414. bool ComponentPeer::handleDragDrop (const ComponentPeer::DragInfo& info)
  415. {
  416. handleDragMove (info);
  417. if (Component* const targetComp = dragAndDropTargetComponent)
  418. {
  419. dragAndDropTargetComponent = nullptr;
  420. lastDragAndDropCompUnderMouse = nullptr;
  421. if (DragHelpers::isSuitableTarget (info, targetComp))
  422. {
  423. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  424. {
  425. targetComp->internalModalInputAttempt();
  426. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  427. return true;
  428. }
  429. ComponentPeer::DragInfo info2 (info);
  430. info2.position = targetComp->getLocalPoint (&component, info.position);
  431. (new DragHelpers::AsyncDropMessage (targetComp, info2))->post();
  432. return true;
  433. }
  434. }
  435. return false;
  436. }
  437. //==============================================================================
  438. void ComponentPeer::handleUserClosingWindow()
  439. {
  440. ModifierKeys::updateCurrentModifiers();
  441. component.userTriedToCloseWindow();
  442. }
  443. bool ComponentPeer::setDocumentEditedStatus (bool)
  444. {
  445. return false;
  446. }
  447. void ComponentPeer::setRepresentedFile (const File&)
  448. {
  449. }
  450. //==============================================================================
  451. void ComponentPeer::clearMaskedRegion()
  452. {
  453. maskedRegion.clear();
  454. }
  455. void ComponentPeer::addMaskedRegion (const Rectangle<int>& area)
  456. {
  457. maskedRegion.add (area);
  458. }
  459. //==============================================================================
  460. StringArray ComponentPeer::getAvailableRenderingEngines() { return StringArray ("Software Renderer"); }
  461. int ComponentPeer::getCurrentRenderingEngine() const { return 0; }
  462. void ComponentPeer::setCurrentRenderingEngine (int index) { jassert (index == 0); (void) index; }