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
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
23 ==============================================================================
29 static Component
* findFirstEnabledAncestor (Component
* in
)
37 return findFirstEnabledAncestor (in
->getParentComponent());
40 Component
* Component::currentlyFocusedComponent
= nullptr;
43 //==============================================================================
44 class Component::MouseListenerList
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
;
60 listeners
.add (newListener
);
65 void removeListener (MouseListener
* listenerToRemove
)
67 auto index
= listeners
.indexOf (listenerToRemove
);
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
&),
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
&),
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),
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
,
109 template <typename
... Params
>
110 static void sendMouseEvent (Component
& comp
, Component::BailOutChecker
& checker
,
111 void (MouseListener::*eventMethod
) (Params
...),
115 if (checker
.shouldBailOut())
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())
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())
146 i
= jmin (i
, list
->numDeepMouseListeners
);
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;
170 Component::BailOutChecker
& checker
;
171 const WeakReference
<Component
> safePointer
;
173 JUCE_DECLARE_NON_COPYABLE (BailOutChecker2
)
176 JUCE_DECLARE_NON_COPYABLE (MouseListenerList
)
179 //==============================================================================
182 FocusRestorer() : lastFocus (Component::getCurrentlyFocusedComponent()) {}
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
,
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
,
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();
304 static Identifier
getColourPropertyID (int colourID
)
307 auto* end
= buffer
+ numElementsInArray (buffer
) - 1;
311 for (auto v
= (uint32
) colourID
;;)
313 *--t
= "0123456789abcdef" [v
& 15];
320 for (int i
= (int) sizeof (colourPropertyPrefix
) - 1; --i
>= 0;)
321 *--t
= colourPropertyPrefix
[i
];
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
)));
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
)));
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
);
395 return comp
.affineTransform
!= nullptr ? preTransform
.transformedBy (*comp
.affineTransform
)
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
)
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)
436 auto* topLevelComp
= target
->getTopLevelComponent();
438 p
= convertFromParentSpace (*topLevelComp
, p
);
440 if (topLevelComp
== target
)
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
);
468 auto childPos
= child
.getPosition();
470 if (clipObscuredRegions (child
, g
, newClip
- childPos
, childPos
+ delta
))
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
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);
541 giveAwayKeyboardFocusInternal (isParentOf (currentlyFocusedComponent
));
543 if (flags
.hasHeavyweightPeerFlag
)
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
)
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
;
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);
630 if (! checker
.shouldBailOut())
631 componentListeners
.callChecked (checker
, [this] (ComponentListener
& l
) { l
.componentVisibilityChanged (*this); });
634 bool Component::isShowing() const
636 if (! flags
.visibleFlag
)
639 if (parentComponent
!= nullptr)
640 return parentComponent
->isShowing();
642 if (auto* peer
= getPeer())
643 return ! peer
->isMinimised();
648 //==============================================================================
649 void* Component::getWindowHandle() const
651 if (auto* peer
= getPeer())
652 return peer
->getNativeHandle();
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
665 styleWanted
&= ~ComponentPeer::windowIsSemiTransparent
;
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()));
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;
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)
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);
740 peer
->setFullScreen (true);
741 peer
->setNonFullScreenBounds (oldNonFullScreenBounds
);
745 peer
->setMinimised (true);
749 peer
->setAlwaysOnTop (true);
752 peer
->setConstrainer (currentConstrainer
);
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();
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;
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)
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.
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
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
863 jmax (1, imageBounds
.getWidth()),
864 jmax (1, imageBounds
.getHeight()),
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(); }
903 RectangleList
<int> validArea
;
907 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandardCachedComponentImage
)
910 void Component::setCachedComponentImage (CachedComponentImage
* newCachedImage
)
912 if (cachedImage
.get() != newCachedImage
)
914 cachedImage
.reset (newCachedImage
);
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));
938 //==============================================================================
939 void Component::reorderChildInternal (int sourceIndex
, int destIndex
)
941 if (sourceIndex
!= destIndex
)
943 auto* c
= childComponentList
.getUnchecked (sourceIndex
);
944 jassert (c
!= nullptr);
947 childComponentList
.move (sourceIndex
, destIndex
);
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))
970 else if (parentComponent
!= nullptr)
972 auto& childList
= parentComponent
->childComponentList
;
974 if (childList
.getLast() != this)
976 auto index
= childList
.indexOf (this);
980 int insertIndex
= -1;
982 if (! flags
.alwaysOnTopFlag
)
984 insertIndex
= childList
.size() - 1;
986 while (insertIndex
> 0 && childList
.getUnchecked (insertIndex
)->isAlwaysOnTop())
990 parentComponent
->reorderChildInternal (index
, insertIndex
);
994 if (shouldGrabKeyboardFocus
)
996 internalBroughtToFront();
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
)
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()
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);
1062 int insertIndex
= 0;
1064 if (flags
.alwaysOnTopFlag
)
1065 while (insertIndex
< childList
.size() && ! childList
.getUnchecked (insertIndex
)->isAlwaysOnTop())
1068 parentComponent
->reorderChildInternal (index
, insertIndex
);
1074 void Component::setAlwaysOnTop (bool shouldStayOnTop
)
1076 if (shouldStayOnTop
!= flags
.alwaysOnTopFlag
)
1078 BailOutChecker
checker (this);
1080 flags
.alwaysOnTopFlag
= shouldStayOnTop
;
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())
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
1156 const bool wasResized
= (getWidth() != w
|| getHeight() != h
);
1157 const bool wasMoved
= (getX() != x
|| getY() != y
);
1160 // It's a very bad idea to try to resize a window during its paint() method!
1161 jassert (! (flags
.isInsidePaintCall
&& wasResized
&& isOnDesktop()));
1164 if (wasMoved
|| wasResized
)
1166 const bool showing
= isShowing();
1170 // send a fake mouse move to trigger enter/exit messages if needed..
1171 sendFakeMouseMove();
1173 if (! flags
.hasHeavyweightPeerFlag
)
1177 boundsRelativeToParent
.setBounds (x
, y
, w
, h
);
1183 else if (! flags
.hasHeavyweightPeerFlag
)
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);
1224 if (checker
.shouldBailOut())
1232 if (checker
.shouldBailOut())
1235 for (int i
= childComponentList
.size(); --i
>= 0;)
1237 childComponentList
.getUnchecked(i
)->parentSizeChanged();
1239 if (checker
.shouldBailOut())
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,
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.
1315 auto sourceArea
= targetArea
.withZeroOrigin();
1317 if (onlyReduceInSize
1318 && getWidth() <= targetArea
.getWidth()
1319 && getHeight() <= targetArea
.getHeight())
1321 sourceArea
= getLocalBounds();
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
)));
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)
1352 affineTransform
.reset();
1354 sendMovedResizedMessages (false, false);
1357 else if (affineTransform
== nullptr)
1360 affineTransform
.reset (new AffineTransform (newTransform
));
1362 sendMovedResizedMessages (false, false);
1364 else if (*affineTransform
!= newTransform
)
1367 *affineTransform
= newTransform
;
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
)
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())))
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);
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
))
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)
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
);
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();
1528 if (! childComponentList
.getUnchecked (zOrder
- 1)->isAlwaysOnTop())
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)
1614 grabKeyboardFocus();
1618 if (sendChildEvents
)
1619 child
->internalHierarchyChanged();
1621 if (sendParentEvents
)
1622 internalChildrenChanged();
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
)
1667 Component
* Component::getTopLevelComponent() const noexcept
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)
1690 //==============================================================================
1691 void Component::parentHierarchyChanged() {}
1692 void Component::childrenChanged() {}
1694 void Component::internalChildrenChanged()
1696 if (componentListeners
.isEmpty())
1702 BailOutChecker
checker (this);
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())
1720 componentListeners
.callChecked (checker
, [this] (ComponentListener
& l
) { l
.componentParentHierarchyChanged (*this); });
1722 if (checker
.shouldBailOut())
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..
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();
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
);
1785 if (shouldTakeKeyboardFocus
)
1786 grabKeyboardFocus();
1790 // Probably a bad idea to try to make a component modal twice!
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
);
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
)
1865 if (flags
.visibleFlag
)
1866 updateMouseCursor();
1870 MouseCursor
Component::getMouseCursor()
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
;
1903 void Component::alphaChanged()
1905 if (flags
.hasHeavyweightPeerFlag
)
1907 if (auto* peer
= getPeer())
1908 peer
->setAlpha (getAlpha());
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
)))
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
);
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
);
2003 paintEntireComponent (g
, false);
2006 void Component::paintComponentAndChildren (Graphics
& g
)
2008 auto clipBounds
= g
.getClipBounds();
2010 if (flags
.dontClipGraphicsFlag
&& getNumChildComponents() == 0)
2016 Graphics::ScopedSaveState
ss (g
);
2018 if (! (ComponentHelpers::clipObscuredRegions (*this, g
, clipBounds
, {}) && g
.isClipEmpty()))
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..
2077 if (! flags
.isInsidePaintCall
) // (avoids an assertion in plugins hosted in WaveLab)
2079 sendMovedResizedMessagesIfPending();
2082 flags
.isInsidePaintCall
= true;
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();
2116 paintComponentAndChildren (g
);
2120 flags
.isInsidePaintCall
= false;
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());
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);
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);
2163 void Component::setComponentEffect (ImageEffectFilter
* newEffect
)
2165 if (effect
!= newEffect
)
2172 //==============================================================================
2173 LookAndFeel
& Component::getLookAndFeel() const noexcept
2175 for (auto* c
= this; c
!= nullptr; c
= c
->parentComponent
)
2176 if (auto lf
= c
->lookAndFeel
.get())
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);
2198 lookAndFeelChanged();
2200 if (safePointer
!= nullptr)
2204 if (safePointer
!= nullptr)
2206 for (int i
= childComponentList
.size(); --i
>= 0;)
2208 childComponentList
.getUnchecked (i
)->sendLookAndFeelChange();
2210 if (safePointer
== nullptr)
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
)))
2242 void Component::setColour (int colourID
, Colour colour
)
2244 if (properties
.set (ComponentHelpers::getColourPropertyID (colourID
), (int) colour
.getARGB()))
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
]))
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
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
*)
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
);
2413 if (flags
.repaintOnMouseActivityFlag
)
2416 BailOutChecker
checker (this);
2418 const auto me
= makeMouseEvent (source
,
2419 PointerState().withPosition (relativePos
),
2420 source
.getCurrentModifiers(),
2430 flags
.cachedMouseInsideComponent
= true;
2432 if (checker
.shouldBailOut())
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
);
2449 if (flags
.repaintOnMouseActivityFlag
)
2452 flags
.cachedMouseInsideComponent
= false;
2454 BailOutChecker
checker (this);
2456 const auto me
= makeMouseEvent (source
,
2457 PointerState().withPosition (relativePos
),
2458 source
.getCurrentModifiers(),
2469 if (checker
.shouldBailOut())
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())
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(),
2501 relativePointerState
.position
,
2503 source
.getNumberOfMultipleClicks(),
2506 desktop
.getMouseListeners().callChecked (checker
, [&] (MouseListener
& l
) { l
.mouseDown (me
); });
2511 flags
.mouseDownWasBlocked
= false;
2513 for (auto* c
= this; c
!= nullptr; c
= c
->parentComponent
)
2515 if (c
->isBroughtToFrontOnMouseClick())
2519 if (checker
.shouldBailOut())
2524 if (! flags
.dontFocusOnMouseClickFlag
)
2526 grabKeyboardFocusInternal (focusChangedByMouseClick
, true);
2528 if (checker
.shouldBailOut())
2532 if (flags
.repaintOnMouseActivityFlag
)
2535 const auto me
= makeMouseEvent (source
,
2536 relativePointerState
,
2537 source
.getCurrentModifiers(),
2541 relativePointerState
.position
,
2543 source
.getNumberOfMultipleClicks(),
2547 if (checker
.shouldBailOut())
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())
2560 BailOutChecker
checker (this);
2562 if (flags
.repaintOnMouseActivityFlag
)
2565 const auto me
= makeMouseEvent (source
,
2566 relativePointerState
,
2571 getLocalPoint (nullptr, source
.getLastMouseDownPosition()),
2572 source
.getLastMouseDownTime(),
2573 source
.getNumberOfMultipleClicks(),
2574 source
.isLongPressOrDrag());
2577 if (checker
.shouldBailOut())
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())
2588 // check for double-click
2589 if (me
.getNumberOfClicks() >= 2)
2591 mouseDoubleClick (me
);
2593 if (checker
.shouldBailOut())
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(),
2613 getLocalPoint (nullptr, source
.getLastMouseDownPosition()),
2614 source
.getLastMouseDownTime(),
2615 source
.getNumberOfMultipleClicks(),
2616 source
.isLongPressOrDrag());
2619 if (checker
.shouldBailOut())
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();
2639 BailOutChecker
checker (this);
2641 const auto me
= makeMouseEvent (source
,
2642 PointerState().withPosition (relativePos
),
2643 source
.getCurrentModifiers(),
2653 if (checker
.shouldBailOut())
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(),
2679 if (isCurrentlyBlockedByAnotherModalComponent())
2681 // allow blocked mouse-events to go to global listeners..
2682 desktop
.mouseListeners
.callChecked (checker
, [&] (MouseListener
& l
) { l
.mouseWheelMove (me
, wheel
); });
2686 mouseWheelMove (me
, wheel
);
2688 if (checker
.shouldBailOut())
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(),
2715 if (isCurrentlyBlockedByAnotherModalComponent())
2717 // allow blocked mouse-events to go to global listeners..
2718 desktop
.mouseListeners
.callChecked (checker
, [&] (MouseListener
& l
) { l
.mouseMagnify (me
, amount
); });
2722 mouseMagnify (me
, amount
);
2724 if (checker
.shouldBailOut())
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
)
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);
2763 if (checker
.shouldBailOut())
2766 componentListeners
.callChecked (checker
, [this] (ComponentListener
& l
) { l
.componentBroughtToFront (*this); });
2768 if (checker
.shouldBailOut())
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)
2798 if (hasKeyboardFocus (false))
2799 if (auto* handler
= getAccessibilityHandler())
2800 handler
->grabFocus();
2802 if (safePointer
== nullptr)
2805 internalChildKeyboardFocusChange (cause
, safePointer
);
2808 void Component::internalKeyboardFocusLoss (FocusChangeType cause
)
2810 const WeakReference
<Component
> safePointer (this);
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)
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)
2888 return findContainer (parent
, isFocusContainer
);
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)
2937 if (auto* peer
= getPeer())
2939 const WeakReference
<Component
> safePointer (this);
2942 if (! peer
->isFocused() || currentlyFocusedComponent
== this)
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
)
2970 if (flags
.wantsKeyboardFocusFlag
2971 && (isEnabled() || parentComponent
== nullptr))
2973 takeKeyboardFocus (cause
);
2977 if (isParentOf (currentlyFocusedComponent
) && currentlyFocusedComponent
->isShowing())
2980 if (auto traverser
= createKeyboardFocusTraverser())
2982 if (auto* defaultComp
= traverser
->getDefaultComponent (this))
2984 defaultComp
->grabKeyboardFocusInternal (cause
, false);
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)))
3054 if (auto* focusContainer
= findKeyboardFocusContainer())
3056 auto allFocusableComponents
= traverser
->getAllComponents (focusContainer
);
3058 if (! allFocusableComponents
.empty())
3059 return moveToNext
? allFocusableComponents
.front()
3060 : allFocusableComponents
.back();
3066 if (auto* nextComp
= findComponentToFocus())
3068 if (nextComp
->isCurrentlyBlockedByAnotherModalComponent())
3070 const WeakReference
<Component
> nextCompPointer (nextComp
);
3071 internalModalInputAttempt();
3073 if (nextCompPointer
== nullptr || nextComp
->isCurrentlyBlockedByAnotherModalComponent())
3077 nextComp
->grabKeyboardFocusInternal (focusChangedByTabKey
, true);
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)
3146 for (int i
= getNumChildComponents(); --i
>= 0;)
3148 if (auto* c
= getChildComponent (i
))
3150 c
->sendEnablementChangeMessage();
3152 if (safePointer
== nullptr)
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))
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())
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())
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)
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
);
3322 jassertfalse
; // createAccessibilityHandler must return non-null
3325 return accessibilityHandler
.get();