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.

580 lines
19KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. static uint32 lastUniquePeerID = 1;
  22. //==============================================================================
  23. ComponentPeer::ComponentPeer (Component& comp, int flags)
  24. : component (comp),
  25. styleFlags (flags),
  26. uniqueID (lastUniquePeerID += 2) // increment by 2 so that this can never hit 0
  27. {
  28. Desktop::getInstance().peers.add (this);
  29. }
  30. ComponentPeer::~ComponentPeer()
  31. {
  32. auto& desktop = Desktop::getInstance();
  33. desktop.peers.removeFirstMatchingValue (this);
  34. desktop.triggerFocusCallback();
  35. }
  36. //==============================================================================
  37. int ComponentPeer::getNumPeers() noexcept
  38. {
  39. return Desktop::getInstance().peers.size();
  40. }
  41. ComponentPeer* ComponentPeer::getPeer (const int index) noexcept
  42. {
  43. return Desktop::getInstance().peers [index];
  44. }
  45. ComponentPeer* ComponentPeer::getPeerFor (const Component* const component) noexcept
  46. {
  47. for (auto* peer : Desktop::getInstance().peers)
  48. if (&(peer->getComponent()) == component)
  49. return peer;
  50. return nullptr;
  51. }
  52. bool ComponentPeer::isValidPeer (const ComponentPeer* const peer) noexcept
  53. {
  54. return Desktop::getInstance().peers.contains (const_cast<ComponentPeer*> (peer));
  55. }
  56. void ComponentPeer::updateBounds()
  57. {
  58. setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component, component.getBoundsInParent()), false);
  59. }
  60. bool ComponentPeer::isKioskMode() const
  61. {
  62. return Desktop::getInstance().getKioskModeComponent() == &component;
  63. }
  64. //==============================================================================
  65. void ComponentPeer::handleMouseEvent (MouseInputSource::InputSourceType type, Point<float> pos, ModifierKeys newMods,
  66. float newPressure, float newOrientation, int64 time, PenDetails pen, int touchIndex)
  67. {
  68. if (auto* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (type, touchIndex))
  69. MouseInputSource (*mouse).handleEvent (*this, pos, time, newMods, newPressure, newOrientation, pen);
  70. }
  71. void ComponentPeer::handleMouseWheel (MouseInputSource::InputSourceType type, Point<float> pos, int64 time, const MouseWheelDetails& wheel, int touchIndex)
  72. {
  73. if (auto* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (type, touchIndex))
  74. MouseInputSource (*mouse).handleWheel (*this, pos, time, wheel);
  75. }
  76. void ComponentPeer::handleMagnifyGesture (MouseInputSource::InputSourceType type, Point<float> pos, int64 time, float scaleFactor, int touchIndex)
  77. {
  78. if (auto* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (type, touchIndex))
  79. MouseInputSource (*mouse).handleMagnifyGesture (*this, pos, time, scaleFactor);
  80. }
  81. //==============================================================================
  82. void ComponentPeer::handlePaint (LowLevelGraphicsContext& contextToPaintTo)
  83. {
  84. Graphics g (contextToPaintTo);
  85. if (component.isTransformed())
  86. g.addTransform (component.getTransform());
  87. auto peerBounds = getBounds();
  88. auto componentBounds = component.getLocalBounds();
  89. if (component.isTransformed())
  90. componentBounds = componentBounds.transformedBy (component.getTransform());
  91. if (peerBounds.getWidth() != componentBounds.getWidth() || peerBounds.getHeight() != componentBounds.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) componentBounds.getWidth(),
  94. peerBounds.getHeight() / (float) componentBounds.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. auto* c = Component::getCurrentlyFocusedComponent();
  132. if (c == nullptr)
  133. c = &component;
  134. if (c->isCurrentlyBlockedByAnotherModalComponent())
  135. if (auto* currentModalComp = Component::getCurrentlyModalComponent())
  136. c = currentModalComp;
  137. return c;
  138. }
  139. bool ComponentPeer::handleKeyPress (const int keyCode, const juce_wchar textCharacter)
  140. {
  141. return handleKeyPress (KeyPress (keyCode,
  142. ModifierKeys::currentModifiers.withoutMouseButtons(),
  143. textCharacter));
  144. }
  145. bool ComponentPeer::handleKeyPress (const KeyPress& keyInfo)
  146. {
  147. bool keyWasUsed = false;
  148. for (auto* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
  149. {
  150. const WeakReference<Component> deletionChecker (target);
  151. if (auto* keyListeners = target->keyListeners.get())
  152. {
  153. for (int i = keyListeners->size(); --i >= 0;)
  154. {
  155. keyWasUsed = keyListeners->getUnchecked(i)->keyPressed (keyInfo, target);
  156. if (keyWasUsed || deletionChecker == nullptr)
  157. return keyWasUsed;
  158. i = jmin (i, keyListeners->size());
  159. }
  160. }
  161. keyWasUsed = target->keyPressed (keyInfo);
  162. if (keyWasUsed || deletionChecker == nullptr)
  163. break;
  164. if (auto* currentlyFocused = Component::getCurrentlyFocusedComponent())
  165. {
  166. const bool isTab = (keyInfo == KeyPress::tabKey);
  167. const bool isShiftTab = (keyInfo == KeyPress (KeyPress::tabKey, ModifierKeys::shiftModifier, 0));
  168. if (isTab || isShiftTab)
  169. {
  170. currentlyFocused->moveKeyboardFocusToSibling (isTab);
  171. keyWasUsed = (currentlyFocused != Component::getCurrentlyFocusedComponent());
  172. if (keyWasUsed || deletionChecker == nullptr)
  173. break;
  174. }
  175. }
  176. }
  177. return keyWasUsed;
  178. }
  179. bool ComponentPeer::handleKeyUpOrDown (const bool isKeyDown)
  180. {
  181. bool keyWasUsed = false;
  182. for (auto* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
  183. {
  184. const WeakReference<Component> deletionChecker (target);
  185. keyWasUsed = target->keyStateChanged (isKeyDown);
  186. if (keyWasUsed || deletionChecker == nullptr)
  187. break;
  188. if (auto* keyListeners = target->keyListeners.get())
  189. {
  190. for (int i = keyListeners->size(); --i >= 0;)
  191. {
  192. keyWasUsed = keyListeners->getUnchecked(i)->keyStateChanged (isKeyDown, target);
  193. if (keyWasUsed || deletionChecker == nullptr)
  194. return keyWasUsed;
  195. i = jmin (i, keyListeners->size());
  196. }
  197. }
  198. }
  199. return keyWasUsed;
  200. }
  201. void ComponentPeer::handleModifierKeysChange()
  202. {
  203. auto* target = Desktop::getInstance().getMainMouseSource().getComponentUnderMouse();
  204. if (target == nullptr)
  205. target = Component::getCurrentlyFocusedComponent();
  206. if (target == nullptr)
  207. target = &component;
  208. target->internalModifierKeysChanged();
  209. }
  210. TextInputTarget* ComponentPeer::findCurrentTextInputTarget()
  211. {
  212. auto* c = Component::getCurrentlyFocusedComponent();
  213. if (c == &component || component.isParentOf (c))
  214. if (auto* 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. component.internalBroughtToFront();
  224. }
  225. void ComponentPeer::setConstrainer (ComponentBoundsConstrainer* const newConstrainer) noexcept
  226. {
  227. constrainer = newConstrainer;
  228. }
  229. void ComponentPeer::handleMovedOrResized()
  230. {
  231. const bool nowMinimised = isMinimised();
  232. if (component.flags.hasHeavyweightPeerFlag && ! nowMinimised)
  233. {
  234. const WeakReference<Component> deletionChecker (&component);
  235. auto newBounds = Component::ComponentHelpers::rawPeerPositionToLocal (component, getBounds());
  236. auto oldBounds = component.getBounds();
  237. const bool wasMoved = (oldBounds.getPosition() != newBounds.getPosition());
  238. const bool wasResized = (oldBounds.getWidth() != newBounds.getWidth() || oldBounds.getHeight() != newBounds.getHeight());
  239. if (wasMoved || wasResized)
  240. {
  241. component.boundsRelativeToParent = newBounds;
  242. if (wasResized)
  243. component.repaint();
  244. component.sendMovedResizedMessages (wasMoved, wasResized);
  245. if (deletionChecker == nullptr)
  246. return;
  247. }
  248. }
  249. if (isWindowMinimised != nowMinimised)
  250. {
  251. isWindowMinimised = nowMinimised;
  252. component.minimisationStateChanged (nowMinimised);
  253. component.sendVisibilityChangeMessage();
  254. }
  255. if (! isFullScreen())
  256. lastNonFullscreenBounds = component.getBounds();
  257. }
  258. void ComponentPeer::handleFocusGain()
  259. {
  260. if (component.isParentOf (lastFocusedComponent)
  261. && lastFocusedComponent->isShowing()
  262. && lastFocusedComponent->getWantsKeyboardFocus())
  263. {
  264. Component::currentlyFocusedComponent = lastFocusedComponent;
  265. Desktop::getInstance().triggerFocusCallback();
  266. lastFocusedComponent->internalFocusGain (Component::focusChangedDirectly);
  267. }
  268. else
  269. {
  270. if (! component.isCurrentlyBlockedByAnotherModalComponent())
  271. component.grabKeyboardFocus();
  272. else
  273. ModalComponentManager::getInstance()->bringModalComponentsToFront();
  274. }
  275. }
  276. void ComponentPeer::handleFocusLoss()
  277. {
  278. if (component.hasKeyboardFocus (true))
  279. {
  280. lastFocusedComponent = Component::currentlyFocusedComponent;
  281. if (lastFocusedComponent != nullptr)
  282. {
  283. Component::currentlyFocusedComponent = nullptr;
  284. Desktop::getInstance().triggerFocusCallback();
  285. lastFocusedComponent->internalFocusLoss (Component::focusChangedByMouseClick);
  286. }
  287. }
  288. }
  289. Component* ComponentPeer::getLastFocusedSubcomponent() const noexcept
  290. {
  291. return (component.isParentOf (lastFocusedComponent) && lastFocusedComponent->isShowing())
  292. ? static_cast<Component*> (lastFocusedComponent)
  293. : &component;
  294. }
  295. void ComponentPeer::handleScreenSizeChange()
  296. {
  297. component.parentSizeChanged();
  298. handleMovedOrResized();
  299. }
  300. void ComponentPeer::setNonFullScreenBounds (const Rectangle<int>& newBounds) noexcept
  301. {
  302. lastNonFullscreenBounds = newBounds;
  303. }
  304. const Rectangle<int>& ComponentPeer::getNonFullScreenBounds() const noexcept
  305. {
  306. return lastNonFullscreenBounds;
  307. }
  308. Point<int> ComponentPeer::localToGlobal (Point<int> p) { return localToGlobal (p.toFloat()).roundToInt(); }
  309. Point<int> ComponentPeer::globalToLocal (Point<int> p) { return globalToLocal (p.toFloat()).roundToInt(); }
  310. Rectangle<int> ComponentPeer::localToGlobal (const Rectangle<int>& relativePosition)
  311. {
  312. return relativePosition.withPosition (localToGlobal (relativePosition.getPosition()));
  313. }
  314. Rectangle<int> ComponentPeer::globalToLocal (const Rectangle<int>& screenPosition)
  315. {
  316. return screenPosition.withPosition (globalToLocal (screenPosition.getPosition()));
  317. }
  318. Rectangle<int> ComponentPeer::getAreaCoveredBy (Component& subComponent) const
  319. {
  320. return ScalingHelpers::scaledScreenPosToUnscaled
  321. (component, component.getLocalArea (&subComponent, subComponent.getLocalBounds()));
  322. }
  323. //==============================================================================
  324. namespace DragHelpers
  325. {
  326. static bool isFileDrag (const ComponentPeer::DragInfo& info)
  327. {
  328. return ! info.files.isEmpty();
  329. }
  330. static bool isSuitableTarget (const ComponentPeer::DragInfo& info, Component* target)
  331. {
  332. return isFileDrag (info) ? dynamic_cast<FileDragAndDropTarget*> (target) != nullptr
  333. : dynamic_cast<TextDragAndDropTarget*> (target) != nullptr;
  334. }
  335. static bool isInterested (const ComponentPeer::DragInfo& info, Component* target)
  336. {
  337. return isFileDrag (info) ? dynamic_cast<FileDragAndDropTarget*> (target)->isInterestedInFileDrag (info.files)
  338. : dynamic_cast<TextDragAndDropTarget*> (target)->isInterestedInTextDrag (info.text);
  339. }
  340. static Component* findDragAndDropTarget (Component* c, const ComponentPeer::DragInfo& info, Component* lastOne)
  341. {
  342. for (; c != nullptr; c = c->getParentComponent())
  343. if (isSuitableTarget (info, c) && (c == lastOne || isInterested (info, c)))
  344. return c;
  345. return nullptr;
  346. }
  347. }
  348. bool ComponentPeer::handleDragMove (const ComponentPeer::DragInfo& info)
  349. {
  350. auto* compUnderMouse = component.getComponentAt (info.position);
  351. auto* lastTarget = dragAndDropTargetComponent.get();
  352. Component* newTarget = nullptr;
  353. if (compUnderMouse != lastDragAndDropCompUnderMouse)
  354. {
  355. lastDragAndDropCompUnderMouse = compUnderMouse;
  356. newTarget = DragHelpers::findDragAndDropTarget (compUnderMouse, info, lastTarget);
  357. if (newTarget != lastTarget)
  358. {
  359. if (lastTarget != nullptr)
  360. {
  361. if (DragHelpers::isFileDrag (info))
  362. dynamic_cast<FileDragAndDropTarget*> (lastTarget)->fileDragExit (info.files);
  363. else
  364. dynamic_cast<TextDragAndDropTarget*> (lastTarget)->textDragExit (info.text);
  365. }
  366. dragAndDropTargetComponent = nullptr;
  367. if (DragHelpers::isSuitableTarget (info, newTarget))
  368. {
  369. dragAndDropTargetComponent = newTarget;
  370. auto pos = newTarget->getLocalPoint (&component, info.position);
  371. if (DragHelpers::isFileDrag (info))
  372. dynamic_cast<FileDragAndDropTarget*> (newTarget)->fileDragEnter (info.files, pos.x, pos.y);
  373. else
  374. dynamic_cast<TextDragAndDropTarget*> (newTarget)->textDragEnter (info.text, pos.x, pos.y);
  375. }
  376. }
  377. }
  378. else
  379. {
  380. newTarget = lastTarget;
  381. }
  382. if (! DragHelpers::isSuitableTarget (info, newTarget))
  383. return false;
  384. auto pos = newTarget->getLocalPoint (&component, info.position);
  385. if (DragHelpers::isFileDrag (info))
  386. dynamic_cast<FileDragAndDropTarget*> (newTarget)->fileDragMove (info.files, pos.x, pos.y);
  387. else
  388. dynamic_cast<TextDragAndDropTarget*> (newTarget)->textDragMove (info.text, pos.x, pos.y);
  389. return true;
  390. }
  391. bool ComponentPeer::handleDragExit (const ComponentPeer::DragInfo& info)
  392. {
  393. DragInfo info2 (info);
  394. info2.position.setXY (-1, -1);
  395. const bool used = handleDragMove (info2);
  396. jassert (dragAndDropTargetComponent == nullptr);
  397. lastDragAndDropCompUnderMouse = nullptr;
  398. return used;
  399. }
  400. bool ComponentPeer::handleDragDrop (const ComponentPeer::DragInfo& info)
  401. {
  402. handleDragMove (info);
  403. if (WeakReference<Component> targetComp = dragAndDropTargetComponent)
  404. {
  405. dragAndDropTargetComponent = nullptr;
  406. lastDragAndDropCompUnderMouse = nullptr;
  407. if (DragHelpers::isSuitableTarget (info, targetComp))
  408. {
  409. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  410. {
  411. targetComp->internalModalInputAttempt();
  412. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  413. return true;
  414. }
  415. ComponentPeer::DragInfo infoCopy (info);
  416. infoCopy.position = targetComp->getLocalPoint (&component, info.position);
  417. // We'll use an async message to deliver the drop, because if the target decides
  418. // to run a modal loop, it can gum-up the operating system..
  419. MessageManager::callAsync ([=]
  420. {
  421. if (auto* c = targetComp.get())
  422. {
  423. if (DragHelpers::isFileDrag (info))
  424. dynamic_cast<FileDragAndDropTarget*> (c)->filesDropped (infoCopy.files, infoCopy.position.x, infoCopy.position.y);
  425. else
  426. dynamic_cast<TextDragAndDropTarget*> (c)->textDropped (infoCopy.text, infoCopy.position.x, infoCopy.position.y);
  427. }
  428. });
  429. return true;
  430. }
  431. }
  432. return false;
  433. }
  434. //==============================================================================
  435. void ComponentPeer::handleUserClosingWindow()
  436. {
  437. component.userTriedToCloseWindow();
  438. }
  439. bool ComponentPeer::setDocumentEditedStatus (bool)
  440. {
  441. return false;
  442. }
  443. void ComponentPeer::setRepresentedFile (const File&)
  444. {
  445. }
  446. //==============================================================================
  447. int ComponentPeer::getCurrentRenderingEngine() const { return 0; }
  448. void ComponentPeer::setCurrentRenderingEngine (int index) { jassert (index == 0); ignoreUnused (index); }
  449. //==============================================================================
  450. std::function<ModifierKeys()> ComponentPeer::getNativeRealtimeModifiers = nullptr;
  451. ModifierKeys ComponentPeer::getCurrentModifiersRealtime() noexcept
  452. {
  453. if (getNativeRealtimeModifiers != nullptr)
  454. return getNativeRealtimeModifiers();
  455. return ModifierKeys::currentModifiers;
  456. }
  457. } // namespace juce