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.

585 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 lastUniqueID = 1;
  18. //==============================================================================
  19. ComponentPeer::ComponentPeer (Component& comp, const int flags)
  20. : component (comp),
  21. styleFlags (flags),
  22. constrainer (nullptr),
  23. lastDragAndDropCompUnderMouse (nullptr),
  24. uniqueID (lastUniqueID += 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. //==============================================================================
  310. namespace DragHelpers
  311. {
  312. static bool isFileDrag (const ComponentPeer::DragInfo& info)
  313. {
  314. return info.files.size() > 0;
  315. }
  316. static bool isSuitableTarget (const ComponentPeer::DragInfo& info, Component* target)
  317. {
  318. return isFileDrag (info) ? dynamic_cast <FileDragAndDropTarget*> (target) != nullptr
  319. : dynamic_cast <TextDragAndDropTarget*> (target) != nullptr;
  320. }
  321. static bool isInterested (const ComponentPeer::DragInfo& info, Component* target)
  322. {
  323. return isFileDrag (info) ? dynamic_cast <FileDragAndDropTarget*> (target)->isInterestedInFileDrag (info.files)
  324. : dynamic_cast <TextDragAndDropTarget*> (target)->isInterestedInTextDrag (info.text);
  325. }
  326. static Component* findDragAndDropTarget (Component* c, const ComponentPeer::DragInfo& info, Component* const lastOne)
  327. {
  328. for (; c != nullptr; c = c->getParentComponent())
  329. if (isSuitableTarget (info, c) && (c == lastOne || isInterested (info, c)))
  330. return c;
  331. return nullptr;
  332. }
  333. // We'll use an async message to deliver the drop, because if the target decides
  334. // to run a modal loop, it can gum-up the operating system..
  335. class AsyncDropMessage : public CallbackMessage
  336. {
  337. public:
  338. AsyncDropMessage (Component* c, const ComponentPeer::DragInfo& d) : target (c), info (d) {}
  339. void messageCallback() override
  340. {
  341. if (Component* const c = target.get())
  342. {
  343. if (isFileDrag (info))
  344. dynamic_cast <FileDragAndDropTarget*> (c)->filesDropped (info.files, info.position.x, info.position.y);
  345. else
  346. dynamic_cast <TextDragAndDropTarget*> (c)->textDropped (info.text, info.position.x, info.position.y);
  347. }
  348. }
  349. private:
  350. WeakReference<Component> target;
  351. const ComponentPeer::DragInfo info;
  352. JUCE_DECLARE_NON_COPYABLE (AsyncDropMessage)
  353. };
  354. }
  355. bool ComponentPeer::handleDragMove (const ComponentPeer::DragInfo& info)
  356. {
  357. ModifierKeys::updateCurrentModifiers();
  358. Component* const compUnderMouse = component.getComponentAt (info.position);
  359. Component* const lastTarget = dragAndDropTargetComponent;
  360. Component* newTarget = nullptr;
  361. if (compUnderMouse != lastDragAndDropCompUnderMouse)
  362. {
  363. lastDragAndDropCompUnderMouse = compUnderMouse;
  364. newTarget = DragHelpers::findDragAndDropTarget (compUnderMouse, info, lastTarget);
  365. if (newTarget != lastTarget)
  366. {
  367. if (lastTarget != nullptr)
  368. {
  369. if (DragHelpers::isFileDrag (info))
  370. dynamic_cast <FileDragAndDropTarget*> (lastTarget)->fileDragExit (info.files);
  371. else
  372. dynamic_cast <TextDragAndDropTarget*> (lastTarget)->textDragExit (info.text);
  373. }
  374. dragAndDropTargetComponent = nullptr;
  375. if (DragHelpers::isSuitableTarget (info, newTarget))
  376. {
  377. dragAndDropTargetComponent = newTarget;
  378. const Point<int> pos (newTarget->getLocalPoint (&component, info.position));
  379. if (DragHelpers::isFileDrag (info))
  380. dynamic_cast <FileDragAndDropTarget*> (newTarget)->fileDragEnter (info.files, pos.x, pos.y);
  381. else
  382. dynamic_cast <TextDragAndDropTarget*> (newTarget)->textDragEnter (info.text, pos.x, pos.y);
  383. }
  384. }
  385. }
  386. else
  387. {
  388. newTarget = lastTarget;
  389. }
  390. if (! DragHelpers::isSuitableTarget (info, newTarget))
  391. return false;
  392. const Point<int> pos (newTarget->getLocalPoint (&component, info.position));
  393. if (DragHelpers::isFileDrag (info))
  394. dynamic_cast <FileDragAndDropTarget*> (newTarget)->fileDragMove (info.files, pos.x, pos.y);
  395. else
  396. dynamic_cast <TextDragAndDropTarget*> (newTarget)->textDragMove (info.text, pos.x, pos.y);
  397. return true;
  398. }
  399. bool ComponentPeer::handleDragExit (const ComponentPeer::DragInfo& info)
  400. {
  401. DragInfo info2 (info);
  402. info2.position.setXY (-1, -1);
  403. const bool used = handleDragMove (info2);
  404. jassert (dragAndDropTargetComponent == nullptr);
  405. lastDragAndDropCompUnderMouse = nullptr;
  406. return used;
  407. }
  408. bool ComponentPeer::handleDragDrop (const ComponentPeer::DragInfo& info)
  409. {
  410. handleDragMove (info);
  411. if (Component* const targetComp = dragAndDropTargetComponent)
  412. {
  413. dragAndDropTargetComponent = nullptr;
  414. lastDragAndDropCompUnderMouse = nullptr;
  415. if (DragHelpers::isSuitableTarget (info, targetComp))
  416. {
  417. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  418. {
  419. targetComp->internalModalInputAttempt();
  420. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  421. return true;
  422. }
  423. ComponentPeer::DragInfo info2 (info);
  424. info2.position = targetComp->getLocalPoint (&component, info.position);
  425. (new DragHelpers::AsyncDropMessage (targetComp, info2))->post();
  426. return true;
  427. }
  428. }
  429. return false;
  430. }
  431. //==============================================================================
  432. void ComponentPeer::handleUserClosingWindow()
  433. {
  434. ModifierKeys::updateCurrentModifiers();
  435. component.userTriedToCloseWindow();
  436. }
  437. bool ComponentPeer::setDocumentEditedStatus (bool)
  438. {
  439. return false;
  440. }
  441. void ComponentPeer::setRepresentedFile (const File&)
  442. {
  443. }
  444. //==============================================================================
  445. void ComponentPeer::clearMaskedRegion()
  446. {
  447. maskedRegion.clear();
  448. }
  449. void ComponentPeer::addMaskedRegion (const Rectangle<int>& area)
  450. {
  451. maskedRegion.add (area);
  452. }
  453. //==============================================================================
  454. StringArray ComponentPeer::getAvailableRenderingEngines() { return StringArray ("Software Renderer"); }
  455. int ComponentPeer::getCurrentRenderingEngine() const { return 0; }
  456. void ComponentPeer::setCurrentRenderingEngine (int index) { jassert (index == 0); (void) index; }