VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_gui_basics / components / juce_Component.cpp
blobcc7577c5dbbeab9d55e06c5f58c2ba73f61db616
1 /*
2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
23 ==============================================================================
26 namespace juce
29 static Component* findFirstEnabledAncestor (Component* in)
31 if (in == nullptr)
32 return nullptr;
34 if (in->isEnabled())
35 return in;
37 return findFirstEnabledAncestor (in->getParentComponent());
40 Component* Component::currentlyFocusedComponent = nullptr;
43 //==============================================================================
44 class Component::MouseListenerList
46 public:
47 MouseListenerList() noexcept {}
49 void addListener (MouseListener* newListener, bool wantsEventsForAllNestedChildComponents)
51 if (! listeners.contains (newListener))
53 if (wantsEventsForAllNestedChildComponents)
55 listeners.insert (0, newListener);
56 ++numDeepMouseListeners;
58 else
60 listeners.add (newListener);
65 void removeListener (MouseListener* listenerToRemove)
67 auto index = listeners.indexOf (listenerToRemove);
69 if (index >= 0)
71 if (index < numDeepMouseListeners)
72 --numDeepMouseListeners;
74 listeners.remove (index);
78 // g++ 4.8 cannot deduce the parameter pack inside the function pointer when it has more than one element
79 #if defined(__GNUC__) && __GNUC__ < 5 && ! defined(__clang__)
80 template <typename... Params>
81 static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker,
82 void (MouseListener::*eventMethod) (const MouseEvent&),
83 Params... params)
85 sendMouseEvent <decltype (eventMethod), Params...> (comp, checker, eventMethod, params...);
88 template <typename... Params>
89 static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker,
90 void (MouseListener::*eventMethod) (const MouseEvent&, const MouseWheelDetails&),
91 Params... params)
93 sendMouseEvent <decltype (eventMethod), Params...> (comp, checker, eventMethod, params...);
96 template <typename... Params>
97 static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker,
98 void (MouseListener::*eventMethod) (const MouseEvent&, float),
99 Params... params)
101 sendMouseEvent <decltype (eventMethod), Params...> (comp, checker, eventMethod, params...);
104 template <typename EventMethod, typename... Params>
105 static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker,
106 EventMethod eventMethod,
107 Params... params)
108 #else
109 template <typename... Params>
110 static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker,
111 void (MouseListener::*eventMethod) (Params...),
112 Params... params)
113 #endif
115 if (checker.shouldBailOut())
116 return;
118 if (auto* list = comp.mouseListeners.get())
120 for (int i = list->listeners.size(); --i >= 0;)
122 (list->listeners.getUnchecked(i)->*eventMethod) (params...);
124 if (checker.shouldBailOut())
125 return;
127 i = jmin (i, list->listeners.size());
131 for (Component* p = comp.parentComponent; p != nullptr; p = p->parentComponent)
133 if (auto* list = p->mouseListeners.get())
135 if (list->numDeepMouseListeners > 0)
137 BailOutChecker2 checker2 (checker, p);
139 for (int i = list->numDeepMouseListeners; --i >= 0;)
141 (list->listeners.getUnchecked(i)->*eventMethod) (params...);
143 if (checker2.shouldBailOut())
144 return;
146 i = jmin (i, list->numDeepMouseListeners);
153 private:
154 Array<MouseListener*> listeners;
155 int numDeepMouseListeners = 0;
157 struct BailOutChecker2
159 BailOutChecker2 (Component::BailOutChecker& boc, Component* comp)
160 : checker (boc), safePointer (comp)
164 bool shouldBailOut() const noexcept
166 return checker.shouldBailOut() || safePointer == nullptr;
169 private:
170 Component::BailOutChecker& checker;
171 const WeakReference<Component> safePointer;
173 JUCE_DECLARE_NON_COPYABLE (BailOutChecker2)
176 JUCE_DECLARE_NON_COPYABLE (MouseListenerList)
179 //==============================================================================
180 struct FocusRestorer
182 FocusRestorer() : lastFocus (Component::getCurrentlyFocusedComponent()) {}
184 ~FocusRestorer()
186 if (lastFocus != nullptr
187 && lastFocus->isShowing()
188 && ! lastFocus->isCurrentlyBlockedByAnotherModalComponent())
189 lastFocus->grabKeyboardFocus();
192 WeakReference<Component> lastFocus;
194 JUCE_DECLARE_NON_COPYABLE (FocusRestorer)
197 //==============================================================================
198 struct ScalingHelpers
200 template <typename PointOrRect>
201 static PointOrRect unscaledScreenPosToScaled (float scale, PointOrRect pos) noexcept
203 return scale != 1.0f ? pos / scale : pos;
206 template <typename PointOrRect>
207 static PointOrRect scaledScreenPosToUnscaled (float scale, PointOrRect pos) noexcept
209 return scale != 1.0f ? pos * scale : pos;
212 // For these, we need to avoid getSmallestIntegerContainer being used, which causes
213 // judder when moving windows
214 static Rectangle<int> unscaledScreenPosToScaled (float scale, Rectangle<int> pos) noexcept
216 return scale != 1.0f ? Rectangle<int> (roundToInt ((float) pos.getX() / scale),
217 roundToInt ((float) pos.getY() / scale),
218 roundToInt ((float) pos.getWidth() / scale),
219 roundToInt ((float) pos.getHeight() / scale)) : pos;
222 static Rectangle<int> scaledScreenPosToUnscaled (float scale, Rectangle<int> pos) noexcept
224 return scale != 1.0f ? Rectangle<int> (roundToInt ((float) pos.getX() * scale),
225 roundToInt ((float) pos.getY() * scale),
226 roundToInt ((float) pos.getWidth() * scale),
227 roundToInt ((float) pos.getHeight() * scale)) : pos;
230 static Rectangle<float> unscaledScreenPosToScaled (float scale, Rectangle<float> pos) noexcept
232 return scale != 1.0f ? Rectangle<float> (pos.getX() / scale,
233 pos.getY() / scale,
234 pos.getWidth() / scale,
235 pos.getHeight() / scale) : pos;
238 static Rectangle<float> scaledScreenPosToUnscaled (float scale, Rectangle<float> pos) noexcept
240 return scale != 1.0f ? Rectangle<float> (pos.getX() * scale,
241 pos.getY() * scale,
242 pos.getWidth() * scale,
243 pos.getHeight() * scale) : pos;
246 template <typename PointOrRect>
247 static PointOrRect unscaledScreenPosToScaled (PointOrRect pos) noexcept
249 return unscaledScreenPosToScaled (Desktop::getInstance().getGlobalScaleFactor(), pos);
252 template <typename PointOrRect>
253 static PointOrRect scaledScreenPosToUnscaled (PointOrRect pos) noexcept
255 return scaledScreenPosToUnscaled (Desktop::getInstance().getGlobalScaleFactor(), pos);
258 template <typename PointOrRect>
259 static PointOrRect unscaledScreenPosToScaled (const Component& comp, PointOrRect pos) noexcept
261 return unscaledScreenPosToScaled (comp.getDesktopScaleFactor(), pos);
264 template <typename PointOrRect>
265 static PointOrRect scaledScreenPosToUnscaled (const Component& comp, PointOrRect pos) noexcept
267 return scaledScreenPosToUnscaled (comp.getDesktopScaleFactor(), pos);
270 static Point<int> addPosition (Point<int> p, const Component& c) noexcept { return p + c.getPosition(); }
271 static Rectangle<int> addPosition (Rectangle<int> p, const Component& c) noexcept { return p + c.getPosition(); }
272 static Point<float> addPosition (Point<float> p, const Component& c) noexcept { return p + c.getPosition().toFloat(); }
273 static Rectangle<float> addPosition (Rectangle<float> p, const Component& c) noexcept { return p + c.getPosition().toFloat(); }
274 static Point<int> subtractPosition (Point<int> p, const Component& c) noexcept { return p - c.getPosition(); }
275 static Rectangle<int> subtractPosition (Rectangle<int> p, const Component& c) noexcept { return p - c.getPosition(); }
276 static Point<float> subtractPosition (Point<float> p, const Component& c) noexcept { return p - c.getPosition().toFloat(); }
277 static Rectangle<float> subtractPosition (Rectangle<float> p, const Component& c) noexcept { return p - c.getPosition().toFloat(); }
279 static Point<float> screenPosToLocalPos (Component& comp, Point<float> pos)
281 if (auto* peer = comp.getPeer())
283 pos = peer->globalToLocal (pos);
284 auto& peerComp = peer->getComponent();
285 return comp.getLocalPoint (&peerComp, unscaledScreenPosToScaled (peerComp, pos));
288 return comp.getLocalPoint (nullptr, unscaledScreenPosToScaled (comp, pos));
292 static const char colourPropertyPrefix[] = "jcclr_";
294 //==============================================================================
295 struct Component::ComponentHelpers
297 #if JUCE_MODAL_LOOPS_PERMITTED
298 static void* runModalLoopCallback (void* userData)
300 return (void*) (pointer_sized_int) static_cast<Component*> (userData)->runModalLoop();
302 #endif
304 static Identifier getColourPropertyID (int colourID)
306 char buffer[32];
307 auto* end = buffer + numElementsInArray (buffer) - 1;
308 auto* t = end;
309 *t = 0;
311 for (auto v = (uint32) colourID;;)
313 *--t = "0123456789abcdef" [v & 15];
314 v >>= 4;
316 if (v == 0)
317 break;
320 for (int i = (int) sizeof (colourPropertyPrefix) - 1; --i >= 0;)
321 *--t = colourPropertyPrefix[i];
323 return t;
326 //==============================================================================
327 static bool hitTest (Component& comp, Point<float> localPoint)
329 const auto intPoint = localPoint.roundToInt();
330 return Rectangle<int> { comp.getWidth(), comp.getHeight() }.contains (intPoint)
331 && comp.hitTest (intPoint.x, intPoint.y);
334 // converts an unscaled position within a peer to the local position within that peer's component
335 template <typename PointOrRect>
336 static PointOrRect rawPeerPositionToLocal (const Component& comp, PointOrRect pos) noexcept
338 if (comp.isTransformed())
339 pos = pos.transformedBy (comp.getTransform().inverted());
341 return ScalingHelpers::unscaledScreenPosToScaled (comp, pos);
344 // converts a position within a peer's component to the unscaled position within the peer
345 template <typename PointOrRect>
346 static PointOrRect localPositionToRawPeerPos (const Component& comp, PointOrRect pos) noexcept
348 if (comp.isTransformed())
349 pos = pos.transformedBy (comp.getTransform());
351 return ScalingHelpers::scaledScreenPosToUnscaled (comp, pos);
354 template <typename PointOrRect>
355 static PointOrRect convertFromParentSpace (const Component& comp, const PointOrRect pointInParentSpace)
357 const auto transformed = comp.affineTransform != nullptr ? pointInParentSpace.transformedBy (comp.affineTransform->inverted())
358 : pointInParentSpace;
360 if (comp.isOnDesktop())
362 if (auto* peer = comp.getPeer())
363 return ScalingHelpers::unscaledScreenPosToScaled (comp, peer->globalToLocal (ScalingHelpers::scaledScreenPosToUnscaled (transformed)));
365 jassertfalse;
366 return transformed;
369 if (comp.getParentComponent() == nullptr)
370 return ScalingHelpers::subtractPosition (ScalingHelpers::unscaledScreenPosToScaled (comp, ScalingHelpers::scaledScreenPosToUnscaled (transformed)), comp);
372 return ScalingHelpers::subtractPosition (transformed, comp);
375 template <typename PointOrRect>
376 static PointOrRect convertToParentSpace (const Component& comp, const PointOrRect pointInLocalSpace)
378 const auto preTransform = [&]
380 if (comp.isOnDesktop())
382 if (auto* peer = comp.getPeer())
383 return ScalingHelpers::unscaledScreenPosToScaled (peer->localToGlobal (ScalingHelpers::scaledScreenPosToUnscaled (comp, pointInLocalSpace)));
385 jassertfalse;
386 return pointInLocalSpace;
389 if (comp.getParentComponent() == nullptr)
390 return ScalingHelpers::unscaledScreenPosToScaled (ScalingHelpers::scaledScreenPosToUnscaled (comp, ScalingHelpers::addPosition (pointInLocalSpace, comp)));
392 return ScalingHelpers::addPosition (pointInLocalSpace, comp);
393 }();
395 return comp.affineTransform != nullptr ? preTransform.transformedBy (*comp.affineTransform)
396 : preTransform;
399 template <typename PointOrRect>
400 static PointOrRect convertFromDistantParentSpace (const Component* parent, const Component& target, PointOrRect coordInParent)
402 auto* directParent = target.getParentComponent();
403 jassert (directParent != nullptr);
405 if (directParent == parent)
406 return convertFromParentSpace (target, coordInParent);
408 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
409 return convertFromParentSpace (target, convertFromDistantParentSpace (parent, *directParent, coordInParent));
410 JUCE_END_IGNORE_WARNINGS_MSVC
413 template <typename PointOrRect>
414 static PointOrRect convertCoordinate (const Component* target, const Component* source, PointOrRect p)
416 while (source != nullptr)
418 if (source == target)
419 return p;
421 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
423 if (source->isParentOf (target))
424 return convertFromDistantParentSpace (source, *target, p);
426 JUCE_END_IGNORE_WARNINGS_MSVC
428 p = convertToParentSpace (*source, p);
429 source = source->getParentComponent();
432 jassert (source == nullptr);
433 if (target == nullptr)
434 return p;
436 auto* topLevelComp = target->getTopLevelComponent();
438 p = convertFromParentSpace (*topLevelComp, p);
440 if (topLevelComp == target)
441 return p;
443 return convertFromDistantParentSpace (topLevelComp, *target, p);
446 static bool clipObscuredRegions (const Component& comp, Graphics& g,
447 const Rectangle<int> clipRect, Point<int> delta)
449 bool wasClipped = false;
451 for (int i = comp.childComponentList.size(); --i >= 0;)
453 auto& child = *comp.childComponentList.getUnchecked(i);
455 if (child.isVisible() && ! child.isTransformed())
457 auto newClip = clipRect.getIntersection (child.boundsRelativeToParent);
459 if (! newClip.isEmpty())
461 if (child.isOpaque() && child.componentTransparency == 0)
463 g.excludeClipRegion (newClip + delta);
464 wasClipped = true;
466 else
468 auto childPos = child.getPosition();
470 if (clipObscuredRegions (child, g, newClip - childPos, childPos + delta))
471 wasClipped = true;
477 return wasClipped;
480 static Rectangle<int> getParentOrMainMonitorBounds (const Component& comp)
482 if (auto* p = comp.getParentComponent())
483 return p->getLocalBounds();
485 return Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea;
488 static void releaseAllCachedImageResources (Component& c)
490 if (auto* cached = c.getCachedComponentImage())
491 cached->releaseResources();
493 for (auto* child : c.childComponentList)
494 releaseAllCachedImageResources (*child);
497 //==============================================================================
498 static bool modalWouldBlockComponent (const Component& maybeBlocked, Component* modal)
500 return modal != nullptr
501 && modal != &maybeBlocked
502 && ! modal->isParentOf (&maybeBlocked)
503 && ! modal->canModalEventBeSentToComponent (&maybeBlocked);
506 template <typename Function>
507 static void sendMouseEventToComponentsThatAreBlockedByModal (Component& modal, Function&& function)
509 for (auto& ms : Desktop::getInstance().getMouseSources())
510 if (auto* c = ms.getComponentUnderMouse())
511 if (modalWouldBlockComponent (*c, &modal))
512 (c->*function) (ms, ScalingHelpers::screenPosToLocalPos (*c, ms.getScreenPosition()), Time::getCurrentTime());
516 //==============================================================================
517 Component::Component() noexcept
518 : componentFlags (0)
522 Component::Component (const String& name) noexcept
523 : componentName (name), componentFlags (0)
527 Component::~Component()
529 static_assert (sizeof (flags) <= sizeof (componentFlags), "componentFlags has too many bits!");
531 componentListeners.call ([this] (ComponentListener& l) { l.componentBeingDeleted (*this); });
533 while (childComponentList.size() > 0)
534 removeChildComponent (childComponentList.size() - 1, false, true);
536 masterReference.clear();
538 if (parentComponent != nullptr)
539 parentComponent->removeChildComponent (parentComponent->childComponentList.indexOf (this), true, false);
540 else
541 giveAwayKeyboardFocusInternal (isParentOf (currentlyFocusedComponent));
543 if (flags.hasHeavyweightPeerFlag)
544 removeFromDesktop();
546 // Something has added some children to this component during its destructor! Not a smart idea!
547 jassert (childComponentList.size() == 0);
550 //==============================================================================
551 void Component::setName (const String& name)
553 // if component methods are being called from threads other than the message
554 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
555 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
557 if (componentName != name)
559 componentName = name;
561 if (flags.hasHeavyweightPeerFlag)
562 if (auto* peer = getPeer())
563 peer->setTitle (name);
565 BailOutChecker checker (this);
566 componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentNameChanged (*this); });
570 void Component::setComponentID (const String& newID)
572 componentID = newID;
575 void Component::setVisible (bool shouldBeVisible)
577 if (flags.visibleFlag != shouldBeVisible)
579 // if component methods are being called from threads other than the message
580 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
581 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
583 const WeakReference<Component> safePointer (this);
584 flags.visibleFlag = shouldBeVisible;
586 if (shouldBeVisible)
587 repaint();
588 else
589 repaintParent();
591 sendFakeMouseMove();
593 if (! shouldBeVisible)
595 ComponentHelpers::releaseAllCachedImageResources (*this);
597 if (hasKeyboardFocus (true))
599 if (parentComponent != nullptr)
600 parentComponent->grabKeyboardFocus();
602 // ensure that keyboard focus is given away if it wasn't taken by parent
603 giveAwayKeyboardFocus();
607 if (safePointer != nullptr)
609 sendVisibilityChangeMessage();
611 if (safePointer != nullptr && flags.hasHeavyweightPeerFlag)
613 if (auto* peer = getPeer())
615 peer->setVisible (shouldBeVisible);
616 internalHierarchyChanged();
623 void Component::visibilityChanged() {}
625 void Component::sendVisibilityChangeMessage()
627 BailOutChecker checker (this);
628 visibilityChanged();
630 if (! checker.shouldBailOut())
631 componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentVisibilityChanged (*this); });
634 bool Component::isShowing() const
636 if (! flags.visibleFlag)
637 return false;
639 if (parentComponent != nullptr)
640 return parentComponent->isShowing();
642 if (auto* peer = getPeer())
643 return ! peer->isMinimised();
645 return false;
648 //==============================================================================
649 void* Component::getWindowHandle() const
651 if (auto* peer = getPeer())
652 return peer->getNativeHandle();
654 return nullptr;
657 //==============================================================================
658 void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo)
660 // if component methods are being called from threads other than the message
661 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
662 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
664 if (isOpaque())
665 styleWanted &= ~ComponentPeer::windowIsSemiTransparent;
666 else
667 styleWanted |= ComponentPeer::windowIsSemiTransparent;
669 // don't use getPeer(), so that we only get the peer that's specifically
670 // for this comp, and not for one of its parents.
671 auto* peer = ComponentPeer::getPeerFor (this);
673 if (peer == nullptr || styleWanted != peer->getStyleFlags())
675 const WeakReference<Component> safePointer (this);
677 #if JUCE_LINUX || JUCE_BSD
678 // it's wise to give the component a non-zero size before
679 // putting it on the desktop, as X windows get confused by this, and
680 // a (1, 1) minimum size is enforced here.
681 setSize (jmax (1, getWidth()),
682 jmax (1, getHeight()));
683 #endif
685 const auto unscaledPosition = ScalingHelpers::scaledScreenPosToUnscaled (getScreenPosition());
686 const auto topLeft = ScalingHelpers::unscaledScreenPosToScaled (*this, unscaledPosition);
688 bool wasFullscreen = false;
689 bool wasMinimised = false;
690 ComponentBoundsConstrainer* currentConstrainer = nullptr;
691 Rectangle<int> oldNonFullScreenBounds;
692 int oldRenderingEngine = -1;
694 if (peer != nullptr)
696 std::unique_ptr<ComponentPeer> oldPeerToDelete (peer);
698 wasFullscreen = peer->isFullScreen();
699 wasMinimised = peer->isMinimised();
700 currentConstrainer = peer->getConstrainer();
701 oldNonFullScreenBounds = peer->getNonFullScreenBounds();
702 oldRenderingEngine = peer->getCurrentRenderingEngine();
704 flags.hasHeavyweightPeerFlag = false;
705 Desktop::getInstance().removeDesktopComponent (this);
706 internalHierarchyChanged(); // give comps a chance to react to the peer change before the old peer is deleted.
708 if (safePointer == nullptr)
709 return;
711 setTopLeftPosition (topLeft);
714 if (parentComponent != nullptr)
715 parentComponent->removeChildComponent (this);
717 if (safePointer != nullptr)
719 flags.hasHeavyweightPeerFlag = true;
721 peer = createNewPeer (styleWanted, nativeWindowToAttachTo);
723 Desktop::getInstance().addDesktopComponent (this);
725 boundsRelativeToParent.setPosition (topLeft);
726 peer->updateBounds();
728 if (oldRenderingEngine >= 0)
729 peer->setCurrentRenderingEngine (oldRenderingEngine);
731 peer->setVisible (isVisible());
733 peer = ComponentPeer::getPeerFor (this);
735 if (peer == nullptr)
736 return;
738 if (wasFullscreen)
740 peer->setFullScreen (true);
741 peer->setNonFullScreenBounds (oldNonFullScreenBounds);
744 if (wasMinimised)
745 peer->setMinimised (true);
747 #if JUCE_WINDOWS
748 if (isAlwaysOnTop())
749 peer->setAlwaysOnTop (true);
750 #endif
752 peer->setConstrainer (currentConstrainer);
754 repaint();
756 #if JUCE_LINUX
757 // Creating the peer Image on Linux will change the reported position of the window. If
758 // the Image creation is interleaved with the coming configureNotifyEvents the window
759 // will appear in the wrong position. To avoid this, we force the Image creation here,
760 // before handling any of the configureNotifyEvents. The Linux implementation of
761 // performAnyPendingRepaintsNow() will force update the peer position if necessary.
762 peer->performAnyPendingRepaintsNow();
763 #endif
765 internalHierarchyChanged();
767 if (auto* handler = getAccessibilityHandler())
768 notifyAccessibilityEventInternal (*handler, InternalAccessibilityEvent::windowOpened);
773 void Component::removeFromDesktop()
775 // if component methods are being called from threads other than the message
776 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
777 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
779 if (flags.hasHeavyweightPeerFlag)
781 if (auto* handler = getAccessibilityHandler())
782 notifyAccessibilityEventInternal (*handler, InternalAccessibilityEvent::windowClosed);
784 ComponentHelpers::releaseAllCachedImageResources (*this);
786 auto* peer = ComponentPeer::getPeerFor (this);
787 jassert (peer != nullptr);
789 flags.hasHeavyweightPeerFlag = false;
790 delete peer;
792 Desktop::getInstance().removeDesktopComponent (this);
796 bool Component::isOnDesktop() const noexcept
798 return flags.hasHeavyweightPeerFlag;
801 ComponentPeer* Component::getPeer() const
803 if (flags.hasHeavyweightPeerFlag)
804 return ComponentPeer::getPeerFor (this);
806 if (parentComponent == nullptr)
807 return nullptr;
809 return parentComponent->getPeer();
812 void Component::userTriedToCloseWindow()
814 /* This means that the user's trying to get rid of your window with the 'close window' system
815 menu option (on windows) or possibly the task manager - you should really handle this
816 and delete or hide your component in an appropriate way.
818 If you want to ignore the event and don't want to trigger this assertion, just override
819 this method and do nothing.
821 jassertfalse;
824 void Component::minimisationStateChanged (bool) {}
826 float Component::getDesktopScaleFactor() const { return Desktop::getInstance().getGlobalScaleFactor(); }
828 //==============================================================================
829 void Component::setOpaque (bool shouldBeOpaque)
831 if (shouldBeOpaque != flags.opaqueFlag)
833 flags.opaqueFlag = shouldBeOpaque;
835 if (flags.hasHeavyweightPeerFlag)
836 if (auto* peer = ComponentPeer::getPeerFor (this))
837 addToDesktop (peer->getStyleFlags()); // recreates the heavyweight window
839 repaint();
843 bool Component::isOpaque() const noexcept
845 return flags.opaqueFlag;
848 //==============================================================================
849 struct StandardCachedComponentImage : public CachedComponentImage
851 StandardCachedComponentImage (Component& c) noexcept : owner (c) {}
853 void paint (Graphics& g) override
855 scale = g.getInternalContext().getPhysicalPixelScaleFactor();
856 auto compBounds = owner.getLocalBounds();
857 auto imageBounds = compBounds * scale;
859 if (image.isNull() || image.getBounds() != imageBounds)
861 image = Image (owner.isOpaque() ? Image::RGB
862 : Image::ARGB,
863 jmax (1, imageBounds.getWidth()),
864 jmax (1, imageBounds.getHeight()),
865 ! owner.isOpaque());
867 validArea.clear();
870 if (! validArea.containsRectangle (compBounds))
872 Graphics imG (image);
873 auto& lg = imG.getInternalContext();
875 lg.addTransform (AffineTransform::scale (scale));
877 for (auto& i : validArea)
878 lg.excludeClipRectangle (i);
880 if (! owner.isOpaque())
882 lg.setFill (Colours::transparentBlack);
883 lg.fillRect (compBounds, true);
884 lg.setFill (Colours::black);
887 owner.paintEntireComponent (imG, true);
890 validArea = compBounds;
892 g.setColour (Colours::black.withAlpha (owner.getAlpha()));
893 g.drawImageTransformed (image, AffineTransform::scale ((float) compBounds.getWidth() / (float) imageBounds.getWidth(),
894 (float) compBounds.getHeight() / (float) imageBounds.getHeight()), false);
897 bool invalidateAll() override { validArea.clear(); return true; }
898 bool invalidate (const Rectangle<int>& area) override { validArea.subtract (area); return true; }
899 void releaseResources() override { image = Image(); }
901 private:
902 Image image;
903 RectangleList<int> validArea;
904 Component& owner;
905 float scale = 1.0f;
907 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandardCachedComponentImage)
910 void Component::setCachedComponentImage (CachedComponentImage* newCachedImage)
912 if (cachedImage.get() != newCachedImage)
914 cachedImage.reset (newCachedImage);
915 repaint();
919 void Component::setBufferedToImage (bool shouldBeBuffered)
921 // This assertion means that this component is already using a custom CachedComponentImage,
922 // so by calling setBufferedToImage, you'll be deleting the custom one - this is almost certainly
923 // not what you wanted to happen... If you really do know what you're doing here, and want to
924 // avoid this assertion, just call setCachedComponentImage (nullptr) before setBufferedToImage().
925 jassert (cachedImage == nullptr || dynamic_cast<StandardCachedComponentImage*> (cachedImage.get()) != nullptr);
927 if (shouldBeBuffered)
929 if (cachedImage == nullptr)
930 cachedImage.reset (new StandardCachedComponentImage (*this));
932 else
934 cachedImage.reset();
938 //==============================================================================
939 void Component::reorderChildInternal (int sourceIndex, int destIndex)
941 if (sourceIndex != destIndex)
943 auto* c = childComponentList.getUnchecked (sourceIndex);
944 jassert (c != nullptr);
945 c->repaintParent();
947 childComponentList.move (sourceIndex, destIndex);
949 sendFakeMouseMove();
950 internalChildrenChanged();
954 void Component::toFront (bool shouldGrabKeyboardFocus)
956 // if component methods are being called from threads other than the message
957 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
958 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
960 if (flags.hasHeavyweightPeerFlag)
962 if (auto* peer = getPeer())
964 peer->toFront (shouldGrabKeyboardFocus);
966 if (shouldGrabKeyboardFocus && ! hasKeyboardFocus (true))
967 grabKeyboardFocus();
970 else if (parentComponent != nullptr)
972 auto& childList = parentComponent->childComponentList;
974 if (childList.getLast() != this)
976 auto index = childList.indexOf (this);
978 if (index >= 0)
980 int insertIndex = -1;
982 if (! flags.alwaysOnTopFlag)
984 insertIndex = childList.size() - 1;
986 while (insertIndex > 0 && childList.getUnchecked (insertIndex)->isAlwaysOnTop())
987 --insertIndex;
990 parentComponent->reorderChildInternal (index, insertIndex);
994 if (shouldGrabKeyboardFocus)
996 internalBroughtToFront();
998 if (isShowing())
999 grabKeyboardFocus();
1004 void Component::toBehind (Component* other)
1006 if (other != nullptr && other != this)
1008 // the two components must belong to the same parent..
1009 jassert (parentComponent == other->parentComponent);
1011 if (parentComponent != nullptr)
1013 auto& childList = parentComponent->childComponentList;
1014 auto index = childList.indexOf (this);
1016 if (index >= 0 && childList [index + 1] != other)
1018 auto otherIndex = childList.indexOf (other);
1020 if (otherIndex >= 0)
1022 if (index < otherIndex)
1023 --otherIndex;
1025 parentComponent->reorderChildInternal (index, otherIndex);
1029 else if (isOnDesktop())
1031 jassert (other->isOnDesktop());
1033 if (other->isOnDesktop())
1035 auto* us = getPeer();
1036 auto* them = other->getPeer();
1037 jassert (us != nullptr && them != nullptr);
1039 if (us != nullptr && them != nullptr)
1040 us->toBehind (them);
1046 void Component::toBack()
1048 if (isOnDesktop())
1050 jassertfalse; //xxx need to add this to native window
1052 else if (parentComponent != nullptr)
1054 auto& childList = parentComponent->childComponentList;
1056 if (childList.getFirst() != this)
1058 auto index = childList.indexOf (this);
1060 if (index > 0)
1062 int insertIndex = 0;
1064 if (flags.alwaysOnTopFlag)
1065 while (insertIndex < childList.size() && ! childList.getUnchecked (insertIndex)->isAlwaysOnTop())
1066 ++insertIndex;
1068 parentComponent->reorderChildInternal (index, insertIndex);
1074 void Component::setAlwaysOnTop (bool shouldStayOnTop)
1076 if (shouldStayOnTop != flags.alwaysOnTopFlag)
1078 BailOutChecker checker (this);
1080 flags.alwaysOnTopFlag = shouldStayOnTop;
1082 if (isOnDesktop())
1084 if (auto* peer = getPeer())
1086 if (! peer->setAlwaysOnTop (shouldStayOnTop))
1088 // some kinds of peer can't change their always-on-top status, so
1089 // for these, we'll need to create a new window
1090 auto oldFlags = peer->getStyleFlags();
1091 removeFromDesktop();
1092 addToDesktop (oldFlags);
1097 if (shouldStayOnTop && ! checker.shouldBailOut())
1098 toFront (false);
1100 if (! checker.shouldBailOut())
1101 internalHierarchyChanged();
1105 bool Component::isAlwaysOnTop() const noexcept
1107 return flags.alwaysOnTopFlag;
1110 //==============================================================================
1111 int Component::proportionOfWidth (float proportion) const noexcept { return roundToInt (proportion * (float) boundsRelativeToParent.getWidth()); }
1112 int Component::proportionOfHeight (float proportion) const noexcept { return roundToInt (proportion * (float) boundsRelativeToParent.getHeight()); }
1114 int Component::getParentWidth() const noexcept
1116 return parentComponent != nullptr ? parentComponent->getWidth()
1117 : getParentMonitorArea().getWidth();
1120 int Component::getParentHeight() const noexcept
1122 return parentComponent != nullptr ? parentComponent->getHeight()
1123 : getParentMonitorArea().getHeight();
1126 Rectangle<int> Component::getParentMonitorArea() const
1128 return Desktop::getInstance().getDisplays().getDisplayForRect (getScreenBounds())->userArea;
1131 int Component::getScreenX() const { return getScreenPosition().x; }
1132 int Component::getScreenY() const { return getScreenPosition().y; }
1133 Point<int> Component::getScreenPosition() const { return localPointToGlobal (Point<int>()); }
1134 Rectangle<int> Component::getScreenBounds() const { return localAreaToGlobal (getLocalBounds()); }
1136 Point<int> Component::getLocalPoint (const Component* source, Point<int> point) const { return ComponentHelpers::convertCoordinate (this, source, point); }
1137 Point<float> Component::getLocalPoint (const Component* source, Point<float> point) const { return ComponentHelpers::convertCoordinate (this, source, point); }
1138 Rectangle<int> Component::getLocalArea (const Component* source, Rectangle<int> area) const { return ComponentHelpers::convertCoordinate (this, source, area); }
1139 Rectangle<float> Component::getLocalArea (const Component* source, Rectangle<float> area) const { return ComponentHelpers::convertCoordinate (this, source, area); }
1141 Point<int> Component::localPointToGlobal (Point<int> point) const { return ComponentHelpers::convertCoordinate (nullptr, this, point); }
1142 Point<float> Component::localPointToGlobal (Point<float> point) const { return ComponentHelpers::convertCoordinate (nullptr, this, point); }
1143 Rectangle<int> Component::localAreaToGlobal (Rectangle<int> area) const { return ComponentHelpers::convertCoordinate (nullptr, this, area); }
1144 Rectangle<float> Component::localAreaToGlobal (Rectangle<float> area) const { return ComponentHelpers::convertCoordinate (nullptr, this, area); }
1146 //==============================================================================
1147 void Component::setBounds (int x, int y, int w, int h)
1149 // if component methods are being called from threads other than the message
1150 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
1151 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
1153 if (w < 0) w = 0;
1154 if (h < 0) h = 0;
1156 const bool wasResized = (getWidth() != w || getHeight() != h);
1157 const bool wasMoved = (getX() != x || getY() != y);
1159 #if JUCE_DEBUG
1160 // It's a very bad idea to try to resize a window during its paint() method!
1161 jassert (! (flags.isInsidePaintCall && wasResized && isOnDesktop()));
1162 #endif
1164 if (wasMoved || wasResized)
1166 const bool showing = isShowing();
1168 if (showing)
1170 // send a fake mouse move to trigger enter/exit messages if needed..
1171 sendFakeMouseMove();
1173 if (! flags.hasHeavyweightPeerFlag)
1174 repaintParent();
1177 boundsRelativeToParent.setBounds (x, y, w, h);
1179 if (showing)
1181 if (wasResized)
1182 repaint();
1183 else if (! flags.hasHeavyweightPeerFlag)
1184 repaintParent();
1186 else if (cachedImage != nullptr)
1188 cachedImage->invalidateAll();
1191 flags.isMoveCallbackPending = wasMoved;
1192 flags.isResizeCallbackPending = wasResized;
1194 if (flags.hasHeavyweightPeerFlag)
1195 if (auto* peer = getPeer())
1196 peer->updateBounds();
1198 sendMovedResizedMessagesIfPending();
1202 void Component::sendMovedResizedMessagesIfPending()
1204 const bool wasMoved = flags.isMoveCallbackPending;
1205 const bool wasResized = flags.isResizeCallbackPending;
1207 if (wasMoved || wasResized)
1209 flags.isMoveCallbackPending = false;
1210 flags.isResizeCallbackPending = false;
1212 sendMovedResizedMessages (wasMoved, wasResized);
1216 void Component::sendMovedResizedMessages (bool wasMoved, bool wasResized)
1218 BailOutChecker checker (this);
1220 if (wasMoved)
1222 moved();
1224 if (checker.shouldBailOut())
1225 return;
1228 if (wasResized)
1230 resized();
1232 if (checker.shouldBailOut())
1233 return;
1235 for (int i = childComponentList.size(); --i >= 0;)
1237 childComponentList.getUnchecked(i)->parentSizeChanged();
1239 if (checker.shouldBailOut())
1240 return;
1242 i = jmin (i, childComponentList.size());
1246 if (parentComponent != nullptr)
1247 parentComponent->childBoundsChanged (this);
1249 if (! checker.shouldBailOut())
1251 componentListeners.callChecked (checker, [this, wasMoved, wasResized] (ComponentListener& l)
1253 l.componentMovedOrResized (*this, wasMoved, wasResized);
1257 if ((wasMoved || wasResized) && ! checker.shouldBailOut())
1258 if (auto* handler = getAccessibilityHandler())
1259 notifyAccessibilityEventInternal (*handler, InternalAccessibilityEvent::elementMovedOrResized);
1262 void Component::setSize (int w, int h) { setBounds (getX(), getY(), w, h); }
1264 void Component::setTopLeftPosition (int x, int y) { setTopLeftPosition ({ x, y }); }
1265 void Component::setTopLeftPosition (Point<int> pos) { setBounds (pos.x, pos.y, getWidth(), getHeight()); }
1267 void Component::setTopRightPosition (int x, int y) { setTopLeftPosition (x - getWidth(), y); }
1268 void Component::setBounds (Rectangle<int> r) { setBounds (r.getX(), r.getY(), r.getWidth(), r.getHeight()); }
1270 void Component::setCentrePosition (Point<int> p) { setBounds (getBounds().withCentre (p.transformedBy (getTransform().inverted()))); }
1271 void Component::setCentrePosition (int x, int y) { setCentrePosition ({ x, y }); }
1273 void Component::setCentreRelative (float x, float y)
1275 setCentrePosition (roundToInt ((float) getParentWidth() * x),
1276 roundToInt ((float) getParentHeight() * y));
1279 void Component::setBoundsRelative (Rectangle<float> target)
1281 setBounds ((target * Point<float> ((float) getParentWidth(),
1282 (float) getParentHeight())).toNearestInt());
1285 void Component::setBoundsRelative (float x, float y, float w, float h)
1287 setBoundsRelative ({ x, y, w, h });
1290 void Component::centreWithSize (int width, int height)
1292 auto parentArea = ComponentHelpers::getParentOrMainMonitorBounds (*this)
1293 .transformedBy (getTransform().inverted());
1295 setBounds (parentArea.getCentreX() - width / 2,
1296 parentArea.getCentreY() - height / 2,
1297 width, height);
1300 void Component::setBoundsInset (BorderSize<int> borders)
1302 setBounds (borders.subtractedFrom (ComponentHelpers::getParentOrMainMonitorBounds (*this)));
1305 void Component::setBoundsToFit (Rectangle<int> targetArea, Justification justification, bool onlyReduceInSize)
1307 if (getLocalBounds().isEmpty() || targetArea.isEmpty())
1309 // it's no good calling this method unless both the component and
1310 // target rectangle have a finite size.
1311 jassertfalse;
1312 return;
1315 auto sourceArea = targetArea.withZeroOrigin();
1317 if (onlyReduceInSize
1318 && getWidth() <= targetArea.getWidth()
1319 && getHeight() <= targetArea.getHeight())
1321 sourceArea = getLocalBounds();
1323 else
1325 auto sourceRatio = getHeight() / (double) getWidth();
1326 auto targetRatio = targetArea.getHeight() / (double) targetArea.getWidth();
1328 if (sourceRatio <= targetRatio)
1329 sourceArea.setHeight (jmin (targetArea.getHeight(),
1330 roundToInt (targetArea.getWidth() * sourceRatio)));
1331 else
1332 sourceArea.setWidth (jmin (targetArea.getWidth(),
1333 roundToInt (targetArea.getHeight() / sourceRatio)));
1336 if (! sourceArea.isEmpty())
1337 setBounds (justification.appliedToRectangle (sourceArea, targetArea));
1340 //==============================================================================
1341 void Component::setTransform (const AffineTransform& newTransform)
1343 // If you pass in a transform with no inverse, the component will have no dimensions,
1344 // and there will be all sorts of maths errors when converting coordinates.
1345 jassert (! newTransform.isSingularity());
1347 if (newTransform.isIdentity())
1349 if (affineTransform != nullptr)
1351 repaint();
1352 affineTransform.reset();
1353 repaint();
1354 sendMovedResizedMessages (false, false);
1357 else if (affineTransform == nullptr)
1359 repaint();
1360 affineTransform.reset (new AffineTransform (newTransform));
1361 repaint();
1362 sendMovedResizedMessages (false, false);
1364 else if (*affineTransform != newTransform)
1366 repaint();
1367 *affineTransform = newTransform;
1368 repaint();
1369 sendMovedResizedMessages (false, false);
1373 bool Component::isTransformed() const noexcept
1375 return affineTransform != nullptr;
1378 AffineTransform Component::getTransform() const
1380 return affineTransform != nullptr ? *affineTransform : AffineTransform();
1383 float Component::getApproximateScaleFactorForComponent (const Component* targetComponent)
1385 AffineTransform transform;
1387 for (auto* target = targetComponent; target != nullptr; target = target->getParentComponent())
1389 transform = transform.followedBy (target->getTransform());
1391 if (target->isOnDesktop())
1392 transform = transform.scaled (target->getDesktopScaleFactor());
1395 auto transformScale = std::sqrt (std::abs (transform.getDeterminant()));
1396 return transformScale / Desktop::getInstance().getGlobalScaleFactor();
1399 //==============================================================================
1400 bool Component::hitTest (int x, int y)
1402 if (! flags.ignoresMouseClicksFlag)
1403 return true;
1405 if (flags.allowChildMouseClicksFlag)
1407 for (int i = childComponentList.size(); --i >= 0;)
1409 auto& child = *childComponentList.getUnchecked (i);
1411 if (child.isVisible()
1412 && ComponentHelpers::hitTest (child, ComponentHelpers::convertFromParentSpace (child, Point<int> (x, y).toFloat())))
1413 return true;
1417 return false;
1420 void Component::setInterceptsMouseClicks (bool allowClicks,
1421 bool allowClicksOnChildComponents) noexcept
1423 flags.ignoresMouseClicksFlag = ! allowClicks;
1424 flags.allowChildMouseClicksFlag = allowClicksOnChildComponents;
1427 void Component::getInterceptsMouseClicks (bool& allowsClicksOnThisComponent,
1428 bool& allowsClicksOnChildComponents) const noexcept
1430 allowsClicksOnThisComponent = ! flags.ignoresMouseClicksFlag;
1431 allowsClicksOnChildComponents = flags.allowChildMouseClicksFlag;
1434 bool Component::contains (Point<int> point)
1436 return contains (point.toFloat());
1439 bool Component::contains (Point<float> point)
1441 if (ComponentHelpers::hitTest (*this, point))
1443 if (parentComponent != nullptr)
1444 return parentComponent->contains (ComponentHelpers::convertToParentSpace (*this, point));
1446 if (flags.hasHeavyweightPeerFlag)
1447 if (auto* peer = getPeer())
1448 return peer->contains (ComponentHelpers::localPositionToRawPeerPos (*this, point).roundToInt(), true);
1451 return false;
1454 bool Component::reallyContains (Point<int> point, bool returnTrueIfWithinAChild)
1456 return reallyContains (point.toFloat(), returnTrueIfWithinAChild);
1459 bool Component::reallyContains (Point<float> point, bool returnTrueIfWithinAChild)
1461 if (! contains (point))
1462 return false;
1464 auto* top = getTopLevelComponent();
1465 auto* compAtPosition = top->getComponentAt (top->getLocalPoint (this, point));
1467 return (compAtPosition == this) || (returnTrueIfWithinAChild && isParentOf (compAtPosition));
1470 Component* Component::getComponentAt (Point<int> position)
1472 return getComponentAt (position.toFloat());
1475 Component* Component::getComponentAt (Point<float> position)
1477 if (flags.visibleFlag && ComponentHelpers::hitTest (*this, position))
1479 for (int i = childComponentList.size(); --i >= 0;)
1481 auto* child = childComponentList.getUnchecked (i);
1483 child = child->getComponentAt (ComponentHelpers::convertFromParentSpace (*child, position));
1485 if (child != nullptr)
1486 return child;
1489 return this;
1492 return nullptr;
1495 Component* Component::getComponentAt (int x, int y)
1497 return getComponentAt (Point<int> { x, y });
1500 //==============================================================================
1501 void Component::addChildComponent (Component& child, int zOrder)
1503 // if component methods are being called from threads other than the message
1504 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
1505 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
1507 jassert (this != &child); // adding a component to itself!?
1509 if (child.parentComponent != this)
1511 if (child.parentComponent != nullptr)
1512 child.parentComponent->removeChildComponent (&child);
1513 else
1514 child.removeFromDesktop();
1516 child.parentComponent = this;
1518 if (child.isVisible())
1519 child.repaintParent();
1521 if (! child.isAlwaysOnTop())
1523 if (zOrder < 0 || zOrder > childComponentList.size())
1524 zOrder = childComponentList.size();
1526 while (zOrder > 0)
1528 if (! childComponentList.getUnchecked (zOrder - 1)->isAlwaysOnTop())
1529 break;
1531 --zOrder;
1535 childComponentList.insert (zOrder, &child);
1537 child.internalHierarchyChanged();
1538 internalChildrenChanged();
1542 void Component::addAndMakeVisible (Component& child, int zOrder)
1544 child.setVisible (true);
1545 addChildComponent (child, zOrder);
1548 void Component::addChildComponent (Component* child, int zOrder)
1550 if (child != nullptr)
1551 addChildComponent (*child, zOrder);
1554 void Component::addAndMakeVisible (Component* child, int zOrder)
1556 if (child != nullptr)
1557 addAndMakeVisible (*child, zOrder);
1560 void Component::addChildAndSetID (Component* child, const String& childID)
1562 if (child != nullptr)
1564 child->setComponentID (childID);
1565 addAndMakeVisible (child);
1569 void Component::removeChildComponent (Component* child)
1571 removeChildComponent (childComponentList.indexOf (child), true, true);
1574 Component* Component::removeChildComponent (int index)
1576 return removeChildComponent (index, true, true);
1579 Component* Component::removeChildComponent (int index, bool sendParentEvents, bool sendChildEvents)
1581 // if component methods are being called from threads other than the message
1582 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
1583 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
1585 if (auto* child = childComponentList [index])
1587 sendParentEvents = sendParentEvents && child->isShowing();
1589 if (sendParentEvents)
1591 sendFakeMouseMove();
1593 if (child->isVisible())
1594 child->repaintParent();
1597 childComponentList.remove (index);
1598 child->parentComponent = nullptr;
1600 ComponentHelpers::releaseAllCachedImageResources (*child);
1602 // (NB: there are obscure situations where child->isShowing() = false, but it still has the focus)
1603 if (child->hasKeyboardFocus (true))
1605 const WeakReference<Component> safeThis (this);
1607 child->giveAwayKeyboardFocusInternal (sendChildEvents || currentlyFocusedComponent != child);
1609 if (sendParentEvents)
1611 if (safeThis == nullptr)
1612 return child;
1614 grabKeyboardFocus();
1618 if (sendChildEvents)
1619 child->internalHierarchyChanged();
1621 if (sendParentEvents)
1622 internalChildrenChanged();
1624 return child;
1627 return nullptr;
1630 //==============================================================================
1631 void Component::removeAllChildren()
1633 while (! childComponentList.isEmpty())
1634 removeChildComponent (childComponentList.size() - 1);
1637 void Component::deleteAllChildren()
1639 while (! childComponentList.isEmpty())
1640 delete (removeChildComponent (childComponentList.size() - 1));
1643 int Component::getNumChildComponents() const noexcept
1645 return childComponentList.size();
1648 Component* Component::getChildComponent (int index) const noexcept
1650 return childComponentList[index];
1653 int Component::getIndexOfChildComponent (const Component* child) const noexcept
1655 return childComponentList.indexOf (const_cast<Component*> (child));
1658 Component* Component::findChildWithID (StringRef targetID) const noexcept
1660 for (auto* c : childComponentList)
1661 if (c->componentID == targetID)
1662 return c;
1664 return nullptr;
1667 Component* Component::getTopLevelComponent() const noexcept
1669 auto* comp = this;
1671 while (comp->parentComponent != nullptr)
1672 comp = comp->parentComponent;
1674 return const_cast<Component*> (comp);
1677 bool Component::isParentOf (const Component* possibleChild) const noexcept
1679 while (possibleChild != nullptr)
1681 possibleChild = possibleChild->parentComponent;
1683 if (possibleChild == this)
1684 return true;
1687 return false;
1690 //==============================================================================
1691 void Component::parentHierarchyChanged() {}
1692 void Component::childrenChanged() {}
1694 void Component::internalChildrenChanged()
1696 if (componentListeners.isEmpty())
1698 childrenChanged();
1700 else
1702 BailOutChecker checker (this);
1704 childrenChanged();
1706 if (! checker.shouldBailOut())
1707 componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentChildrenChanged (*this); });
1711 void Component::internalHierarchyChanged()
1713 BailOutChecker checker (this);
1715 parentHierarchyChanged();
1717 if (checker.shouldBailOut())
1718 return;
1720 componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentParentHierarchyChanged (*this); });
1722 if (checker.shouldBailOut())
1723 return;
1725 for (int i = childComponentList.size(); --i >= 0;)
1727 childComponentList.getUnchecked (i)->internalHierarchyChanged();
1729 if (checker.shouldBailOut())
1731 // you really shouldn't delete the parent component during a callback telling you
1732 // that it's changed..
1733 jassertfalse;
1734 return;
1737 i = jmin (i, childComponentList.size());
1740 if (flags.hasHeavyweightPeerFlag)
1741 if (auto* handler = getAccessibilityHandler())
1742 handler->notifyAccessibilityEvent (AccessibilityEvent::structureChanged);
1745 //==============================================================================
1746 #if JUCE_MODAL_LOOPS_PERMITTED
1747 int Component::runModalLoop()
1749 if (! MessageManager::getInstance()->isThisTheMessageThread())
1751 // use a callback so this can be called from non-gui threads
1752 return (int) (pointer_sized_int) MessageManager::getInstance()
1753 ->callFunctionOnMessageThread (&ComponentHelpers::runModalLoopCallback, this);
1756 if (! isCurrentlyModal (false))
1757 enterModalState (true);
1759 return ModalComponentManager::getInstance()->runEventLoopForCurrentComponent();
1761 #endif
1763 //==============================================================================
1764 void Component::enterModalState (bool shouldTakeKeyboardFocus,
1765 ModalComponentManager::Callback* callback,
1766 bool deleteWhenDismissed)
1768 // if component methods are being called from threads other than the message
1769 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
1770 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
1772 if (! isCurrentlyModal (false))
1774 // While this component is in modal state it may block other components from receiving
1775 // mouseExit events. To keep mouseEnter and mouseExit calls balanced on these components,
1776 // we must manually force the mouse to "leave" blocked components.
1777 ComponentHelpers::sendMouseEventToComponentsThatAreBlockedByModal (*this, &Component::internalMouseExit);
1779 auto& mcm = *ModalComponentManager::getInstance();
1780 mcm.startModal (this, deleteWhenDismissed);
1781 mcm.attachCallback (this, callback);
1783 setVisible (true);
1785 if (shouldTakeKeyboardFocus)
1786 grabKeyboardFocus();
1788 else
1790 // Probably a bad idea to try to make a component modal twice!
1791 jassertfalse;
1795 void Component::exitModalState (int returnValue)
1797 WeakReference<Component> deletionChecker (this);
1799 if (isCurrentlyModal (false))
1801 if (MessageManager::getInstance()->isThisTheMessageThread())
1803 auto& mcm = *ModalComponentManager::getInstance();
1804 mcm.endModal (this, returnValue);
1805 mcm.bringModalComponentsToFront();
1807 // While this component is in modal state it may block other components from receiving
1808 // mouseEnter events. To keep mouseEnter and mouseExit calls balanced on these components,
1809 // we must manually force the mouse to "enter" blocked components.
1810 if (deletionChecker != nullptr)
1811 ComponentHelpers::sendMouseEventToComponentsThatAreBlockedByModal (*deletionChecker, &Component::internalMouseEnter);
1813 else
1815 MessageManager::callAsync ([target = WeakReference<Component> { this }, returnValue]
1817 if (target != nullptr)
1818 target->exitModalState (returnValue);
1824 bool Component::isCurrentlyModal (bool onlyConsiderForemostModalComponent) const noexcept
1826 auto& mcm = *ModalComponentManager::getInstance();
1828 return onlyConsiderForemostModalComponent ? mcm.isFrontModalComponent (this)
1829 : mcm.isModal (this);
1832 bool Component::isCurrentlyBlockedByAnotherModalComponent() const
1834 return ComponentHelpers::modalWouldBlockComponent (*this, getCurrentlyModalComponent());
1837 int JUCE_CALLTYPE Component::getNumCurrentlyModalComponents() noexcept
1839 return ModalComponentManager::getInstance()->getNumModalComponents();
1842 Component* JUCE_CALLTYPE Component::getCurrentlyModalComponent (int index) noexcept
1844 return ModalComponentManager::getInstance()->getModalComponent (index);
1847 //==============================================================================
1848 void Component::setBroughtToFrontOnMouseClick (bool shouldBeBroughtToFront) noexcept
1850 flags.bringToFrontOnClickFlag = shouldBeBroughtToFront;
1853 bool Component::isBroughtToFrontOnMouseClick() const noexcept
1855 return flags.bringToFrontOnClickFlag;
1858 //==============================================================================
1859 void Component::setMouseCursor (const MouseCursor& newCursor)
1861 if (cursor != newCursor)
1863 cursor = newCursor;
1865 if (flags.visibleFlag)
1866 updateMouseCursor();
1870 MouseCursor Component::getMouseCursor()
1872 return cursor;
1875 void Component::updateMouseCursor() const
1877 Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
1880 //==============================================================================
1881 void Component::setRepaintsOnMouseActivity (bool shouldRepaint) noexcept
1883 flags.repaintOnMouseActivityFlag = shouldRepaint;
1886 //==============================================================================
1887 float Component::getAlpha() const noexcept
1889 return (255 - componentTransparency) / 255.0f;
1892 void Component::setAlpha (float newAlpha)
1894 auto newIntAlpha = (uint8) (255 - jlimit (0, 255, roundToInt (newAlpha * 255.0)));
1896 if (componentTransparency != newIntAlpha)
1898 componentTransparency = newIntAlpha;
1899 alphaChanged();
1903 void Component::alphaChanged()
1905 if (flags.hasHeavyweightPeerFlag)
1907 if (auto* peer = getPeer())
1908 peer->setAlpha (getAlpha());
1910 else
1912 repaint();
1916 //==============================================================================
1917 void Component::repaint()
1919 internalRepaintUnchecked (getLocalBounds(), true);
1922 void Component::repaint (int x, int y, int w, int h)
1924 internalRepaint ({ x, y, w, h });
1927 void Component::repaint (Rectangle<int> area)
1929 internalRepaint (area);
1932 void Component::repaintParent()
1934 if (parentComponent != nullptr)
1935 parentComponent->internalRepaint (ComponentHelpers::convertToParentSpace (*this, getLocalBounds()));
1938 void Component::internalRepaint (Rectangle<int> area)
1940 area = area.getIntersection (getLocalBounds());
1942 if (! area.isEmpty())
1943 internalRepaintUnchecked (area, false);
1946 void Component::internalRepaintUnchecked (Rectangle<int> area, bool isEntireComponent)
1948 // if component methods are being called from threads other than the message
1949 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
1950 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
1952 if (flags.visibleFlag)
1954 if (cachedImage != nullptr)
1955 if (! (isEntireComponent ? cachedImage->invalidateAll()
1956 : cachedImage->invalidate (area)))
1957 return;
1959 if (area.isEmpty())
1960 return;
1962 if (flags.hasHeavyweightPeerFlag)
1964 if (auto* peer = getPeer())
1966 // Tweak the scaling so that the component's integer size exactly aligns with the peer's scaled size
1967 auto peerBounds = peer->getBounds();
1968 auto scaled = area * Point<float> ((float) peerBounds.getWidth() / (float) getWidth(),
1969 (float) peerBounds.getHeight() / (float) getHeight());
1971 peer->repaint (affineTransform != nullptr ? scaled.transformedBy (*affineTransform) : scaled);
1974 else
1976 if (parentComponent != nullptr)
1977 parentComponent->internalRepaint (ComponentHelpers::convertToParentSpace (*this, area));
1982 //==============================================================================
1983 void Component::paint (Graphics&)
1985 // if your component is marked as opaque, you must implement a paint
1986 // method and ensure that its entire area is completely painted.
1987 jassert (getBounds().isEmpty() || ! isOpaque());
1990 void Component::paintOverChildren (Graphics&)
1992 // all painting is done in the subclasses
1995 //==============================================================================
1996 void Component::paintWithinParentContext (Graphics& g)
1998 g.setOrigin (getPosition());
2000 if (cachedImage != nullptr)
2001 cachedImage->paint (g);
2002 else
2003 paintEntireComponent (g, false);
2006 void Component::paintComponentAndChildren (Graphics& g)
2008 auto clipBounds = g.getClipBounds();
2010 if (flags.dontClipGraphicsFlag && getNumChildComponents() == 0)
2012 paint (g);
2014 else
2016 Graphics::ScopedSaveState ss (g);
2018 if (! (ComponentHelpers::clipObscuredRegions (*this, g, clipBounds, {}) && g.isClipEmpty()))
2019 paint (g);
2022 for (int i = 0; i < childComponentList.size(); ++i)
2024 auto& child = *childComponentList.getUnchecked (i);
2026 if (child.isVisible())
2028 if (child.affineTransform != nullptr)
2030 Graphics::ScopedSaveState ss (g);
2032 g.addTransform (*child.affineTransform);
2034 if ((child.flags.dontClipGraphicsFlag && ! g.isClipEmpty()) || g.reduceClipRegion (child.getBounds()))
2035 child.paintWithinParentContext (g);
2037 else if (clipBounds.intersects (child.getBounds()))
2039 Graphics::ScopedSaveState ss (g);
2041 if (child.flags.dontClipGraphicsFlag)
2043 child.paintWithinParentContext (g);
2045 else if (g.reduceClipRegion (child.getBounds()))
2047 bool nothingClipped = true;
2049 for (int j = i + 1; j < childComponentList.size(); ++j)
2051 auto& sibling = *childComponentList.getUnchecked (j);
2053 if (sibling.flags.opaqueFlag && sibling.isVisible() && sibling.affineTransform == nullptr)
2055 nothingClipped = false;
2056 g.excludeClipRegion (sibling.getBounds());
2060 if (nothingClipped || ! g.isClipEmpty())
2061 child.paintWithinParentContext (g);
2067 Graphics::ScopedSaveState ss (g);
2068 paintOverChildren (g);
2071 void Component::paintEntireComponent (Graphics& g, bool ignoreAlphaLevel)
2073 // If sizing a top-level-window and the OS paint message is delivered synchronously
2074 // before resized() is called, then we'll invoke the callback here, to make sure
2075 // the components inside have had a chance to sort their sizes out..
2076 #if JUCE_DEBUG
2077 if (! flags.isInsidePaintCall) // (avoids an assertion in plugins hosted in WaveLab)
2078 #endif
2079 sendMovedResizedMessagesIfPending();
2081 #if JUCE_DEBUG
2082 flags.isInsidePaintCall = true;
2083 #endif
2085 if (effect != nullptr)
2087 auto scale = g.getInternalContext().getPhysicalPixelScaleFactor();
2089 auto scaledBounds = getLocalBounds() * scale;
2091 Image effectImage (flags.opaqueFlag ? Image::RGB : Image::ARGB,
2092 scaledBounds.getWidth(), scaledBounds.getHeight(), ! flags.opaqueFlag);
2094 Graphics g2 (effectImage);
2095 g2.addTransform (AffineTransform::scale ((float) scaledBounds.getWidth() / (float) getWidth(),
2096 (float) scaledBounds.getHeight() / (float) getHeight()));
2097 paintComponentAndChildren (g2);
2100 Graphics::ScopedSaveState ss (g);
2102 g.addTransform (AffineTransform::scale (1.0f / scale));
2103 effect->applyEffect (effectImage, g, scale, ignoreAlphaLevel ? 1.0f : getAlpha());
2105 else if (componentTransparency > 0 && ! ignoreAlphaLevel)
2107 if (componentTransparency < 255)
2109 g.beginTransparencyLayer (getAlpha());
2110 paintComponentAndChildren (g);
2111 g.endTransparencyLayer();
2114 else
2116 paintComponentAndChildren (g);
2119 #if JUCE_DEBUG
2120 flags.isInsidePaintCall = false;
2121 #endif
2124 void Component::setPaintingIsUnclipped (bool shouldPaintWithoutClipping) noexcept
2126 flags.dontClipGraphicsFlag = shouldPaintWithoutClipping;
2129 bool Component::isPaintingUnclipped() const noexcept
2131 return flags.dontClipGraphicsFlag;
2134 //==============================================================================
2135 Image Component::createComponentSnapshot (Rectangle<int> areaToGrab,
2136 bool clipImageToComponentBounds, float scaleFactor)
2138 auto r = areaToGrab;
2140 if (clipImageToComponentBounds)
2141 r = r.getIntersection (getLocalBounds());
2143 if (r.isEmpty())
2144 return {};
2146 auto w = roundToInt (scaleFactor * (float) r.getWidth());
2147 auto h = roundToInt (scaleFactor * (float) r.getHeight());
2149 Image image (flags.opaqueFlag ? Image::RGB : Image::ARGB, w, h, true);
2151 Graphics g (image);
2153 if (w != getWidth() || h != getHeight())
2154 g.addTransform (AffineTransform::scale ((float) w / (float) r.getWidth(),
2155 (float) h / (float) r.getHeight()));
2156 g.setOrigin (-r.getPosition());
2158 paintEntireComponent (g, true);
2160 return image;
2163 void Component::setComponentEffect (ImageEffectFilter* newEffect)
2165 if (effect != newEffect)
2167 effect = newEffect;
2168 repaint();
2172 //==============================================================================
2173 LookAndFeel& Component::getLookAndFeel() const noexcept
2175 for (auto* c = this; c != nullptr; c = c->parentComponent)
2176 if (auto lf = c->lookAndFeel.get())
2177 return *lf;
2179 return LookAndFeel::getDefaultLookAndFeel();
2182 void Component::setLookAndFeel (LookAndFeel* newLookAndFeel)
2184 if (lookAndFeel != newLookAndFeel)
2186 lookAndFeel = newLookAndFeel;
2187 sendLookAndFeelChange();
2191 void Component::lookAndFeelChanged() {}
2192 void Component::colourChanged() {}
2194 void Component::sendLookAndFeelChange()
2196 const WeakReference<Component> safePointer (this);
2197 repaint();
2198 lookAndFeelChanged();
2200 if (safePointer != nullptr)
2202 colourChanged();
2204 if (safePointer != nullptr)
2206 for (int i = childComponentList.size(); --i >= 0;)
2208 childComponentList.getUnchecked (i)->sendLookAndFeelChange();
2210 if (safePointer == nullptr)
2211 return;
2213 i = jmin (i, childComponentList.size());
2219 Colour Component::findColour (int colourID, bool inheritFromParent) const
2221 if (auto* v = properties.getVarPointer (ComponentHelpers::getColourPropertyID (colourID)))
2222 return Colour ((uint32) static_cast<int> (*v));
2224 if (inheritFromParent && parentComponent != nullptr
2225 && (lookAndFeel == nullptr || ! lookAndFeel->isColourSpecified (colourID)))
2226 return parentComponent->findColour (colourID, true);
2228 return getLookAndFeel().findColour (colourID);
2231 bool Component::isColourSpecified (int colourID) const
2233 return properties.contains (ComponentHelpers::getColourPropertyID (colourID));
2236 void Component::removeColour (int colourID)
2238 if (properties.remove (ComponentHelpers::getColourPropertyID (colourID)))
2239 colourChanged();
2242 void Component::setColour (int colourID, Colour colour)
2244 if (properties.set (ComponentHelpers::getColourPropertyID (colourID), (int) colour.getARGB()))
2245 colourChanged();
2248 void Component::copyAllExplicitColoursTo (Component& target) const
2250 bool changed = false;
2252 for (int i = properties.size(); --i >= 0;)
2254 auto name = properties.getName(i);
2256 if (name.toString().startsWith (colourPropertyPrefix))
2257 if (target.properties.set (name, properties [name]))
2258 changed = true;
2261 if (changed)
2262 target.colourChanged();
2265 //==============================================================================
2266 Component::Positioner::Positioner (Component& c) noexcept : component (c)
2270 Component::Positioner* Component::getPositioner() const noexcept
2272 return positioner.get();
2275 void Component::setPositioner (Positioner* newPositioner)
2277 // You can only assign a positioner to the component that it was created for!
2278 jassert (newPositioner == nullptr || this == &(newPositioner->getComponent()));
2279 positioner.reset (newPositioner);
2282 //==============================================================================
2283 Rectangle<int> Component::getLocalBounds() const noexcept
2285 return boundsRelativeToParent.withZeroOrigin();
2288 Rectangle<int> Component::getBoundsInParent() const noexcept
2290 return affineTransform == nullptr ? boundsRelativeToParent
2291 : boundsRelativeToParent.transformedBy (*affineTransform);
2294 //==============================================================================
2295 void Component::mouseEnter (const MouseEvent&) {}
2296 void Component::mouseExit (const MouseEvent&) {}
2297 void Component::mouseDown (const MouseEvent&) {}
2298 void Component::mouseUp (const MouseEvent&) {}
2299 void Component::mouseDrag (const MouseEvent&) {}
2300 void Component::mouseMove (const MouseEvent&) {}
2301 void Component::mouseDoubleClick (const MouseEvent&) {}
2303 void Component::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel)
2305 // the base class just passes this event up to the nearest enabled ancestor
2306 if (auto* enabledComponent = findFirstEnabledAncestor (getParentComponent()))
2307 enabledComponent->mouseWheelMove (e.getEventRelativeTo (enabledComponent), wheel);
2310 void Component::mouseMagnify (const MouseEvent& e, float magnifyAmount)
2312 // the base class just passes this event up to the nearest enabled ancestor
2313 if (auto* enabledComponent = findFirstEnabledAncestor (getParentComponent()))
2314 enabledComponent->mouseMagnify (e.getEventRelativeTo (enabledComponent), magnifyAmount);
2317 //==============================================================================
2318 void Component::resized() {}
2319 void Component::moved() {}
2320 void Component::childBoundsChanged (Component*) {}
2321 void Component::parentSizeChanged() {}
2323 void Component::addComponentListener (ComponentListener* newListener)
2325 // if component methods are being called from threads other than the message
2326 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
2327 #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS
2328 if (getParentComponent() != nullptr)
2330 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
2332 #endif
2334 componentListeners.add (newListener);
2337 void Component::removeComponentListener (ComponentListener* listenerToRemove)
2339 componentListeners.remove (listenerToRemove);
2342 //==============================================================================
2343 void Component::inputAttemptWhenModal()
2345 ModalComponentManager::getInstance()->bringModalComponentsToFront();
2346 getLookAndFeel().playAlertSound();
2349 bool Component::canModalEventBeSentToComponent (const Component*)
2351 return false;
2354 void Component::internalModalInputAttempt()
2356 if (auto* current = getCurrentlyModalComponent())
2357 current->inputAttemptWhenModal();
2360 //==============================================================================
2361 void Component::postCommandMessage (int commandID)
2363 MessageManager::callAsync ([target = WeakReference<Component> { this }, commandID]
2365 if (target != nullptr)
2366 target->handleCommandMessage (commandID);
2370 void Component::handleCommandMessage (int)
2372 // used by subclasses
2375 //==============================================================================
2376 void Component::addMouseListener (MouseListener* newListener,
2377 bool wantsEventsForAllNestedChildComponents)
2379 // if component methods are being called from threads other than the message
2380 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
2381 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
2383 // If you register a component as a mouselistener for itself, it'll receive all the events
2384 // twice - once via the direct callback that all components get anyway, and then again as a listener!
2385 jassert ((newListener != this) || wantsEventsForAllNestedChildComponents);
2387 if (mouseListeners == nullptr)
2388 mouseListeners.reset (new MouseListenerList());
2390 mouseListeners->addListener (newListener, wantsEventsForAllNestedChildComponents);
2393 void Component::removeMouseListener (MouseListener* listenerToRemove)
2395 // if component methods are being called from threads other than the message
2396 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
2397 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
2399 if (mouseListeners != nullptr)
2400 mouseListeners->removeListener (listenerToRemove);
2403 //==============================================================================
2404 void Component::internalMouseEnter (MouseInputSource source, Point<float> relativePos, Time time)
2406 if (isCurrentlyBlockedByAnotherModalComponent())
2408 // if something else is modal, always just show a normal mouse cursor
2409 source.showMouseCursor (MouseCursor::NormalCursor);
2410 return;
2413 if (flags.repaintOnMouseActivityFlag)
2414 repaint();
2416 BailOutChecker checker (this);
2418 const auto me = makeMouseEvent (source,
2419 PointerState().withPosition (relativePos),
2420 source.getCurrentModifiers(),
2421 this,
2422 this,
2423 time,
2424 relativePos,
2425 time,
2427 false);
2428 mouseEnter (me);
2430 flags.cachedMouseInsideComponent = true;
2432 if (checker.shouldBailOut())
2433 return;
2435 Desktop::getInstance().getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseEnter (me); });
2437 MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseEnter, me);
2440 void Component::internalMouseExit (MouseInputSource source, Point<float> relativePos, Time time)
2442 if (isCurrentlyBlockedByAnotherModalComponent())
2444 // if something else is modal, always just show a normal mouse cursor
2445 source.showMouseCursor (MouseCursor::NormalCursor);
2446 return;
2449 if (flags.repaintOnMouseActivityFlag)
2450 repaint();
2452 flags.cachedMouseInsideComponent = false;
2454 BailOutChecker checker (this);
2456 const auto me = makeMouseEvent (source,
2457 PointerState().withPosition (relativePos),
2458 source.getCurrentModifiers(),
2459 this,
2460 this,
2461 time,
2462 relativePos,
2463 time,
2465 false);
2467 mouseExit (me);
2469 if (checker.shouldBailOut())
2470 return;
2472 Desktop::getInstance().getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseExit (me); });
2474 MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseExit, me);
2477 void Component::internalMouseDown (MouseInputSource source, const PointerState& relativePointerState, Time time)
2479 auto& desktop = Desktop::getInstance();
2480 BailOutChecker checker (this);
2482 if (isCurrentlyBlockedByAnotherModalComponent())
2484 flags.mouseDownWasBlocked = true;
2485 internalModalInputAttempt();
2487 if (checker.shouldBailOut())
2488 return;
2490 // If processing the input attempt has exited the modal loop, we'll allow the event
2491 // to be delivered..
2492 if (isCurrentlyBlockedByAnotherModalComponent())
2494 // allow blocked mouse-events to go to global listeners..
2495 const auto me = makeMouseEvent (source,
2496 relativePointerState,
2497 source.getCurrentModifiers(),
2498 this,
2499 this,
2500 time,
2501 relativePointerState.position,
2502 time,
2503 source.getNumberOfMultipleClicks(),
2504 false);
2506 desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDown (me); });
2507 return;
2511 flags.mouseDownWasBlocked = false;
2513 for (auto* c = this; c != nullptr; c = c->parentComponent)
2515 if (c->isBroughtToFrontOnMouseClick())
2517 c->toFront (true);
2519 if (checker.shouldBailOut())
2520 return;
2524 if (! flags.dontFocusOnMouseClickFlag)
2526 grabKeyboardFocusInternal (focusChangedByMouseClick, true);
2528 if (checker.shouldBailOut())
2529 return;
2532 if (flags.repaintOnMouseActivityFlag)
2533 repaint();
2535 const auto me = makeMouseEvent (source,
2536 relativePointerState,
2537 source.getCurrentModifiers(),
2538 this,
2539 this,
2540 time,
2541 relativePointerState.position,
2542 time,
2543 source.getNumberOfMultipleClicks(),
2544 false);
2545 mouseDown (me);
2547 if (checker.shouldBailOut())
2548 return;
2550 desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDown (me); });
2552 MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseDown, me);
2555 void Component::internalMouseUp (MouseInputSource source, const PointerState& relativePointerState, Time time, const ModifierKeys oldModifiers)
2557 if (flags.mouseDownWasBlocked && isCurrentlyBlockedByAnotherModalComponent())
2558 return;
2560 BailOutChecker checker (this);
2562 if (flags.repaintOnMouseActivityFlag)
2563 repaint();
2565 const auto me = makeMouseEvent (source,
2566 relativePointerState,
2567 oldModifiers,
2568 this,
2569 this,
2570 time,
2571 getLocalPoint (nullptr, source.getLastMouseDownPosition()),
2572 source.getLastMouseDownTime(),
2573 source.getNumberOfMultipleClicks(),
2574 source.isLongPressOrDrag());
2575 mouseUp (me);
2577 if (checker.shouldBailOut())
2578 return;
2580 auto& desktop = Desktop::getInstance();
2581 desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseUp (me); });
2583 MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseUp, me);
2585 if (checker.shouldBailOut())
2586 return;
2588 // check for double-click
2589 if (me.getNumberOfClicks() >= 2)
2591 mouseDoubleClick (me);
2593 if (checker.shouldBailOut())
2594 return;
2596 desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseDoubleClick (me); });
2597 MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseDoubleClick, me);
2601 void Component::internalMouseDrag (MouseInputSource source, const PointerState& relativePointerState, Time time)
2603 if (! isCurrentlyBlockedByAnotherModalComponent())
2605 BailOutChecker checker (this);
2607 const auto me = makeMouseEvent (source,
2608 relativePointerState,
2609 source.getCurrentModifiers(),
2610 this,
2611 this,
2612 time,
2613 getLocalPoint (nullptr, source.getLastMouseDownPosition()),
2614 source.getLastMouseDownTime(),
2615 source.getNumberOfMultipleClicks(),
2616 source.isLongPressOrDrag());
2617 mouseDrag (me);
2619 if (checker.shouldBailOut())
2620 return;
2622 Desktop::getInstance().getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDrag (me); });
2624 MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseDrag, me);
2628 void Component::internalMouseMove (MouseInputSource source, Point<float> relativePos, Time time)
2630 auto& desktop = Desktop::getInstance();
2632 if (isCurrentlyBlockedByAnotherModalComponent())
2634 // allow blocked mouse-events to go to global listeners..
2635 desktop.sendMouseMove();
2637 else
2639 BailOutChecker checker (this);
2641 const auto me = makeMouseEvent (source,
2642 PointerState().withPosition (relativePos),
2643 source.getCurrentModifiers(),
2644 this,
2645 this,
2646 time,
2647 relativePos,
2648 time,
2650 false);
2651 mouseMove (me);
2653 if (checker.shouldBailOut())
2654 return;
2656 desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseMove (me); });
2658 MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseMove, me);
2662 void Component::internalMouseWheel (MouseInputSource source, Point<float> relativePos,
2663 Time time, const MouseWheelDetails& wheel)
2665 auto& desktop = Desktop::getInstance();
2666 BailOutChecker checker (this);
2668 const auto me = makeMouseEvent (source,
2669 PointerState().withPosition (relativePos),
2670 source.getCurrentModifiers(),
2671 this,
2672 this,
2673 time,
2674 relativePos,
2675 time,
2677 false);
2679 if (isCurrentlyBlockedByAnotherModalComponent())
2681 // allow blocked mouse-events to go to global listeners..
2682 desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseWheelMove (me, wheel); });
2684 else
2686 mouseWheelMove (me, wheel);
2688 if (checker.shouldBailOut())
2689 return;
2691 desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseWheelMove (me, wheel); });
2693 if (! checker.shouldBailOut())
2694 MouseListenerList::template sendMouseEvent<const MouseEvent&, const MouseWheelDetails&> (*this, checker, &MouseListener::mouseWheelMove, me, wheel);
2698 void Component::internalMagnifyGesture (MouseInputSource source, Point<float> relativePos,
2699 Time time, float amount)
2701 auto& desktop = Desktop::getInstance();
2702 BailOutChecker checker (this);
2704 const auto me = makeMouseEvent (source,
2705 PointerState().withPosition (relativePos),
2706 source.getCurrentModifiers(),
2707 this,
2708 this,
2709 time,
2710 relativePos,
2711 time,
2713 false);
2715 if (isCurrentlyBlockedByAnotherModalComponent())
2717 // allow blocked mouse-events to go to global listeners..
2718 desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseMagnify (me, amount); });
2720 else
2722 mouseMagnify (me, amount);
2724 if (checker.shouldBailOut())
2725 return;
2727 desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseMagnify (me, amount); });
2729 if (! checker.shouldBailOut())
2730 MouseListenerList::template sendMouseEvent<const MouseEvent&, float> (*this, checker, &MouseListener::mouseMagnify, me, amount);
2734 void Component::sendFakeMouseMove() const
2736 if (flags.ignoresMouseClicksFlag && ! flags.allowChildMouseClicksFlag)
2737 return;
2739 auto mainMouse = Desktop::getInstance().getMainMouseSource();
2741 if (! mainMouse.isDragging())
2742 mainMouse.triggerFakeMove();
2745 void JUCE_CALLTYPE Component::beginDragAutoRepeat (int interval)
2747 Desktop::getInstance().beginDragAutoRepeat (interval);
2750 //==============================================================================
2751 void Component::broughtToFront()
2755 void Component::internalBroughtToFront()
2757 if (flags.hasHeavyweightPeerFlag)
2758 Desktop::getInstance().componentBroughtToFront (this);
2760 BailOutChecker checker (this);
2761 broughtToFront();
2763 if (checker.shouldBailOut())
2764 return;
2766 componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentBroughtToFront (*this); });
2768 if (checker.shouldBailOut())
2769 return;
2771 // When brought to the front and there's a modal component blocking this one,
2772 // we need to bring the modal one to the front instead..
2773 if (auto* cm = getCurrentlyModalComponent())
2774 if (cm->getTopLevelComponent() != getTopLevelComponent())
2775 ModalComponentManager::getInstance()->bringModalComponentsToFront (false); // very important that this is false, otherwise in Windows,
2776 // non-front components can't get focus when another modal comp is
2777 // active, and therefore can't receive mouse-clicks
2780 //==============================================================================
2781 void Component::focusGained (FocusChangeType) {}
2782 void Component::focusLost (FocusChangeType) {}
2783 void Component::focusOfChildComponentChanged (FocusChangeType) {}
2785 void Component::internalKeyboardFocusGain (FocusChangeType cause)
2787 internalKeyboardFocusGain (cause, WeakReference<Component> (this));
2790 void Component::internalKeyboardFocusGain (FocusChangeType cause,
2791 const WeakReference<Component>& safePointer)
2793 focusGained (cause);
2795 if (safePointer == nullptr)
2796 return;
2798 if (hasKeyboardFocus (false))
2799 if (auto* handler = getAccessibilityHandler())
2800 handler->grabFocus();
2802 if (safePointer == nullptr)
2803 return;
2805 internalChildKeyboardFocusChange (cause, safePointer);
2808 void Component::internalKeyboardFocusLoss (FocusChangeType cause)
2810 const WeakReference<Component> safePointer (this);
2812 focusLost (cause);
2814 if (safePointer != nullptr)
2816 if (auto* handler = getAccessibilityHandler())
2817 handler->giveAwayFocus();
2819 internalChildKeyboardFocusChange (cause, safePointer);
2823 void Component::internalChildKeyboardFocusChange (FocusChangeType cause,
2824 const WeakReference<Component>& safePointer)
2826 const bool childIsNowKeyboardFocused = hasKeyboardFocus (true);
2828 if (flags.childKeyboardFocusedFlag != childIsNowKeyboardFocused)
2830 flags.childKeyboardFocusedFlag = childIsNowKeyboardFocused;
2832 focusOfChildComponentChanged (cause);
2834 if (safePointer == nullptr)
2835 return;
2838 if (parentComponent != nullptr)
2839 parentComponent->internalChildKeyboardFocusChange (cause, parentComponent);
2842 void Component::setWantsKeyboardFocus (bool wantsFocus) noexcept
2844 flags.wantsKeyboardFocusFlag = wantsFocus;
2847 void Component::setMouseClickGrabsKeyboardFocus (bool shouldGrabFocus)
2849 flags.dontFocusOnMouseClickFlag = ! shouldGrabFocus;
2852 bool Component::getMouseClickGrabsKeyboardFocus() const noexcept
2854 return ! flags.dontFocusOnMouseClickFlag;
2857 bool Component::getWantsKeyboardFocus() const noexcept
2859 return flags.wantsKeyboardFocusFlag && ! flags.isDisabledFlag;
2862 void Component::setFocusContainerType (FocusContainerType containerType) noexcept
2864 flags.isFocusContainerFlag = (containerType == FocusContainerType::focusContainer
2865 || containerType == FocusContainerType::keyboardFocusContainer);
2867 flags.isKeyboardFocusContainerFlag = (containerType == FocusContainerType::keyboardFocusContainer);
2870 bool Component::isFocusContainer() const noexcept
2872 return flags.isFocusContainerFlag;
2875 bool Component::isKeyboardFocusContainer() const noexcept
2877 return flags.isKeyboardFocusContainerFlag;
2880 template <typename FocusContainerFn>
2881 static Component* findContainer (const Component* child, FocusContainerFn isFocusContainer)
2883 if (auto* parent = child->getParentComponent())
2885 if ((parent->*isFocusContainer)() || parent->getParentComponent() == nullptr)
2886 return parent;
2888 return findContainer (parent, isFocusContainer);
2891 return nullptr;
2894 Component* Component::findFocusContainer() const
2896 return findContainer (this, &Component::isFocusContainer);
2899 Component* Component::findKeyboardFocusContainer() const
2901 return findContainer (this, &Component::isKeyboardFocusContainer);
2904 static const Identifier juce_explicitFocusOrderId ("_jexfo");
2906 int Component::getExplicitFocusOrder() const
2908 return properties [juce_explicitFocusOrderId];
2911 void Component::setExplicitFocusOrder (int newFocusOrderIndex)
2913 properties.set (juce_explicitFocusOrderId, newFocusOrderIndex);
2916 std::unique_ptr<ComponentTraverser> Component::createFocusTraverser()
2918 if (flags.isFocusContainerFlag || parentComponent == nullptr)
2919 return std::make_unique<FocusTraverser>();
2921 return parentComponent->createFocusTraverser();
2924 std::unique_ptr<ComponentTraverser> Component::createKeyboardFocusTraverser()
2926 if (flags.isKeyboardFocusContainerFlag || parentComponent == nullptr)
2927 return std::make_unique<KeyboardFocusTraverser>();
2929 return parentComponent->createKeyboardFocusTraverser();
2932 void Component::takeKeyboardFocus (FocusChangeType cause)
2934 if (currentlyFocusedComponent == this)
2935 return;
2937 if (auto* peer = getPeer())
2939 const WeakReference<Component> safePointer (this);
2940 peer->grabFocus();
2942 if (! peer->isFocused() || currentlyFocusedComponent == this)
2943 return;
2945 WeakReference<Component> componentLosingFocus (currentlyFocusedComponent);
2947 if (auto* losingFocus = componentLosingFocus.get())
2948 if (auto* otherPeer = losingFocus->getPeer())
2949 otherPeer->closeInputMethodContext();
2951 currentlyFocusedComponent = this;
2953 Desktop::getInstance().triggerFocusCallback();
2955 // call this after setting currentlyFocusedComponent so that the one that's
2956 // losing it has a chance to see where focus is going
2957 if (componentLosingFocus != nullptr)
2958 componentLosingFocus->internalKeyboardFocusLoss (cause);
2960 if (currentlyFocusedComponent == this)
2961 internalKeyboardFocusGain (cause, safePointer);
2965 void Component::grabKeyboardFocusInternal (FocusChangeType cause, bool canTryParent)
2967 if (! isShowing())
2968 return;
2970 if (flags.wantsKeyboardFocusFlag
2971 && (isEnabled() || parentComponent == nullptr))
2973 takeKeyboardFocus (cause);
2974 return;
2977 if (isParentOf (currentlyFocusedComponent) && currentlyFocusedComponent->isShowing())
2978 return;
2980 if (auto traverser = createKeyboardFocusTraverser())
2982 if (auto* defaultComp = traverser->getDefaultComponent (this))
2984 defaultComp->grabKeyboardFocusInternal (cause, false);
2985 return;
2989 // if no children want it and we're allowed to try our parent comp,
2990 // then pass up to parent, which will try our siblings.
2991 if (canTryParent && parentComponent != nullptr)
2992 parentComponent->grabKeyboardFocusInternal (cause, true);
2995 void Component::grabKeyboardFocus()
2997 // if component methods are being called from threads other than the message
2998 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
2999 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
3001 grabKeyboardFocusInternal (focusChangedDirectly, true);
3003 // A component can only be focused when it's actually on the screen!
3004 // If this fails then you're probably trying to grab the focus before you've
3005 // added the component to a parent or made it visible. Or maybe one of its parent
3006 // components isn't yet visible.
3007 jassert (isShowing() || isOnDesktop());
3010 void Component::giveAwayKeyboardFocusInternal (bool sendFocusLossEvent)
3012 if (hasKeyboardFocus (true))
3014 if (auto* componentLosingFocus = currentlyFocusedComponent)
3016 if (auto* otherPeer = componentLosingFocus->getPeer())
3017 otherPeer->closeInputMethodContext();
3019 currentlyFocusedComponent = nullptr;
3021 if (sendFocusLossEvent && componentLosingFocus != nullptr)
3022 componentLosingFocus->internalKeyboardFocusLoss (focusChangedDirectly);
3024 Desktop::getInstance().triggerFocusCallback();
3029 void Component::giveAwayKeyboardFocus()
3031 // if component methods are being called from threads other than the message
3032 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
3033 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
3035 giveAwayKeyboardFocusInternal (true);
3038 void Component::moveKeyboardFocusToSibling (bool moveToNext)
3040 // if component methods are being called from threads other than the message
3041 // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
3042 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
3044 if (parentComponent != nullptr)
3046 if (auto traverser = createKeyboardFocusTraverser())
3048 auto findComponentToFocus = [&]() -> Component*
3050 if (auto* comp = (moveToNext ? traverser->getNextComponent (this)
3051 : traverser->getPreviousComponent (this)))
3052 return comp;
3054 if (auto* focusContainer = findKeyboardFocusContainer())
3056 auto allFocusableComponents = traverser->getAllComponents (focusContainer);
3058 if (! allFocusableComponents.empty())
3059 return moveToNext ? allFocusableComponents.front()
3060 : allFocusableComponents.back();
3063 return nullptr;
3066 if (auto* nextComp = findComponentToFocus())
3068 if (nextComp->isCurrentlyBlockedByAnotherModalComponent())
3070 const WeakReference<Component> nextCompPointer (nextComp);
3071 internalModalInputAttempt();
3073 if (nextCompPointer == nullptr || nextComp->isCurrentlyBlockedByAnotherModalComponent())
3074 return;
3077 nextComp->grabKeyboardFocusInternal (focusChangedByTabKey, true);
3078 return;
3082 parentComponent->moveKeyboardFocusToSibling (moveToNext);
3086 bool Component::hasKeyboardFocus (bool trueIfChildIsFocused) const
3088 return (currentlyFocusedComponent == this)
3089 || (trueIfChildIsFocused && isParentOf (currentlyFocusedComponent));
3092 Component* JUCE_CALLTYPE Component::getCurrentlyFocusedComponent() noexcept
3094 return currentlyFocusedComponent;
3097 void JUCE_CALLTYPE Component::unfocusAllComponents()
3099 if (currentlyFocusedComponent != nullptr)
3100 currentlyFocusedComponent->giveAwayKeyboardFocus();
3103 //==============================================================================
3104 bool Component::isEnabled() const noexcept
3106 return (! flags.isDisabledFlag)
3107 && (parentComponent == nullptr || parentComponent->isEnabled());
3110 void Component::setEnabled (bool shouldBeEnabled)
3112 if (flags.isDisabledFlag == shouldBeEnabled)
3114 flags.isDisabledFlag = ! shouldBeEnabled;
3116 // if any parent components are disabled, setting our flag won't make a difference,
3117 // so no need to send a change message
3118 if (parentComponent == nullptr || parentComponent->isEnabled())
3119 sendEnablementChangeMessage();
3121 BailOutChecker checker (this);
3122 componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentEnablementChanged (*this); });
3124 if (! shouldBeEnabled && hasKeyboardFocus (true))
3126 if (parentComponent != nullptr)
3127 parentComponent->grabKeyboardFocus();
3129 // ensure that keyboard focus is given away if it wasn't taken by parent
3130 giveAwayKeyboardFocus();
3135 void Component::enablementChanged() {}
3137 void Component::sendEnablementChangeMessage()
3139 const WeakReference<Component> safePointer (this);
3141 enablementChanged();
3143 if (safePointer == nullptr)
3144 return;
3146 for (int i = getNumChildComponents(); --i >= 0;)
3148 if (auto* c = getChildComponent (i))
3150 c->sendEnablementChangeMessage();
3152 if (safePointer == nullptr)
3153 return;
3158 //==============================================================================
3159 bool Component::isMouseOver (bool includeChildren) const
3161 if (! MessageManager::getInstance()->isThisTheMessageThread())
3162 return flags.cachedMouseInsideComponent;
3164 for (auto& ms : Desktop::getInstance().getMouseSources())
3166 auto* c = ms.getComponentUnderMouse();
3168 if (c != nullptr && (c == this || (includeChildren && isParentOf (c))))
3169 if (ms.isDragging() || ! (ms.isTouch() || ms.isPen()))
3170 if (c->reallyContains (c->getLocalPoint (nullptr, ms.getScreenPosition()), false))
3171 return true;
3174 return false;
3177 bool Component::isMouseButtonDown (bool includeChildren) const
3179 for (auto& ms : Desktop::getInstance().getMouseSources())
3181 auto* c = ms.getComponentUnderMouse();
3183 if (c == this || (includeChildren && isParentOf (c)))
3184 if (ms.isDragging())
3185 return true;
3188 return false;
3191 bool Component::isMouseOverOrDragging (bool includeChildren) const
3193 for (auto& ms : Desktop::getInstance().getMouseSources())
3195 auto* c = ms.getComponentUnderMouse();
3197 if (c == this || (includeChildren && isParentOf (c)))
3198 if (ms.isDragging() || ! ms.isTouch())
3199 return true;
3202 return false;
3205 bool JUCE_CALLTYPE Component::isMouseButtonDownAnywhere() noexcept
3207 return ModifierKeys::currentModifiers.isAnyMouseButtonDown();
3210 Point<int> Component::getMouseXYRelative() const
3212 return getLocalPoint (nullptr, Desktop::getMousePosition());
3215 //==============================================================================
3216 void Component::addKeyListener (KeyListener* newListener)
3218 if (keyListeners == nullptr)
3219 keyListeners.reset (new Array<KeyListener*>());
3221 keyListeners->addIfNotAlreadyThere (newListener);
3224 void Component::removeKeyListener (KeyListener* listenerToRemove)
3226 if (keyListeners != nullptr)
3227 keyListeners->removeFirstMatchingValue (listenerToRemove);
3230 bool Component::keyPressed (const KeyPress&) { return false; }
3231 bool Component::keyStateChanged (bool /*isKeyDown*/) { return false; }
3233 void Component::modifierKeysChanged (const ModifierKeys& modifiers)
3235 if (parentComponent != nullptr)
3236 parentComponent->modifierKeysChanged (modifiers);
3239 void Component::internalModifierKeysChanged()
3241 sendFakeMouseMove();
3242 modifierKeysChanged (ModifierKeys::currentModifiers);
3245 //==============================================================================
3246 Component::BailOutChecker::BailOutChecker (Component* component)
3247 : safePointer (component)
3249 jassert (component != nullptr);
3252 bool Component::BailOutChecker::shouldBailOut() const noexcept
3254 return safePointer == nullptr;
3257 //==============================================================================
3258 void Component::setTitle (const String& newTitle)
3260 componentTitle = newTitle;
3263 void Component::setDescription (const String& newDescription)
3265 componentDescription = newDescription;
3268 void Component::setHelpText (const String& newHelpText)
3270 componentHelpText = newHelpText;
3273 void Component::setAccessible (bool shouldBeAccessible)
3275 flags.accessibilityIgnoredFlag = ! shouldBeAccessible;
3277 if (flags.accessibilityIgnoredFlag)
3278 invalidateAccessibilityHandler();
3281 bool Component::isAccessible() const noexcept
3283 return (! flags.accessibilityIgnoredFlag
3284 && (parentComponent == nullptr || parentComponent->isAccessible()));
3287 std::unique_ptr<AccessibilityHandler> Component::createAccessibilityHandler()
3289 return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::unspecified);
3292 std::unique_ptr<AccessibilityHandler> Component::createIgnoredAccessibilityHandler (Component& comp)
3294 return std::make_unique<AccessibilityHandler> (comp, AccessibilityRole::ignored);
3297 void Component::invalidateAccessibilityHandler()
3299 accessibilityHandler = nullptr;
3302 AccessibilityHandler* Component::getAccessibilityHandler()
3304 if (! isAccessible() || getWindowHandle() == nullptr)
3305 return nullptr;
3307 if (accessibilityHandler == nullptr
3308 || accessibilityHandler->getTypeIndex() != std::type_index (typeid (*this)))
3310 accessibilityHandler = createAccessibilityHandler();
3312 // On Android, notifying that an element was created can cause the system to request
3313 // the accessibility node info for the new element. If we're not careful, this will lead
3314 // to recursive calls, as each time an element is created, new node info will be requested,
3315 // causing an element to be created, causing a new info request...
3316 // By assigning the accessibility handler before notifying the system that an element was
3317 // created, the if() predicate above should evaluate to false on recursive calls,
3318 // terminating the recursion.
3319 if (accessibilityHandler != nullptr)
3320 notifyAccessibilityEventInternal (*accessibilityHandler, InternalAccessibilityEvent::elementCreated);
3321 else
3322 jassertfalse; // createAccessibilityHandler must return non-null
3325 return accessibilityHandler.get();
3328 } // namespace juce