1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
29 Component* Component::currentlyFocusedComponent = nullptr;
30 
31 
32 //==============================================================================
33 class Component::MouseListenerList
34 {
35 public:
MouseListenerList()36     MouseListenerList() noexcept {}
37 
addListener(MouseListener * newListener,bool wantsEventsForAllNestedChildComponents)38     void addListener (MouseListener* newListener, bool wantsEventsForAllNestedChildComponents)
39     {
40         if (! listeners.contains (newListener))
41         {
42             if (wantsEventsForAllNestedChildComponents)
43             {
44                 listeners.insert (0, newListener);
45                 ++numDeepMouseListeners;
46             }
47             else
48             {
49                 listeners.add (newListener);
50             }
51         }
52     }
53 
removeListener(MouseListener * listenerToRemove)54     void removeListener (MouseListener* listenerToRemove)
55     {
56         auto index = listeners.indexOf (listenerToRemove);
57 
58         if (index >= 0)
59         {
60             if (index < numDeepMouseListeners)
61                 --numDeepMouseListeners;
62 
63             listeners.remove (index);
64         }
65     }
66 
67     // g++ 4.8 cannot deduce the parameter pack inside the function pointer when it has more than one element
68    #if defined(__GNUC__) && __GNUC__ < 5 && ! defined(__clang__)
69     template <typename... Params>
sendMouseEvent(Component & comp,Component::BailOutChecker & checker,void (MouseListener::* eventMethod)(const MouseEvent &),Params...params)70     static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker,
71                                 void (MouseListener::*eventMethod) (const MouseEvent&),
72                                 Params... params)
73     {
74         sendMouseEvent <decltype (eventMethod), Params...> (comp, checker, eventMethod, params...);
75     }
76 
77     template <typename... Params>
sendMouseEvent(Component & comp,Component::BailOutChecker & checker,void (MouseListener::* eventMethod)(const MouseEvent &,const MouseWheelDetails &),Params...params)78     static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker,
79                                 void (MouseListener::*eventMethod) (const MouseEvent&, const MouseWheelDetails&),
80                                 Params... params)
81     {
82         sendMouseEvent <decltype (eventMethod), Params...> (comp, checker, eventMethod, params...);
83     }
84 
85     template <typename... Params>
sendMouseEvent(Component & comp,Component::BailOutChecker & checker,void (MouseListener::* eventMethod)(const MouseEvent &,float),Params...params)86     static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker,
87                                 void (MouseListener::*eventMethod) (const MouseEvent&, float),
88                                 Params... params)
89     {
90         sendMouseEvent <decltype (eventMethod), Params...> (comp, checker, eventMethod, params...);
91     }
92 
93     template <typename EventMethod, typename... Params>
sendMouseEvent(Component & comp,Component::BailOutChecker & checker,EventMethod eventMethod,Params...params)94     static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker,
95                                 EventMethod eventMethod,
96                                 Params... params)
97    #else
98     template <typename... Params>
99     static void sendMouseEvent (Component& comp, Component::BailOutChecker& checker,
100                                 void (MouseListener::*eventMethod) (Params...),
101                                 Params... params)
102    #endif
103     {
104         if (checker.shouldBailOut())
105             return;
106 
107         if (auto* list = comp.mouseListeners.get())
108         {
109             for (int i = list->listeners.size(); --i >= 0;)
110             {
111                 (list->listeners.getUnchecked(i)->*eventMethod) (params...);
112 
113                 if (checker.shouldBailOut())
114                     return;
115 
116                 i = jmin (i, list->listeners.size());
117             }
118         }
119 
120         for (Component* p = comp.parentComponent; p != nullptr; p = p->parentComponent)
121         {
122             if (auto* list = p->mouseListeners.get())
123             {
124                 if (list->numDeepMouseListeners > 0)
125                 {
126                     BailOutChecker2 checker2 (checker, p);
127 
128                     for (int i = list->numDeepMouseListeners; --i >= 0;)
129                     {
130                         (list->listeners.getUnchecked(i)->*eventMethod) (params...);
131 
132                         if (checker2.shouldBailOut())
133                             return;
134 
135                         i = jmin (i, list->numDeepMouseListeners);
136                     }
137                 }
138             }
139         }
140     }
141 
142 private:
143     Array<MouseListener*> listeners;
144     int numDeepMouseListeners = 0;
145 
146     struct BailOutChecker2
147     {
BailOutChecker2juce::Component::MouseListenerList::BailOutChecker2148         BailOutChecker2 (Component::BailOutChecker& boc, Component* comp)
149             : checker (boc), safePointer (comp)
150         {
151         }
152 
shouldBailOutjuce::Component::MouseListenerList::BailOutChecker2153         bool shouldBailOut() const noexcept
154         {
155             return checker.shouldBailOut() || safePointer == nullptr;
156         }
157 
158     private:
159         Component::BailOutChecker& checker;
160         const WeakReference<Component> safePointer;
161 
162         JUCE_DECLARE_NON_COPYABLE (BailOutChecker2)
163     };
164 
165     JUCE_DECLARE_NON_COPYABLE (MouseListenerList)
166 };
167 
168 //==============================================================================
169 struct FocusRestorer
170 {
FocusRestorerjuce::FocusRestorer171     FocusRestorer()  : lastFocus (Component::getCurrentlyFocusedComponent()) {}
172 
~FocusRestorerjuce::FocusRestorer173     ~FocusRestorer()
174     {
175         if (lastFocus != nullptr
176              && lastFocus->isShowing()
177              && ! lastFocus->isCurrentlyBlockedByAnotherModalComponent())
178             lastFocus->grabKeyboardFocus();
179     }
180 
181     WeakReference<Component> lastFocus;
182 
183     JUCE_DECLARE_NON_COPYABLE (FocusRestorer)
184 };
185 
186 //==============================================================================
187 struct ScalingHelpers
188 {
189     template <typename PointOrRect>
unscaledScreenPosToScaledjuce::ScalingHelpers190     static PointOrRect unscaledScreenPosToScaled (float scale, PointOrRect pos) noexcept
191     {
192         return scale != 1.0f ? pos / scale : pos;
193     }
194 
195     template <typename PointOrRect>
scaledScreenPosToUnscaledjuce::ScalingHelpers196     static PointOrRect scaledScreenPosToUnscaled (float scale, PointOrRect pos) noexcept
197     {
198         return scale != 1.0f ? pos * scale : pos;
199     }
200 
201     // For these, we need to avoid getSmallestIntegerContainer being used, which causes
202     // judder when moving windows
unscaledScreenPosToScaledjuce::ScalingHelpers203     static Rectangle<int> unscaledScreenPosToScaled (float scale, Rectangle<int> pos) noexcept
204     {
205         return scale != 1.0f ? Rectangle<int> (roundToInt ((float) pos.getX() / scale),
206                                                roundToInt ((float) pos.getY() / scale),
207                                                roundToInt ((float) pos.getWidth() / scale),
208                                                roundToInt ((float) pos.getHeight() / scale)) : pos;
209     }
210 
scaledScreenPosToUnscaledjuce::ScalingHelpers211     static Rectangle<int> scaledScreenPosToUnscaled (float scale, Rectangle<int> pos) noexcept
212     {
213         return scale != 1.0f ? Rectangle<int> (roundToInt ((float) pos.getX() * scale),
214                                                roundToInt ((float) pos.getY() * scale),
215                                                roundToInt ((float) pos.getWidth() * scale),
216                                                roundToInt ((float) pos.getHeight() * scale)) : pos;
217     }
218 
unscaledScreenPosToScaledjuce::ScalingHelpers219     static Rectangle<float> unscaledScreenPosToScaled (float scale, Rectangle<float> pos) noexcept
220     {
221         return scale != 1.0f ? Rectangle<float> (pos.getX() / scale,
222                                                  pos.getY() / scale,
223                                                  pos.getWidth() / scale,
224                                                  pos.getHeight() / scale) : pos;
225     }
226 
scaledScreenPosToUnscaledjuce::ScalingHelpers227     static Rectangle<float> scaledScreenPosToUnscaled (float scale, Rectangle<float> pos) noexcept
228     {
229         return scale != 1.0f ? Rectangle<float> (pos.getX() * scale,
230                                                  pos.getY() * scale,
231                                                  pos.getWidth() * scale,
232                                                  pos.getHeight() * scale) : pos;
233     }
234 
235     template <typename PointOrRect>
unscaledScreenPosToScaledjuce::ScalingHelpers236     static PointOrRect unscaledScreenPosToScaled (PointOrRect pos) noexcept
237     {
238         return unscaledScreenPosToScaled (Desktop::getInstance().getGlobalScaleFactor(), pos);
239     }
240 
241     template <typename PointOrRect>
scaledScreenPosToUnscaledjuce::ScalingHelpers242     static PointOrRect scaledScreenPosToUnscaled (PointOrRect pos) noexcept
243     {
244         return scaledScreenPosToUnscaled (Desktop::getInstance().getGlobalScaleFactor(), pos);
245     }
246 
247     template <typename PointOrRect>
unscaledScreenPosToScaledjuce::ScalingHelpers248     static PointOrRect unscaledScreenPosToScaled (const Component& comp, PointOrRect pos) noexcept
249     {
250         return unscaledScreenPosToScaled (comp.getDesktopScaleFactor(), pos);
251     }
252 
253     template <typename PointOrRect>
scaledScreenPosToUnscaledjuce::ScalingHelpers254     static PointOrRect scaledScreenPosToUnscaled (const Component& comp, PointOrRect pos) noexcept
255     {
256         return scaledScreenPosToUnscaled (comp.getDesktopScaleFactor(), pos);
257     }
258 
addPositionjuce::ScalingHelpers259     static Point<int>       addPosition      (Point<int> p,       const Component& c) noexcept  { return p + c.getPosition(); }
addPositionjuce::ScalingHelpers260     static Rectangle<int>   addPosition      (Rectangle<int> p,   const Component& c) noexcept  { return p + c.getPosition(); }
addPositionjuce::ScalingHelpers261     static Point<float>     addPosition      (Point<float> p,     const Component& c) noexcept  { return p + c.getPosition().toFloat(); }
addPositionjuce::ScalingHelpers262     static Rectangle<float> addPosition      (Rectangle<float> p, const Component& c) noexcept  { return p + c.getPosition().toFloat(); }
subtractPositionjuce::ScalingHelpers263     static Point<int>       subtractPosition (Point<int> p,       const Component& c) noexcept  { return p - c.getPosition(); }
subtractPositionjuce::ScalingHelpers264     static Rectangle<int>   subtractPosition (Rectangle<int> p,   const Component& c) noexcept  { return p - c.getPosition(); }
subtractPositionjuce::ScalingHelpers265     static Point<float>     subtractPosition (Point<float> p,     const Component& c) noexcept  { return p - c.getPosition().toFloat(); }
subtractPositionjuce::ScalingHelpers266     static Rectangle<float> subtractPosition (Rectangle<float> p, const Component& c) noexcept  { return p - c.getPosition().toFloat(); }
267 };
268 
269 static const char colourPropertyPrefix[] = "jcclr_";
270 
271 //==============================================================================
272 struct Component::ComponentHelpers
273 {
274    #if JUCE_MODAL_LOOPS_PERMITTED
runModalLoopCallbackjuce::Component::ComponentHelpers275     static void* runModalLoopCallback (void* userData)
276     {
277         return (void*) (pointer_sized_int) static_cast<Component*> (userData)->runModalLoop();
278     }
279    #endif
280 
getColourPropertyIDjuce::Component::ComponentHelpers281     static Identifier getColourPropertyID (int colourID)
282     {
283         char buffer[32];
284         auto* end = buffer + numElementsInArray (buffer) - 1;
285         auto* t = end;
286         *t = 0;
287 
288         for (auto v = (uint32) colourID;;)
289         {
290             *--t = "0123456789abcdef" [v & 15];
291             v >>= 4;
292 
293             if (v == 0)
294                 break;
295         }
296 
297         for (int i = (int) sizeof (colourPropertyPrefix) - 1; --i >= 0;)
298             *--t = colourPropertyPrefix[i];
299 
300         return t;
301     }
302 
303     //==============================================================================
hitTestjuce::Component::ComponentHelpers304     static bool hitTest (Component& comp, Point<int> localPoint)
305     {
306         return isPositiveAndBelow (localPoint.x, comp.getWidth())
307             && isPositiveAndBelow (localPoint.y, comp.getHeight())
308             && comp.hitTest (localPoint.x, localPoint.y);
309     }
310 
311     // converts an unscaled position within a peer to the local position within that peer's component
312     template <typename PointOrRect>
rawPeerPositionToLocaljuce::Component::ComponentHelpers313     static PointOrRect rawPeerPositionToLocal (const Component& comp, PointOrRect pos) noexcept
314     {
315         if (comp.isTransformed())
316             pos = pos.transformedBy (comp.getTransform().inverted());
317 
318         return ScalingHelpers::unscaledScreenPosToScaled (comp, pos);
319     }
320 
321     // converts a position within a peer's component to the unscaled position within the peer
322     template <typename PointOrRect>
localPositionToRawPeerPosjuce::Component::ComponentHelpers323     static PointOrRect localPositionToRawPeerPos (const Component& comp, PointOrRect pos) noexcept
324     {
325         if (comp.isTransformed())
326             pos = pos.transformedBy (comp.getTransform());
327 
328         return ScalingHelpers::scaledScreenPosToUnscaled (comp, pos);
329     }
330 
331     template <typename PointOrRect>
convertFromParentSpacejuce::Component::ComponentHelpers332     static PointOrRect convertFromParentSpace (const Component& comp, PointOrRect pointInParentSpace)
333     {
334         if (comp.affineTransform != nullptr)
335             pointInParentSpace = pointInParentSpace.transformedBy (comp.affineTransform->inverted());
336 
337         if (comp.isOnDesktop())
338         {
339             if (auto* peer = comp.getPeer())
340                 pointInParentSpace = ScalingHelpers::unscaledScreenPosToScaled
341                                         (comp, peer->globalToLocal (ScalingHelpers::scaledScreenPosToUnscaled (pointInParentSpace)));
342             else
343                 jassertfalse;
344         }
345         else
346         {
347             pointInParentSpace = ScalingHelpers::subtractPosition (pointInParentSpace, comp);
348         }
349 
350         return pointInParentSpace;
351     }
352 
353     template <typename PointOrRect>
convertToParentSpacejuce::Component::ComponentHelpers354     static PointOrRect convertToParentSpace (const Component& comp, PointOrRect pointInLocalSpace)
355     {
356         if (comp.isOnDesktop())
357         {
358             if (auto* peer = comp.getPeer())
359                 pointInLocalSpace = ScalingHelpers::unscaledScreenPosToScaled
360                                         (peer->localToGlobal (ScalingHelpers::scaledScreenPosToUnscaled (comp, pointInLocalSpace)));
361             else
362                 jassertfalse;
363         }
364         else
365         {
366             pointInLocalSpace = ScalingHelpers::addPosition (pointInLocalSpace, comp);
367         }
368 
369         if (comp.affineTransform != nullptr)
370             pointInLocalSpace = pointInLocalSpace.transformedBy (*comp.affineTransform);
371 
372         return pointInLocalSpace;
373     }
374 
375     template <typename PointOrRect>
convertFromDistantParentSpacejuce::Component::ComponentHelpers376     static PointOrRect convertFromDistantParentSpace (const Component* parent, const Component& target, PointOrRect coordInParent)
377     {
378         auto* directParent = target.getParentComponent();
379         jassert (directParent != nullptr);
380 
381         if (directParent == parent)
382             return convertFromParentSpace (target, coordInParent);
383 
384         return convertFromParentSpace (target, convertFromDistantParentSpace (parent, *directParent, coordInParent));
385     }
386 
387     template <typename PointOrRect>
convertCoordinatejuce::Component::ComponentHelpers388     static PointOrRect convertCoordinate (const Component* target, const Component* source, PointOrRect p)
389     {
390         while (source != nullptr)
391         {
392             if (source == target)
393                 return p;
394 
395             if (source->isParentOf (target))
396                 return convertFromDistantParentSpace (source, *target, p);
397 
398             p = convertToParentSpace (*source, p);
399             source = source->getParentComponent();
400         }
401 
402         jassert (source == nullptr);
403         if (target == nullptr)
404             return p;
405 
406         auto* topLevelComp = target->getTopLevelComponent();
407 
408         p = convertFromParentSpace (*topLevelComp, p);
409 
410         if (topLevelComp == target)
411             return p;
412 
413         return convertFromDistantParentSpace (topLevelComp, *target, p);
414     }
415 
clipObscuredRegionsjuce::Component::ComponentHelpers416     static bool clipObscuredRegions (const Component& comp, Graphics& g,
417                                      const Rectangle<int> clipRect, Point<int> delta)
418     {
419         bool wasClipped = false;
420 
421         for (int i = comp.childComponentList.size(); --i >= 0;)
422         {
423             auto& child = *comp.childComponentList.getUnchecked(i);
424 
425             if (child.isVisible() && ! child.isTransformed())
426             {
427                 auto newClip = clipRect.getIntersection (child.boundsRelativeToParent);
428 
429                 if (! newClip.isEmpty())
430                 {
431                     if (child.isOpaque() && child.componentTransparency == 0)
432                     {
433                         g.excludeClipRegion (newClip + delta);
434                         wasClipped = true;
435                     }
436                     else
437                     {
438                         auto childPos = child.getPosition();
439 
440                         if (clipObscuredRegions (child, g, newClip - childPos, childPos + delta))
441                             wasClipped = true;
442                     }
443                 }
444             }
445         }
446 
447         return wasClipped;
448     }
449 
getParentOrMainMonitorBoundsjuce::Component::ComponentHelpers450     static Rectangle<int> getParentOrMainMonitorBounds (const Component& comp)
451     {
452         if (auto* p = comp.getParentComponent())
453             return p->getLocalBounds();
454 
455         return Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea;
456     }
457 
releaseAllCachedImageResourcesjuce::Component::ComponentHelpers458     static void releaseAllCachedImageResources (Component& c)
459     {
460         if (auto* cached = c.getCachedComponentImage())
461             cached->releaseResources();
462 
463         for (auto* child : c.childComponentList)
464             releaseAllCachedImageResources (*child);
465     }
466 };
467 
468 //==============================================================================
Component()469 Component::Component() noexcept
470   : componentFlags (0)
471 {
472 }
473 
Component(const String & name)474 Component::Component (const String& name) noexcept
475   : componentName (name), componentFlags (0)
476 {
477 }
478 
~Component()479 Component::~Component()
480 {
481     static_assert (sizeof (flags) <= sizeof (componentFlags), "componentFlags has too many bits!");
482 
483     componentListeners.call ([this] (ComponentListener& l) { l.componentBeingDeleted (*this); });
484 
485     masterReference.clear();
486 
487     while (childComponentList.size() > 0)
488         removeChildComponent (childComponentList.size() - 1, false, true);
489 
490     if (parentComponent != nullptr)
491         parentComponent->removeChildComponent (parentComponent->childComponentList.indexOf (this), true, false);
492     else if (hasKeyboardFocus (true))
493         giveAwayFocus (currentlyFocusedComponent != this);
494 
495     if (flags.hasHeavyweightPeerFlag)
496         removeFromDesktop();
497 
498     // Something has added some children to this component during its destructor! Not a smart idea!
499     jassert (childComponentList.size() == 0);
500 }
501 
502 //==============================================================================
setName(const String & name)503 void Component::setName (const String& name)
504 {
505     // if component methods are being called from threads other than the message
506     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
507     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
508 
509     if (componentName != name)
510     {
511         componentName = name;
512 
513         if (flags.hasHeavyweightPeerFlag)
514             if (auto* peer = getPeer())
515                 peer->setTitle (name);
516 
517         BailOutChecker checker (this);
518         componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentNameChanged (*this); });
519     }
520 }
521 
setComponentID(const String & newID)522 void Component::setComponentID (const String& newID)
523 {
524     componentID = newID;
525 }
526 
setVisible(bool shouldBeVisible)527 void Component::setVisible (bool shouldBeVisible)
528 {
529     if (flags.visibleFlag != shouldBeVisible)
530     {
531         // if component methods are being called from threads other than the message
532         // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
533         JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
534 
535         const WeakReference<Component> safePointer (this);
536         flags.visibleFlag = shouldBeVisible;
537 
538         if (shouldBeVisible)
539             repaint();
540         else
541             repaintParent();
542 
543         sendFakeMouseMove();
544 
545         if (! shouldBeVisible)
546         {
547             ComponentHelpers::releaseAllCachedImageResources (*this);
548 
549             if (hasKeyboardFocus (true))
550             {
551                 if (parentComponent != nullptr)
552                     parentComponent->grabKeyboardFocus();
553 
554                 if (hasKeyboardFocus (true))
555                     giveAwayFocus (true);
556             }
557         }
558 
559         if (safePointer != nullptr)
560         {
561             sendVisibilityChangeMessage();
562 
563             if (safePointer != nullptr && flags.hasHeavyweightPeerFlag)
564             {
565                 if (auto* peer = getPeer())
566                 {
567                     peer->setVisible (shouldBeVisible);
568                     internalHierarchyChanged();
569                 }
570             }
571         }
572     }
573 }
574 
visibilityChanged()575 void Component::visibilityChanged() {}
576 
sendVisibilityChangeMessage()577 void Component::sendVisibilityChangeMessage()
578 {
579     BailOutChecker checker (this);
580     visibilityChanged();
581 
582     if (! checker.shouldBailOut())
583         componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentVisibilityChanged (*this); });
584 }
585 
isShowing() const586 bool Component::isShowing() const
587 {
588     if (! flags.visibleFlag)
589         return false;
590 
591     if (parentComponent != nullptr)
592         return parentComponent->isShowing();
593 
594     if (auto* peer = getPeer())
595         return ! peer->isMinimised();
596 
597     return false;
598 }
599 
600 //==============================================================================
getWindowHandle() const601 void* Component::getWindowHandle() const
602 {
603     if (auto* peer = getPeer())
604         return peer->getNativeHandle();
605 
606     return nullptr;
607 }
608 
609 //==============================================================================
addToDesktop(int styleWanted,void * nativeWindowToAttachTo)610 void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo)
611 {
612     // if component methods are being called from threads other than the message
613     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
614     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
615 
616     if (isOpaque())
617         styleWanted &= ~ComponentPeer::windowIsSemiTransparent;
618     else
619         styleWanted |= ComponentPeer::windowIsSemiTransparent;
620 
621     // don't use getPeer(), so that we only get the peer that's specifically
622     // for this comp, and not for one of its parents.
623     auto* peer = ComponentPeer::getPeerFor (this);
624 
625     if (peer == nullptr || styleWanted != peer->getStyleFlags())
626     {
627         const WeakReference<Component> safePointer (this);
628 
629        #if JUCE_LINUX || JUCE_BSD
630         // it's wise to give the component a non-zero size before
631         // putting it on the desktop, as X windows get confused by this, and
632         // a (1, 1) minimum size is enforced here.
633         setSize (jmax (1, getWidth()),
634                  jmax (1, getHeight()));
635        #endif
636 
637         auto topLeft = getScreenPosition();
638 
639         bool wasFullscreen = false;
640         bool wasMinimised = false;
641         ComponentBoundsConstrainer* currentConstrainer = nullptr;
642         Rectangle<int> oldNonFullScreenBounds;
643         int oldRenderingEngine = -1;
644 
645         if (peer != nullptr)
646         {
647             std::unique_ptr<ComponentPeer> oldPeerToDelete (peer);
648 
649             wasFullscreen = peer->isFullScreen();
650             wasMinimised = peer->isMinimised();
651             currentConstrainer = peer->getConstrainer();
652             oldNonFullScreenBounds = peer->getNonFullScreenBounds();
653             oldRenderingEngine = peer->getCurrentRenderingEngine();
654 
655             flags.hasHeavyweightPeerFlag = false;
656             Desktop::getInstance().removeDesktopComponent (this);
657             internalHierarchyChanged(); // give comps a chance to react to the peer change before the old peer is deleted.
658 
659             if (safePointer == nullptr)
660                 return;
661 
662             setTopLeftPosition (topLeft);
663         }
664 
665         if (parentComponent != nullptr)
666             parentComponent->removeChildComponent (this);
667 
668         if (safePointer != nullptr)
669         {
670             flags.hasHeavyweightPeerFlag = true;
671 
672             peer = createNewPeer (styleWanted, nativeWindowToAttachTo);
673 
674             Desktop::getInstance().addDesktopComponent (this);
675 
676             boundsRelativeToParent.setPosition (topLeft);
677             peer->updateBounds();
678 
679             if (oldRenderingEngine >= 0)
680                 peer->setCurrentRenderingEngine (oldRenderingEngine);
681 
682             peer->setVisible (isVisible());
683 
684             peer = ComponentPeer::getPeerFor (this);
685 
686             if (peer == nullptr)
687                 return;
688 
689             if (wasFullscreen)
690             {
691                 peer->setFullScreen (true);
692                 peer->setNonFullScreenBounds (oldNonFullScreenBounds);
693             }
694 
695             if (wasMinimised)
696                 peer->setMinimised (true);
697 
698            #if JUCE_WINDOWS
699             if (isAlwaysOnTop())
700                 peer->setAlwaysOnTop (true);
701            #endif
702 
703             peer->setConstrainer (currentConstrainer);
704 
705             repaint();
706             internalHierarchyChanged();
707         }
708     }
709 }
710 
removeFromDesktop()711 void Component::removeFromDesktop()
712 {
713     // if component methods are being called from threads other than the message
714     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
715     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
716 
717     if (flags.hasHeavyweightPeerFlag)
718     {
719         ComponentHelpers::releaseAllCachedImageResources (*this);
720 
721         auto* peer = ComponentPeer::getPeerFor (this);
722         jassert (peer != nullptr);
723 
724         flags.hasHeavyweightPeerFlag = false;
725         delete peer;
726 
727         Desktop::getInstance().removeDesktopComponent (this);
728     }
729 }
730 
isOnDesktop() const731 bool Component::isOnDesktop() const noexcept
732 {
733     return flags.hasHeavyweightPeerFlag;
734 }
735 
getPeer() const736 ComponentPeer* Component::getPeer() const
737 {
738     if (flags.hasHeavyweightPeerFlag)
739         return ComponentPeer::getPeerFor (this);
740 
741     if (parentComponent == nullptr)
742         return nullptr;
743 
744     return parentComponent->getPeer();
745 }
746 
userTriedToCloseWindow()747 void Component::userTriedToCloseWindow()
748 {
749     /* This means that the user's trying to get rid of your window with the 'close window' system
750        menu option (on windows) or possibly the task manager - you should really handle this
751        and delete or hide your component in an appropriate way.
752 
753        If you want to ignore the event and don't want to trigger this assertion, just override
754        this method and do nothing.
755     */
756     jassertfalse;
757 }
758 
minimisationStateChanged(bool)759 void Component::minimisationStateChanged (bool) {}
760 
getDesktopScaleFactor() const761 float Component::getDesktopScaleFactor() const  { return Desktop::getInstance().getGlobalScaleFactor(); }
762 
763 //==============================================================================
setOpaque(bool shouldBeOpaque)764 void Component::setOpaque (bool shouldBeOpaque)
765 {
766     if (shouldBeOpaque != flags.opaqueFlag)
767     {
768         flags.opaqueFlag = shouldBeOpaque;
769 
770         if (flags.hasHeavyweightPeerFlag)
771             if (auto* peer = ComponentPeer::getPeerFor (this))
772                 addToDesktop (peer->getStyleFlags());  // recreates the heavyweight window
773 
774         repaint();
775     }
776 }
777 
isOpaque() const778 bool Component::isOpaque() const noexcept
779 {
780     return flags.opaqueFlag;
781 }
782 
783 //==============================================================================
784 struct StandardCachedComponentImage  : public CachedComponentImage
785 {
StandardCachedComponentImagejuce::StandardCachedComponentImage786     StandardCachedComponentImage (Component& c) noexcept : owner (c)  {}
787 
paintjuce::StandardCachedComponentImage788     void paint (Graphics& g) override
789     {
790         scale = g.getInternalContext().getPhysicalPixelScaleFactor();
791         auto compBounds = owner.getLocalBounds();
792         auto imageBounds = compBounds * scale;
793 
794         if (image.isNull() || image.getBounds() != imageBounds)
795         {
796             image = Image (owner.isOpaque() ? Image::RGB
797                                             : Image::ARGB,
798                            jmax (1, imageBounds.getWidth()),
799                            jmax (1, imageBounds.getHeight()),
800                            ! owner.isOpaque());
801 
802             validArea.clear();
803         }
804 
805         if (! validArea.containsRectangle (compBounds))
806         {
807             Graphics imG (image);
808             auto& lg = imG.getInternalContext();
809 
810             lg.addTransform (AffineTransform::scale (scale));
811 
812             for (auto& i : validArea)
813                 lg.excludeClipRectangle (i);
814 
815             if (! owner.isOpaque())
816             {
817                 lg.setFill (Colours::transparentBlack);
818                 lg.fillRect (compBounds, true);
819                 lg.setFill (Colours::black);
820             }
821 
822             owner.paintEntireComponent (imG, true);
823         }
824 
825         validArea = compBounds;
826 
827         g.setColour (Colours::black.withAlpha (owner.getAlpha()));
828         g.drawImageTransformed (image, AffineTransform::scale ((float) compBounds.getWidth()  / (float) imageBounds.getWidth(),
829                                                                (float) compBounds.getHeight() / (float) imageBounds.getHeight()), false);
830     }
831 
invalidateAlljuce::StandardCachedComponentImage832     bool invalidateAll() override                            { validArea.clear(); return true; }
invalidatejuce::StandardCachedComponentImage833     bool invalidate (const Rectangle<int>& area) override    { validArea.subtract (area); return true; }
releaseResourcesjuce::StandardCachedComponentImage834     void releaseResources() override                         { image = Image(); }
835 
836 private:
837     Image image;
838     RectangleList<int> validArea;
839     Component& owner;
840     float scale = 1.0f;
841 
842     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandardCachedComponentImage)
843 };
844 
setCachedComponentImage(CachedComponentImage * newCachedImage)845 void Component::setCachedComponentImage (CachedComponentImage* newCachedImage)
846 {
847     if (cachedImage.get() != newCachedImage)
848     {
849         cachedImage.reset (newCachedImage);
850         repaint();
851     }
852 }
853 
setBufferedToImage(bool shouldBeBuffered)854 void Component::setBufferedToImage (bool shouldBeBuffered)
855 {
856     // This assertion means that this component is already using a custom CachedComponentImage,
857     // so by calling setBufferedToImage, you'll be deleting the custom one - this is almost certainly
858     // not what you wanted to happen... If you really do know what you're doing here, and want to
859     // avoid this assertion, just call setCachedComponentImage (nullptr) before setBufferedToImage().
860     jassert (cachedImage == nullptr || dynamic_cast<StandardCachedComponentImage*> (cachedImage.get()) != nullptr);
861 
862     if (shouldBeBuffered)
863     {
864         if (cachedImage == nullptr)
865             cachedImage.reset (new StandardCachedComponentImage (*this));
866     }
867     else
868     {
869         cachedImage.reset();
870     }
871 }
872 
873 //==============================================================================
reorderChildInternal(int sourceIndex,int destIndex)874 void Component::reorderChildInternal (int sourceIndex, int destIndex)
875 {
876     if (sourceIndex != destIndex)
877     {
878         auto* c = childComponentList.getUnchecked (sourceIndex);
879         jassert (c != nullptr);
880         c->repaintParent();
881 
882         childComponentList.move (sourceIndex, destIndex);
883 
884         sendFakeMouseMove();
885         internalChildrenChanged();
886     }
887 }
888 
toFront(bool setAsForeground)889 void Component::toFront (bool setAsForeground)
890 {
891     // if component methods are being called from threads other than the message
892     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
893     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
894 
895     if (flags.hasHeavyweightPeerFlag)
896     {
897         if (auto* peer = getPeer())
898         {
899             peer->toFront (setAsForeground);
900 
901             if (setAsForeground && ! hasKeyboardFocus (true))
902                 grabKeyboardFocus();
903         }
904     }
905     else if (parentComponent != nullptr)
906     {
907         auto& childList = parentComponent->childComponentList;
908 
909         if (childList.getLast() != this)
910         {
911             auto index = childList.indexOf (this);
912 
913             if (index >= 0)
914             {
915                 int insertIndex = -1;
916 
917                 if (! flags.alwaysOnTopFlag)
918                 {
919                     insertIndex = childList.size() - 1;
920 
921                     while (insertIndex > 0 && childList.getUnchecked (insertIndex)->isAlwaysOnTop())
922                         --insertIndex;
923                 }
924 
925                 parentComponent->reorderChildInternal (index, insertIndex);
926             }
927         }
928 
929         if (setAsForeground)
930         {
931             internalBroughtToFront();
932 
933             if (isShowing())
934                 grabKeyboardFocus();
935         }
936     }
937 }
938 
toBehind(Component * other)939 void Component::toBehind (Component* other)
940 {
941     if (other != nullptr && other != this)
942     {
943         // the two components must belong to the same parent..
944         jassert (parentComponent == other->parentComponent);
945 
946         if (parentComponent != nullptr)
947         {
948             auto& childList = parentComponent->childComponentList;
949             auto index = childList.indexOf (this);
950 
951             if (index >= 0 && childList [index + 1] != other)
952             {
953                 auto otherIndex = childList.indexOf (other);
954 
955                 if (otherIndex >= 0)
956                 {
957                     if (index < otherIndex)
958                         --otherIndex;
959 
960                     parentComponent->reorderChildInternal (index, otherIndex);
961                 }
962             }
963         }
964         else if (isOnDesktop())
965         {
966             jassert (other->isOnDesktop());
967 
968             if (other->isOnDesktop())
969             {
970                 auto* us = getPeer();
971                 auto* them = other->getPeer();
972                 jassert (us != nullptr && them != nullptr);
973 
974                 if (us != nullptr && them != nullptr)
975                     us->toBehind (them);
976             }
977         }
978     }
979 }
980 
toBack()981 void Component::toBack()
982 {
983     if (isOnDesktop())
984     {
985         jassertfalse; //xxx need to add this to native window
986     }
987     else if (parentComponent != nullptr)
988     {
989         auto& childList = parentComponent->childComponentList;
990 
991         if (childList.getFirst() != this)
992         {
993             auto index = childList.indexOf (this);
994 
995             if (index > 0)
996             {
997                 int insertIndex = 0;
998 
999                 if (flags.alwaysOnTopFlag)
1000                     while (insertIndex < childList.size() && ! childList.getUnchecked (insertIndex)->isAlwaysOnTop())
1001                         ++insertIndex;
1002 
1003                 parentComponent->reorderChildInternal (index, insertIndex);
1004             }
1005         }
1006     }
1007 }
1008 
setAlwaysOnTop(bool shouldStayOnTop)1009 void Component::setAlwaysOnTop (bool shouldStayOnTop)
1010 {
1011     if (shouldStayOnTop != flags.alwaysOnTopFlag)
1012     {
1013         BailOutChecker checker (this);
1014 
1015         flags.alwaysOnTopFlag = shouldStayOnTop;
1016 
1017         if (isOnDesktop())
1018         {
1019             if (auto* peer = getPeer())
1020             {
1021                 if (! peer->setAlwaysOnTop (shouldStayOnTop))
1022                 {
1023                     // some kinds of peer can't change their always-on-top status, so
1024                     // for these, we'll need to create a new window
1025                     auto oldFlags = peer->getStyleFlags();
1026                     removeFromDesktop();
1027                     addToDesktop (oldFlags);
1028                 }
1029             }
1030         }
1031 
1032         if (shouldStayOnTop && ! checker.shouldBailOut())
1033             toFront (false);
1034 
1035         if (! checker.shouldBailOut())
1036             internalHierarchyChanged();
1037     }
1038 }
1039 
isAlwaysOnTop() const1040 bool Component::isAlwaysOnTop() const noexcept
1041 {
1042     return flags.alwaysOnTopFlag;
1043 }
1044 
1045 //==============================================================================
proportionOfWidth(float proportion) const1046 int Component::proportionOfWidth  (float proportion) const noexcept   { return roundToInt (proportion * (float) boundsRelativeToParent.getWidth()); }
proportionOfHeight(float proportion) const1047 int Component::proportionOfHeight (float proportion) const noexcept   { return roundToInt (proportion * (float) boundsRelativeToParent.getHeight()); }
1048 
getParentWidth() const1049 int Component::getParentWidth() const noexcept
1050 {
1051     return parentComponent != nullptr ? parentComponent->getWidth()
1052                                       : getParentMonitorArea().getWidth();
1053 }
1054 
getParentHeight() const1055 int Component::getParentHeight() const noexcept
1056 {
1057     return parentComponent != nullptr ? parentComponent->getHeight()
1058                                       : getParentMonitorArea().getHeight();
1059 }
1060 
getParentMonitorArea() const1061 Rectangle<int> Component::getParentMonitorArea() const
1062 {
1063     return Desktop::getInstance().getDisplays().getDisplayForRect (getScreenBounds())->userArea;
1064 }
1065 
getScreenX() const1066 int Component::getScreenX() const                       { return getScreenPosition().x; }
getScreenY() const1067 int Component::getScreenY() const                       { return getScreenPosition().y; }
getScreenPosition() const1068 Point<int>     Component::getScreenPosition() const     { return localPointToGlobal (Point<int>()); }
getScreenBounds() const1069 Rectangle<int> Component::getScreenBounds() const       { return localAreaToGlobal (getLocalBounds()); }
1070 
getLocalPoint(const Component * source,Point<int> point) const1071 Point<int>       Component::getLocalPoint (const Component* source, Point<int> point) const       { return ComponentHelpers::convertCoordinate (this, source, point); }
getLocalPoint(const Component * source,Point<float> point) const1072 Point<float>     Component::getLocalPoint (const Component* source, Point<float> point) const     { return ComponentHelpers::convertCoordinate (this, source, point); }
getLocalArea(const Component * source,Rectangle<int> area) const1073 Rectangle<int>   Component::getLocalArea  (const Component* source, Rectangle<int> area) const    { return ComponentHelpers::convertCoordinate (this, source, area); }
getLocalArea(const Component * source,Rectangle<float> area) const1074 Rectangle<float> Component::getLocalArea  (const Component* source, Rectangle<float> area) const  { return ComponentHelpers::convertCoordinate (this, source, area); }
1075 
localPointToGlobal(Point<int> point) const1076 Point<int>       Component::localPointToGlobal (Point<int> point) const       { return ComponentHelpers::convertCoordinate (nullptr, this, point); }
localPointToGlobal(Point<float> point) const1077 Point<float>     Component::localPointToGlobal (Point<float> point) const     { return ComponentHelpers::convertCoordinate (nullptr, this, point); }
localAreaToGlobal(Rectangle<int> area) const1078 Rectangle<int>   Component::localAreaToGlobal  (Rectangle<int> area) const    { return ComponentHelpers::convertCoordinate (nullptr, this, area); }
localAreaToGlobal(Rectangle<float> area) const1079 Rectangle<float> Component::localAreaToGlobal  (Rectangle<float> area) const  { return ComponentHelpers::convertCoordinate (nullptr, this, area); }
1080 
1081 //==============================================================================
setBounds(int x,int y,int w,int h)1082 void Component::setBounds (int x, int y, int w, int h)
1083 {
1084     // if component methods are being called from threads other than the message
1085     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
1086     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
1087 
1088     if (w < 0) w = 0;
1089     if (h < 0) h = 0;
1090 
1091     const bool wasResized  = (getWidth() != w || getHeight() != h);
1092     const bool wasMoved    = (getX() != x || getY() != y);
1093 
1094    #if JUCE_DEBUG
1095     // It's a very bad idea to try to resize a window during its paint() method!
1096     jassert (! (flags.isInsidePaintCall && wasResized && isOnDesktop()));
1097    #endif
1098 
1099     if (wasMoved || wasResized)
1100     {
1101         const bool showing = isShowing();
1102 
1103         if (showing)
1104         {
1105             // send a fake mouse move to trigger enter/exit messages if needed..
1106             sendFakeMouseMove();
1107 
1108             if (! flags.hasHeavyweightPeerFlag)
1109                 repaintParent();
1110         }
1111 
1112         boundsRelativeToParent.setBounds (x, y, w, h);
1113 
1114         if (showing)
1115         {
1116             if (wasResized)
1117                 repaint();
1118             else if (! flags.hasHeavyweightPeerFlag)
1119                 repaintParent();
1120         }
1121         else if (cachedImage != nullptr)
1122         {
1123             cachedImage->invalidateAll();
1124         }
1125 
1126         flags.isMoveCallbackPending = wasMoved;
1127         flags.isResizeCallbackPending = wasResized;
1128 
1129         if (flags.hasHeavyweightPeerFlag)
1130             if (auto* peer = getPeer())
1131                 peer->updateBounds();
1132 
1133         sendMovedResizedMessagesIfPending();
1134     }
1135 }
1136 
sendMovedResizedMessagesIfPending()1137 void Component::sendMovedResizedMessagesIfPending()
1138 {
1139     const bool wasMoved   = flags.isMoveCallbackPending;
1140     const bool wasResized = flags.isResizeCallbackPending;
1141 
1142     if (wasMoved || wasResized)
1143     {
1144         flags.isMoveCallbackPending = false;
1145         flags.isResizeCallbackPending = false;
1146 
1147         sendMovedResizedMessages (wasMoved, wasResized);
1148     }
1149 }
1150 
sendMovedResizedMessages(bool wasMoved,bool wasResized)1151 void Component::sendMovedResizedMessages (bool wasMoved, bool wasResized)
1152 {
1153     BailOutChecker checker (this);
1154 
1155     if (wasMoved)
1156     {
1157         moved();
1158 
1159         if (checker.shouldBailOut())
1160             return;
1161     }
1162 
1163     if (wasResized)
1164     {
1165         resized();
1166 
1167         if (checker.shouldBailOut())
1168             return;
1169 
1170         for (int i = childComponentList.size(); --i >= 0;)
1171         {
1172             childComponentList.getUnchecked(i)->parentSizeChanged();
1173 
1174             if (checker.shouldBailOut())
1175                 return;
1176 
1177             i = jmin (i, childComponentList.size());
1178         }
1179     }
1180 
1181     if (parentComponent != nullptr)
1182         parentComponent->childBoundsChanged (this);
1183 
1184     if (! checker.shouldBailOut())
1185     {
1186         componentListeners.callChecked (checker, [this, wasMoved, wasResized] (ComponentListener& l)
1187         {
1188             l.componentMovedOrResized (*this, wasMoved, wasResized);
1189         });
1190     }
1191 }
1192 
setSize(int w,int h)1193 void Component::setSize (int w, int h)                  { setBounds (getX(), getY(), w, h); }
1194 
setTopLeftPosition(int x,int y)1195 void Component::setTopLeftPosition (int x, int y)       { setTopLeftPosition ({ x, y }); }
setTopLeftPosition(Point<int> pos)1196 void Component::setTopLeftPosition (Point<int> pos)     { setBounds (pos.x, pos.y, getWidth(), getHeight()); }
1197 
setTopRightPosition(int x,int y)1198 void Component::setTopRightPosition (int x, int y)      { setTopLeftPosition (x - getWidth(), y); }
setBounds(Rectangle<int> r)1199 void Component::setBounds (Rectangle<int> r)            { setBounds (r.getX(), r.getY(), r.getWidth(), r.getHeight()); }
1200 
setCentrePosition(Point<int> p)1201 void Component::setCentrePosition (Point<int> p)        { setBounds (getBounds().withCentre (p.transformedBy (getTransform().inverted()))); }
setCentrePosition(int x,int y)1202 void Component::setCentrePosition (int x, int y)        { setCentrePosition ({ x, y }); }
1203 
setCentreRelative(float x,float y)1204 void Component::setCentreRelative (float x, float y)
1205 {
1206     setCentrePosition (roundToInt ((float) getParentWidth()  * x),
1207                        roundToInt ((float) getParentHeight() * y));
1208 }
1209 
setBoundsRelative(Rectangle<float> target)1210 void Component::setBoundsRelative (Rectangle<float> target)
1211 {
1212     setBounds ((target * Point<float> ((float) getParentWidth(),
1213                                        (float) getParentHeight())).toNearestInt());
1214 }
1215 
setBoundsRelative(float x,float y,float w,float h)1216 void Component::setBoundsRelative (float x, float y, float w, float h)
1217 {
1218     setBoundsRelative ({ x, y, w, h });
1219 }
1220 
centreWithSize(int width,int height)1221 void Component::centreWithSize (int width, int height)
1222 {
1223     auto parentArea = ComponentHelpers::getParentOrMainMonitorBounds (*this)
1224                           .transformedBy (getTransform().inverted());
1225 
1226     setBounds (parentArea.getCentreX() - width / 2,
1227                parentArea.getCentreY() - height / 2,
1228                width, height);
1229 }
1230 
setBoundsInset(BorderSize<int> borders)1231 void Component::setBoundsInset (BorderSize<int> borders)
1232 {
1233     setBounds (borders.subtractedFrom (ComponentHelpers::getParentOrMainMonitorBounds (*this)));
1234 }
1235 
setBoundsToFit(Rectangle<int> targetArea,Justification justification,bool onlyReduceInSize)1236 void Component::setBoundsToFit (Rectangle<int> targetArea, Justification justification, bool onlyReduceInSize)
1237 {
1238     if (getLocalBounds().isEmpty() || targetArea.isEmpty())
1239     {
1240         // it's no good calling this method unless both the component and
1241         // target rectangle have a finite size.
1242         jassertfalse;
1243         return;
1244     }
1245 
1246     auto sourceArea = targetArea.withZeroOrigin();
1247 
1248     if (onlyReduceInSize
1249          && getWidth() <= targetArea.getWidth()
1250          && getHeight() <= targetArea.getHeight())
1251     {
1252         sourceArea = getLocalBounds();
1253     }
1254     else
1255     {
1256         auto sourceRatio = getHeight() / (double) getWidth();
1257         auto targetRatio = targetArea.getHeight() / (double) targetArea.getWidth();
1258 
1259         if (sourceRatio <= targetRatio)
1260             sourceArea.setHeight (jmin (targetArea.getHeight(),
1261                                         roundToInt (targetArea.getWidth() * sourceRatio)));
1262         else
1263             sourceArea.setWidth (jmin (targetArea.getWidth(),
1264                                        roundToInt (targetArea.getHeight() / sourceRatio)));
1265     }
1266 
1267     if (! sourceArea.isEmpty())
1268         setBounds (justification.appliedToRectangle (sourceArea, targetArea));
1269 }
1270 
1271 //==============================================================================
setTransform(const AffineTransform & newTransform)1272 void Component::setTransform (const AffineTransform& newTransform)
1273 {
1274     // If you pass in a transform with no inverse, the component will have no dimensions,
1275     // and there will be all sorts of maths errors when converting coordinates.
1276     jassert (! newTransform.isSingularity());
1277 
1278     if (newTransform.isIdentity())
1279     {
1280         if (affineTransform != nullptr)
1281         {
1282             repaint();
1283             affineTransform.reset();
1284             repaint();
1285             sendMovedResizedMessages (false, false);
1286         }
1287     }
1288     else if (affineTransform == nullptr)
1289     {
1290         repaint();
1291         affineTransform.reset (new AffineTransform (newTransform));
1292         repaint();
1293         sendMovedResizedMessages (false, false);
1294     }
1295     else if (*affineTransform != newTransform)
1296     {
1297         repaint();
1298         *affineTransform = newTransform;
1299         repaint();
1300         sendMovedResizedMessages (false, false);
1301     }
1302 }
1303 
isTransformed() const1304 bool Component::isTransformed() const noexcept
1305 {
1306     return affineTransform != nullptr;
1307 }
1308 
getTransform() const1309 AffineTransform Component::getTransform() const
1310 {
1311     return affineTransform != nullptr ? *affineTransform : AffineTransform();
1312 }
1313 
getApproximateScaleFactorForComponent(Component * targetComponent)1314 float Component::getApproximateScaleFactorForComponent (Component* targetComponent)
1315 {
1316     AffineTransform transform;
1317 
1318     for (auto* target = targetComponent; target != nullptr; target = target->getParentComponent())
1319     {
1320         transform = transform.followedBy (target->getTransform());
1321 
1322         if (target->isOnDesktop())
1323             transform = transform.scaled (target->getDesktopScaleFactor());
1324     }
1325 
1326     auto transformScale = std::sqrt (std::abs (transform.getDeterminant()));
1327     return transformScale / Desktop::getInstance().getGlobalScaleFactor();
1328 }
1329 
1330 //==============================================================================
hitTest(int x,int y)1331 bool Component::hitTest (int x, int y)
1332 {
1333     if (! flags.ignoresMouseClicksFlag)
1334         return true;
1335 
1336     if (flags.allowChildMouseClicksFlag)
1337     {
1338         for (int i = childComponentList.size(); --i >= 0;)
1339         {
1340             auto& child = *childComponentList.getUnchecked (i);
1341 
1342             if (child.isVisible()
1343                  && ComponentHelpers::hitTest (child, ComponentHelpers::convertFromParentSpace (child, Point<int> (x, y))))
1344                 return true;
1345         }
1346     }
1347 
1348     return false;
1349 }
1350 
setInterceptsMouseClicks(bool allowClicks,bool allowClicksOnChildComponents)1351 void Component::setInterceptsMouseClicks (bool allowClicks,
1352                                           bool allowClicksOnChildComponents) noexcept
1353 {
1354     flags.ignoresMouseClicksFlag = ! allowClicks;
1355     flags.allowChildMouseClicksFlag = allowClicksOnChildComponents;
1356 }
1357 
getInterceptsMouseClicks(bool & allowsClicksOnThisComponent,bool & allowsClicksOnChildComponents) const1358 void Component::getInterceptsMouseClicks (bool& allowsClicksOnThisComponent,
1359                                           bool& allowsClicksOnChildComponents) const noexcept
1360 {
1361     allowsClicksOnThisComponent = ! flags.ignoresMouseClicksFlag;
1362     allowsClicksOnChildComponents = flags.allowChildMouseClicksFlag;
1363 }
1364 
contains(Point<int> point)1365 bool Component::contains (Point<int> point)
1366 {
1367     if (ComponentHelpers::hitTest (*this, point))
1368     {
1369         if (parentComponent != nullptr)
1370             return parentComponent->contains (ComponentHelpers::convertToParentSpace (*this, point));
1371 
1372         if (flags.hasHeavyweightPeerFlag)
1373             if (auto* peer = getPeer())
1374                 return peer->contains (ComponentHelpers::localPositionToRawPeerPos (*this, point), true);
1375     }
1376 
1377     return false;
1378 }
1379 
reallyContains(Point<int> point,bool returnTrueIfWithinAChild)1380 bool Component::reallyContains (Point<int> point, bool returnTrueIfWithinAChild)
1381 {
1382     if (! contains (point))
1383         return false;
1384 
1385     auto* top = getTopLevelComponent();
1386     auto* compAtPosition = top->getComponentAt (top->getLocalPoint (this, point));
1387 
1388     return (compAtPosition == this) || (returnTrueIfWithinAChild && isParentOf (compAtPosition));
1389 }
1390 
getComponentAt(Point<int> position)1391 Component* Component::getComponentAt (Point<int> position)
1392 {
1393     if (flags.visibleFlag && ComponentHelpers::hitTest (*this, position))
1394     {
1395         for (int i = childComponentList.size(); --i >= 0;)
1396         {
1397             auto* child = childComponentList.getUnchecked(i);
1398 
1399             child = child->getComponentAt (ComponentHelpers::convertFromParentSpace (*child, position));
1400 
1401             if (child != nullptr)
1402                 return child;
1403         }
1404 
1405         return this;
1406     }
1407 
1408     return nullptr;
1409 }
1410 
getComponentAt(int x,int y)1411 Component* Component::getComponentAt (int x, int y)
1412 {
1413     return getComponentAt ({ x, y });
1414 }
1415 
1416 //==============================================================================
addChildComponent(Component & child,int zOrder)1417 void Component::addChildComponent (Component& child, int zOrder)
1418 {
1419     // if component methods are being called from threads other than the message
1420     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
1421     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
1422 
1423     jassert (this != &child); // adding a component to itself!?
1424 
1425     if (child.parentComponent != this)
1426     {
1427         if (child.parentComponent != nullptr)
1428             child.parentComponent->removeChildComponent (&child);
1429         else
1430             child.removeFromDesktop();
1431 
1432         child.parentComponent = this;
1433 
1434         if (child.isVisible())
1435             child.repaintParent();
1436 
1437         if (! child.isAlwaysOnTop())
1438         {
1439             if (zOrder < 0 || zOrder > childComponentList.size())
1440                 zOrder = childComponentList.size();
1441 
1442             while (zOrder > 0)
1443             {
1444                 if (! childComponentList.getUnchecked (zOrder - 1)->isAlwaysOnTop())
1445                     break;
1446 
1447                 --zOrder;
1448             }
1449         }
1450 
1451         childComponentList.insert (zOrder, &child);
1452 
1453         child.internalHierarchyChanged();
1454         internalChildrenChanged();
1455     }
1456 }
1457 
addAndMakeVisible(Component & child,int zOrder)1458 void Component::addAndMakeVisible (Component& child, int zOrder)
1459 {
1460     child.setVisible (true);
1461     addChildComponent (child, zOrder);
1462 }
1463 
addChildComponent(Component * child,int zOrder)1464 void Component::addChildComponent (Component* child, int zOrder)
1465 {
1466     if (child != nullptr)
1467         addChildComponent (*child, zOrder);
1468 }
1469 
addAndMakeVisible(Component * child,int zOrder)1470 void Component::addAndMakeVisible (Component* child, int zOrder)
1471 {
1472     if (child != nullptr)
1473         addAndMakeVisible (*child, zOrder);
1474 }
1475 
addChildAndSetID(Component * child,const String & childID)1476 void Component::addChildAndSetID (Component* child, const String& childID)
1477 {
1478     if (child != nullptr)
1479     {
1480         child->setComponentID (childID);
1481         addAndMakeVisible (child);
1482     }
1483 }
1484 
removeChildComponent(Component * child)1485 void Component::removeChildComponent (Component* child)
1486 {
1487     removeChildComponent (childComponentList.indexOf (child), true, true);
1488 }
1489 
removeChildComponent(int index)1490 Component* Component::removeChildComponent (int index)
1491 {
1492     return removeChildComponent (index, true, true);
1493 }
1494 
removeChildComponent(int index,bool sendParentEvents,bool sendChildEvents)1495 Component* Component::removeChildComponent (int index, bool sendParentEvents, bool sendChildEvents)
1496 {
1497     // if component methods are being called from threads other than the message
1498     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
1499     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
1500 
1501     auto* child = childComponentList [index];
1502 
1503     if (child != nullptr)
1504     {
1505         sendParentEvents = sendParentEvents && child->isShowing();
1506 
1507         if (sendParentEvents)
1508         {
1509             sendFakeMouseMove();
1510 
1511             if (child->isVisible())
1512                 child->repaintParent();
1513         }
1514 
1515         childComponentList.remove (index);
1516         child->parentComponent = nullptr;
1517 
1518         ComponentHelpers::releaseAllCachedImageResources (*child);
1519 
1520         // (NB: there are obscure situations where child->isShowing() = false, but it still has the focus)
1521         if (currentlyFocusedComponent == child || child->isParentOf (currentlyFocusedComponent))
1522         {
1523             if (sendParentEvents)
1524             {
1525                 const WeakReference<Component> thisPointer (this);
1526 
1527                 giveAwayFocus (sendChildEvents || currentlyFocusedComponent != child);
1528 
1529                 if (thisPointer == nullptr)
1530                     return child;
1531 
1532                 grabKeyboardFocus();
1533             }
1534             else
1535             {
1536                 giveAwayFocus (sendChildEvents || currentlyFocusedComponent != child);
1537             }
1538         }
1539 
1540         if (sendChildEvents)
1541             child->internalHierarchyChanged();
1542 
1543         if (sendParentEvents)
1544             internalChildrenChanged();
1545     }
1546 
1547     return child;
1548 }
1549 
1550 //==============================================================================
removeAllChildren()1551 void Component::removeAllChildren()
1552 {
1553     while (! childComponentList.isEmpty())
1554         removeChildComponent (childComponentList.size() - 1);
1555 }
1556 
deleteAllChildren()1557 void Component::deleteAllChildren()
1558 {
1559     while (! childComponentList.isEmpty())
1560         delete (removeChildComponent (childComponentList.size() - 1));
1561 }
1562 
getNumChildComponents() const1563 int Component::getNumChildComponents() const noexcept
1564 {
1565     return childComponentList.size();
1566 }
1567 
getChildComponent(int index) const1568 Component* Component::getChildComponent (int index) const noexcept
1569 {
1570     return childComponentList[index];
1571 }
1572 
getIndexOfChildComponent(const Component * child) const1573 int Component::getIndexOfChildComponent (const Component* child) const noexcept
1574 {
1575     return childComponentList.indexOf (const_cast<Component*> (child));
1576 }
1577 
findChildWithID(StringRef targetID) const1578 Component* Component::findChildWithID (StringRef targetID) const noexcept
1579 {
1580     for (auto* c : childComponentList)
1581         if (c->componentID == targetID)
1582             return c;
1583 
1584     return nullptr;
1585 }
1586 
getTopLevelComponent() const1587 Component* Component::getTopLevelComponent() const noexcept
1588 {
1589     auto* comp = this;
1590 
1591     while (comp->parentComponent != nullptr)
1592         comp = comp->parentComponent;
1593 
1594     return const_cast<Component*> (comp);
1595 }
1596 
isParentOf(const Component * possibleChild) const1597 bool Component::isParentOf (const Component* possibleChild) const noexcept
1598 {
1599     while (possibleChild != nullptr)
1600     {
1601         possibleChild = possibleChild->parentComponent;
1602 
1603         if (possibleChild == this)
1604             return true;
1605     }
1606 
1607     return false;
1608 }
1609 
1610 //==============================================================================
parentHierarchyChanged()1611 void Component::parentHierarchyChanged() {}
childrenChanged()1612 void Component::childrenChanged() {}
1613 
internalChildrenChanged()1614 void Component::internalChildrenChanged()
1615 {
1616     if (componentListeners.isEmpty())
1617     {
1618         childrenChanged();
1619     }
1620     else
1621     {
1622         BailOutChecker checker (this);
1623 
1624         childrenChanged();
1625 
1626         if (! checker.shouldBailOut())
1627             componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentChildrenChanged (*this); });
1628     }
1629 }
1630 
internalHierarchyChanged()1631 void Component::internalHierarchyChanged()
1632 {
1633     BailOutChecker checker (this);
1634 
1635     parentHierarchyChanged();
1636 
1637     if (checker.shouldBailOut())
1638         return;
1639 
1640     componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentParentHierarchyChanged (*this); });
1641 
1642     if (checker.shouldBailOut())
1643         return;
1644 
1645     for (int i = childComponentList.size(); --i >= 0;)
1646     {
1647         childComponentList.getUnchecked (i)->internalHierarchyChanged();
1648 
1649         if (checker.shouldBailOut())
1650         {
1651             // you really shouldn't delete the parent component during a callback telling you
1652             // that it's changed..
1653             jassertfalse;
1654             return;
1655         }
1656 
1657         i = jmin (i, childComponentList.size());
1658     }
1659 }
1660 
1661 //==============================================================================
1662 #if JUCE_MODAL_LOOPS_PERMITTED
runModalLoop()1663 int Component::runModalLoop()
1664 {
1665     if (! MessageManager::getInstance()->isThisTheMessageThread())
1666     {
1667         // use a callback so this can be called from non-gui threads
1668         return (int) (pointer_sized_int) MessageManager::getInstance()
1669                                            ->callFunctionOnMessageThread (&ComponentHelpers::runModalLoopCallback, this);
1670     }
1671 
1672     if (! isCurrentlyModal (false))
1673         enterModalState (true);
1674 
1675     return ModalComponentManager::getInstance()->runEventLoopForCurrentComponent();
1676 }
1677 #endif
1678 
1679 //==============================================================================
enterModalState(bool shouldTakeKeyboardFocus,ModalComponentManager::Callback * callback,bool deleteWhenDismissed)1680 void Component::enterModalState (bool shouldTakeKeyboardFocus,
1681                                  ModalComponentManager::Callback* callback,
1682                                  bool deleteWhenDismissed)
1683 {
1684     // if component methods are being called from threads other than the message
1685     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
1686     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
1687 
1688     if (! isCurrentlyModal (false))
1689     {
1690         auto& mcm = *ModalComponentManager::getInstance();
1691         mcm.startModal (this, deleteWhenDismissed);
1692         mcm.attachCallback (this, callback);
1693 
1694         setVisible (true);
1695 
1696         if (shouldTakeKeyboardFocus)
1697             grabKeyboardFocus();
1698     }
1699     else
1700     {
1701         // Probably a bad idea to try to make a component modal twice!
1702         jassertfalse;
1703     }
1704 }
1705 
exitModalState(int returnValue)1706 void Component::exitModalState (int returnValue)
1707 {
1708     if (isCurrentlyModal (false))
1709     {
1710         if (MessageManager::getInstance()->isThisTheMessageThread())
1711         {
1712             auto& mcm = *ModalComponentManager::getInstance();
1713             mcm.endModal (this, returnValue);
1714             mcm.bringModalComponentsToFront();
1715 
1716             // If any of the mouse sources are over another Component when we exit the modal state then send a mouse enter event
1717             for (auto& ms : Desktop::getInstance().getMouseSources())
1718                 if (auto* c = ms.getComponentUnderMouse())
1719                     c->internalMouseEnter (ms, ms.getScreenPosition(), Time::getCurrentTime());
1720         }
1721         else
1722         {
1723             WeakReference<Component> target (this);
1724 
1725             MessageManager::callAsync ([=]
1726             {
1727                 if (auto* c = target.get())
1728                     c->exitModalState (returnValue);
1729             });
1730         }
1731     }
1732 }
1733 
isCurrentlyModal(bool onlyConsiderForemostModalComponent) const1734 bool Component::isCurrentlyModal (bool onlyConsiderForemostModalComponent) const noexcept
1735 {
1736     auto& mcm = *ModalComponentManager::getInstance();
1737 
1738     return onlyConsiderForemostModalComponent ? mcm.isFrontModalComponent (this)
1739                                               : mcm.isModal (this);
1740 }
1741 
isCurrentlyBlockedByAnotherModalComponent() const1742 bool Component::isCurrentlyBlockedByAnotherModalComponent() const
1743 {
1744     auto* mc = getCurrentlyModalComponent();
1745 
1746     return ! (mc == nullptr || mc == this || mc->isParentOf (this)
1747                || mc->canModalEventBeSentToComponent (this));
1748 }
1749 
getNumCurrentlyModalComponents()1750 int JUCE_CALLTYPE Component::getNumCurrentlyModalComponents() noexcept
1751 {
1752     return ModalComponentManager::getInstance()->getNumModalComponents();
1753 }
1754 
getCurrentlyModalComponent(int index)1755 Component* JUCE_CALLTYPE Component::getCurrentlyModalComponent (int index) noexcept
1756 {
1757     return ModalComponentManager::getInstance()->getModalComponent (index);
1758 }
1759 
1760 //==============================================================================
setBroughtToFrontOnMouseClick(bool shouldBeBroughtToFront)1761 void Component::setBroughtToFrontOnMouseClick (bool shouldBeBroughtToFront) noexcept
1762 {
1763     flags.bringToFrontOnClickFlag = shouldBeBroughtToFront;
1764 }
1765 
isBroughtToFrontOnMouseClick() const1766 bool Component::isBroughtToFrontOnMouseClick() const noexcept
1767 {
1768     return flags.bringToFrontOnClickFlag;
1769 }
1770 
1771 //==============================================================================
setMouseCursor(const MouseCursor & newCursor)1772 void Component::setMouseCursor (const MouseCursor& newCursor)
1773 {
1774     if (cursor != newCursor)
1775     {
1776         cursor = newCursor;
1777 
1778         if (flags.visibleFlag)
1779             updateMouseCursor();
1780     }
1781 }
1782 
getMouseCursor()1783 MouseCursor Component::getMouseCursor()
1784 {
1785     return cursor;
1786 }
1787 
updateMouseCursor() const1788 void Component::updateMouseCursor() const
1789 {
1790     Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
1791 }
1792 
1793 //==============================================================================
setRepaintsOnMouseActivity(bool shouldRepaint)1794 void Component::setRepaintsOnMouseActivity (bool shouldRepaint) noexcept
1795 {
1796     flags.repaintOnMouseActivityFlag = shouldRepaint;
1797 }
1798 
1799 //==============================================================================
getAlpha() const1800 float Component::getAlpha() const noexcept
1801 {
1802     return (255 - componentTransparency) / 255.0f;
1803 }
1804 
setAlpha(float newAlpha)1805 void Component::setAlpha (float newAlpha)
1806 {
1807     auto newIntAlpha = (uint8) (255 - jlimit (0, 255, roundToInt (newAlpha * 255.0)));
1808 
1809     if (componentTransparency != newIntAlpha)
1810     {
1811         componentTransparency = newIntAlpha;
1812         alphaChanged();
1813     }
1814 }
1815 
alphaChanged()1816 void Component::alphaChanged()
1817 {
1818     if (flags.hasHeavyweightPeerFlag)
1819     {
1820         if (auto* peer = getPeer())
1821             peer->setAlpha (getAlpha());
1822     }
1823     else
1824     {
1825         repaint();
1826     }
1827 }
1828 
1829 //==============================================================================
repaint()1830 void Component::repaint()
1831 {
1832     internalRepaintUnchecked (getLocalBounds(), true);
1833 }
1834 
repaint(int x,int y,int w,int h)1835 void Component::repaint (int x, int y, int w, int h)
1836 {
1837     internalRepaint ({ x, y, w, h });
1838 }
1839 
repaint(Rectangle<int> area)1840 void Component::repaint (Rectangle<int> area)
1841 {
1842     internalRepaint (area);
1843 }
1844 
repaintParent()1845 void Component::repaintParent()
1846 {
1847     if (parentComponent != nullptr)
1848         parentComponent->internalRepaint (ComponentHelpers::convertToParentSpace (*this, getLocalBounds()));
1849 }
1850 
internalRepaint(Rectangle<int> area)1851 void Component::internalRepaint (Rectangle<int> area)
1852 {
1853     area = area.getIntersection (getLocalBounds());
1854 
1855     if (! area.isEmpty())
1856         internalRepaintUnchecked (area, false);
1857 }
1858 
internalRepaintUnchecked(Rectangle<int> area,bool isEntireComponent)1859 void Component::internalRepaintUnchecked (Rectangle<int> area, bool isEntireComponent)
1860 {
1861     // if component methods are being called from threads other than the message
1862     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
1863     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
1864 
1865     if (flags.visibleFlag)
1866     {
1867         if (cachedImage != nullptr)
1868             if (! (isEntireComponent ? cachedImage->invalidateAll()
1869                                      : cachedImage->invalidate (area)))
1870                 return;
1871 
1872         if (area.isEmpty())
1873             return;
1874 
1875         if (flags.hasHeavyweightPeerFlag)
1876         {
1877             if (auto* peer = getPeer())
1878             {
1879                 // Tweak the scaling so that the component's integer size exactly aligns with the peer's scaled size
1880                 auto peerBounds = peer->getBounds();
1881                 auto scaled = area * Point<float> ((float) peerBounds.getWidth()  / (float) getWidth(),
1882                                                    (float) peerBounds.getHeight() / (float) getHeight());
1883 
1884                 peer->repaint (affineTransform != nullptr ? scaled.transformedBy (*affineTransform) : scaled);
1885             }
1886         }
1887         else
1888         {
1889             if (parentComponent != nullptr)
1890                 parentComponent->internalRepaint (ComponentHelpers::convertToParentSpace (*this, area));
1891         }
1892     }
1893 }
1894 
1895 //==============================================================================
paint(Graphics &)1896 void Component::paint (Graphics&)
1897 {
1898     // if your component is marked as opaque, you must implement a paint
1899     // method and ensure that its entire area is completely painted.
1900     jassert (getBounds().isEmpty() || ! isOpaque());
1901 }
1902 
paintOverChildren(Graphics &)1903 void Component::paintOverChildren (Graphics&)
1904 {
1905     // all painting is done in the subclasses
1906 }
1907 
1908 //==============================================================================
paintWithinParentContext(Graphics & g)1909 void Component::paintWithinParentContext (Graphics& g)
1910 {
1911     g.setOrigin (getPosition());
1912 
1913     if (cachedImage != nullptr)
1914         cachedImage->paint (g);
1915     else
1916         paintEntireComponent (g, false);
1917 }
1918 
paintComponentAndChildren(Graphics & g)1919 void Component::paintComponentAndChildren (Graphics& g)
1920 {
1921     auto clipBounds = g.getClipBounds();
1922 
1923     if (flags.dontClipGraphicsFlag)
1924     {
1925         paint (g);
1926     }
1927     else
1928     {
1929         Graphics::ScopedSaveState ss (g);
1930 
1931         if (! (ComponentHelpers::clipObscuredRegions (*this, g, clipBounds, {}) && g.isClipEmpty()))
1932             paint (g);
1933     }
1934 
1935     for (int i = 0; i < childComponentList.size(); ++i)
1936     {
1937         auto& child = *childComponentList.getUnchecked (i);
1938 
1939         if (child.isVisible())
1940         {
1941             if (child.affineTransform != nullptr)
1942             {
1943                 Graphics::ScopedSaveState ss (g);
1944 
1945                 g.addTransform (*child.affineTransform);
1946 
1947                 if ((child.flags.dontClipGraphicsFlag && ! g.isClipEmpty()) || g.reduceClipRegion (child.getBounds()))
1948                     child.paintWithinParentContext (g);
1949             }
1950             else if (clipBounds.intersects (child.getBounds()))
1951             {
1952                 Graphics::ScopedSaveState ss (g);
1953 
1954                 if (child.flags.dontClipGraphicsFlag)
1955                 {
1956                     child.paintWithinParentContext (g);
1957                 }
1958                 else if (g.reduceClipRegion (child.getBounds()))
1959                 {
1960                     bool nothingClipped = true;
1961 
1962                     for (int j = i + 1; j < childComponentList.size(); ++j)
1963                     {
1964                         auto& sibling = *childComponentList.getUnchecked (j);
1965 
1966                         if (sibling.flags.opaqueFlag && sibling.isVisible() && sibling.affineTransform == nullptr)
1967                         {
1968                             nothingClipped = false;
1969                             g.excludeClipRegion (sibling.getBounds());
1970                         }
1971                     }
1972 
1973                     if (nothingClipped || ! g.isClipEmpty())
1974                         child.paintWithinParentContext (g);
1975                 }
1976             }
1977         }
1978     }
1979 
1980     Graphics::ScopedSaveState ss (g);
1981     paintOverChildren (g);
1982 }
1983 
paintEntireComponent(Graphics & g,bool ignoreAlphaLevel)1984 void Component::paintEntireComponent (Graphics& g, bool ignoreAlphaLevel)
1985 {
1986     // If sizing a top-level-window and the OS paint message is delivered synchronously
1987     // before resized() is called, then we'll invoke the callback here, to make sure
1988     // the components inside have had a chance to sort their sizes out..
1989    #if JUCE_DEBUG
1990     if (! flags.isInsidePaintCall) // (avoids an assertion in plugins hosted in WaveLab)
1991    #endif
1992         sendMovedResizedMessagesIfPending();
1993 
1994    #if JUCE_DEBUG
1995     flags.isInsidePaintCall = true;
1996    #endif
1997 
1998     if (effect != nullptr)
1999     {
2000         auto scale = g.getInternalContext().getPhysicalPixelScaleFactor();
2001 
2002         auto scaledBounds = getLocalBounds() * scale;
2003 
2004         Image effectImage (flags.opaqueFlag ? Image::RGB : Image::ARGB,
2005                            scaledBounds.getWidth(), scaledBounds.getHeight(), ! flags.opaqueFlag);
2006         {
2007             Graphics g2 (effectImage);
2008             g2.addTransform (AffineTransform::scale ((float) scaledBounds.getWidth()  / (float) getWidth(),
2009                                                      (float) scaledBounds.getHeight() / (float) getHeight()));
2010             paintComponentAndChildren (g2);
2011         }
2012 
2013         Graphics::ScopedSaveState ss (g);
2014 
2015         g.addTransform (AffineTransform::scale (1.0f / scale));
2016         effect->applyEffect (effectImage, g, scale, ignoreAlphaLevel ? 1.0f : getAlpha());
2017     }
2018     else if (componentTransparency > 0 && ! ignoreAlphaLevel)
2019     {
2020         if (componentTransparency < 255)
2021         {
2022             g.beginTransparencyLayer (getAlpha());
2023             paintComponentAndChildren (g);
2024             g.endTransparencyLayer();
2025         }
2026     }
2027     else
2028     {
2029         paintComponentAndChildren (g);
2030     }
2031 
2032    #if JUCE_DEBUG
2033     flags.isInsidePaintCall = false;
2034    #endif
2035 }
2036 
setPaintingIsUnclipped(bool shouldPaintWithoutClipping)2037 void Component::setPaintingIsUnclipped (bool shouldPaintWithoutClipping) noexcept
2038 {
2039     flags.dontClipGraphicsFlag = shouldPaintWithoutClipping;
2040 }
2041 
isPaintingUnclipped() const2042 bool Component::isPaintingUnclipped() const noexcept
2043 {
2044     return flags.dontClipGraphicsFlag;
2045 }
2046 
2047 //==============================================================================
createComponentSnapshot(Rectangle<int> areaToGrab,bool clipImageToComponentBounds,float scaleFactor)2048 Image Component::createComponentSnapshot (Rectangle<int> areaToGrab,
2049                                           bool clipImageToComponentBounds, float scaleFactor)
2050 {
2051     auto r = areaToGrab;
2052 
2053     if (clipImageToComponentBounds)
2054         r = r.getIntersection (getLocalBounds());
2055 
2056     if (r.isEmpty())
2057         return {};
2058 
2059     auto w = roundToInt (scaleFactor * (float) r.getWidth());
2060     auto h = roundToInt (scaleFactor * (float) r.getHeight());
2061 
2062     Image image (flags.opaqueFlag ? Image::RGB : Image::ARGB, w, h, true);
2063 
2064     Graphics g (image);
2065 
2066     if (w != getWidth() || h != getHeight())
2067         g.addTransform (AffineTransform::scale ((float) w / (float) r.getWidth(),
2068                                                 (float) h / (float) r.getHeight()));
2069     g.setOrigin (-r.getPosition());
2070 
2071     paintEntireComponent (g, true);
2072 
2073     return image;
2074 }
2075 
setComponentEffect(ImageEffectFilter * newEffect)2076 void Component::setComponentEffect (ImageEffectFilter* newEffect)
2077 {
2078     if (effect != newEffect)
2079     {
2080         effect = newEffect;
2081         repaint();
2082     }
2083 }
2084 
2085 //==============================================================================
getLookAndFeel() const2086 LookAndFeel& Component::getLookAndFeel() const noexcept
2087 {
2088     for (auto* c = this; c != nullptr; c = c->parentComponent)
2089         if (auto lf = c->lookAndFeel.get())
2090             return *lf;
2091 
2092     return LookAndFeel::getDefaultLookAndFeel();
2093 }
2094 
setLookAndFeel(LookAndFeel * newLookAndFeel)2095 void Component::setLookAndFeel (LookAndFeel* newLookAndFeel)
2096 {
2097     if (lookAndFeel != newLookAndFeel)
2098     {
2099         lookAndFeel = newLookAndFeel;
2100         sendLookAndFeelChange();
2101     }
2102 }
2103 
lookAndFeelChanged()2104 void Component::lookAndFeelChanged() {}
colourChanged()2105 void Component::colourChanged() {}
2106 
sendLookAndFeelChange()2107 void Component::sendLookAndFeelChange()
2108 {
2109     const WeakReference<Component> safePointer (this);
2110     repaint();
2111     lookAndFeelChanged();
2112 
2113     if (safePointer != nullptr)
2114     {
2115         colourChanged();
2116 
2117         if (safePointer != nullptr)
2118         {
2119             for (int i = childComponentList.size(); --i >= 0;)
2120             {
2121                 childComponentList.getUnchecked (i)->sendLookAndFeelChange();
2122 
2123                 if (safePointer == nullptr)
2124                     return;
2125 
2126                 i = jmin (i, childComponentList.size());
2127             }
2128         }
2129     }
2130 }
2131 
findColour(int colourID,bool inheritFromParent) const2132 Colour Component::findColour (int colourID, bool inheritFromParent) const
2133 {
2134     if (auto* v = properties.getVarPointer (ComponentHelpers::getColourPropertyID (colourID)))
2135         return Colour ((uint32) static_cast<int> (*v));
2136 
2137     if (inheritFromParent && parentComponent != nullptr
2138          && (lookAndFeel == nullptr || ! lookAndFeel->isColourSpecified (colourID)))
2139         return parentComponent->findColour (colourID, true);
2140 
2141     return getLookAndFeel().findColour (colourID);
2142 }
2143 
isColourSpecified(int colourID) const2144 bool Component::isColourSpecified (int colourID) const
2145 {
2146     return properties.contains (ComponentHelpers::getColourPropertyID (colourID));
2147 }
2148 
removeColour(int colourID)2149 void Component::removeColour (int colourID)
2150 {
2151     if (properties.remove (ComponentHelpers::getColourPropertyID (colourID)))
2152         colourChanged();
2153 }
2154 
setColour(int colourID,Colour colour)2155 void Component::setColour (int colourID, Colour colour)
2156 {
2157     if (properties.set (ComponentHelpers::getColourPropertyID (colourID), (int) colour.getARGB()))
2158         colourChanged();
2159 }
2160 
copyAllExplicitColoursTo(Component & target) const2161 void Component::copyAllExplicitColoursTo (Component& target) const
2162 {
2163     bool changed = false;
2164 
2165     for (int i = properties.size(); --i >= 0;)
2166     {
2167         auto name = properties.getName(i);
2168 
2169         if (name.toString().startsWith (colourPropertyPrefix))
2170             if (target.properties.set (name, properties [name]))
2171                 changed = true;
2172     }
2173 
2174     if (changed)
2175         target.colourChanged();
2176 }
2177 
2178 //==============================================================================
Positioner(Component & c)2179 Component::Positioner::Positioner (Component& c) noexcept  : component (c)
2180 {
2181 }
2182 
getPositioner() const2183 Component::Positioner* Component::getPositioner() const noexcept
2184 {
2185     return positioner.get();
2186 }
2187 
setPositioner(Positioner * newPositioner)2188 void Component::setPositioner (Positioner* newPositioner)
2189 {
2190     // You can only assign a positioner to the component that it was created for!
2191     jassert (newPositioner == nullptr || this == &(newPositioner->getComponent()));
2192     positioner.reset (newPositioner);
2193 }
2194 
2195 //==============================================================================
getLocalBounds() const2196 Rectangle<int> Component::getLocalBounds() const noexcept
2197 {
2198     return boundsRelativeToParent.withZeroOrigin();
2199 }
2200 
getBoundsInParent() const2201 Rectangle<int> Component::getBoundsInParent() const noexcept
2202 {
2203     return affineTransform == nullptr ? boundsRelativeToParent
2204                                       : boundsRelativeToParent.transformedBy (*affineTransform);
2205 }
2206 
2207 //==============================================================================
mouseEnter(const MouseEvent &)2208 void Component::mouseEnter (const MouseEvent&)          {}
mouseExit(const MouseEvent &)2209 void Component::mouseExit  (const MouseEvent&)          {}
mouseDown(const MouseEvent &)2210 void Component::mouseDown  (const MouseEvent&)          {}
mouseUp(const MouseEvent &)2211 void Component::mouseUp    (const MouseEvent&)          {}
mouseDrag(const MouseEvent &)2212 void Component::mouseDrag  (const MouseEvent&)          {}
mouseMove(const MouseEvent &)2213 void Component::mouseMove  (const MouseEvent&)          {}
mouseDoubleClick(const MouseEvent &)2214 void Component::mouseDoubleClick (const MouseEvent&)    {}
2215 
mouseWheelMove(const MouseEvent & e,const MouseWheelDetails & wheel)2216 void Component::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel)
2217 {
2218     // the base class just passes this event up to its parent..
2219     if (parentComponent != nullptr)
2220         parentComponent->mouseWheelMove (e.getEventRelativeTo (parentComponent), wheel);
2221 }
2222 
mouseMagnify(const MouseEvent & e,float magnifyAmount)2223 void Component::mouseMagnify (const MouseEvent& e, float magnifyAmount)
2224 {
2225     // the base class just passes this event up to its parent..
2226     if (parentComponent != nullptr)
2227         parentComponent->mouseMagnify (e.getEventRelativeTo (parentComponent), magnifyAmount);
2228 }
2229 
2230 //==============================================================================
resized()2231 void Component::resized()                       {}
moved()2232 void Component::moved()                         {}
childBoundsChanged(Component *)2233 void Component::childBoundsChanged (Component*) {}
parentSizeChanged()2234 void Component::parentSizeChanged()             {}
2235 
addComponentListener(ComponentListener * newListener)2236 void Component::addComponentListener (ComponentListener* newListener)
2237 {
2238     // if component methods are being called from threads other than the message
2239     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
2240    #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS
2241     if (getParentComponent() != nullptr)
2242     {
2243         JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
2244     }
2245    #endif
2246 
2247     componentListeners.add (newListener);
2248 }
2249 
removeComponentListener(ComponentListener * listenerToRemove)2250 void Component::removeComponentListener (ComponentListener* listenerToRemove)
2251 {
2252     componentListeners.remove (listenerToRemove);
2253 }
2254 
2255 //==============================================================================
inputAttemptWhenModal()2256 void Component::inputAttemptWhenModal()
2257 {
2258     ModalComponentManager::getInstance()->bringModalComponentsToFront();
2259     getLookAndFeel().playAlertSound();
2260 }
2261 
canModalEventBeSentToComponent(const Component *)2262 bool Component::canModalEventBeSentToComponent (const Component*)
2263 {
2264     return false;
2265 }
2266 
internalModalInputAttempt()2267 void Component::internalModalInputAttempt()
2268 {
2269     if (auto* current = getCurrentlyModalComponent())
2270         current->inputAttemptWhenModal();
2271 }
2272 
2273 //==============================================================================
postCommandMessage(int commandID)2274 void Component::postCommandMessage (int commandID)
2275 {
2276     WeakReference<Component> target (this);
2277 
2278     MessageManager::callAsync ([=]
2279     {
2280         if (auto* c = target.get())
2281             c->handleCommandMessage (commandID);
2282     });
2283 }
2284 
handleCommandMessage(int)2285 void Component::handleCommandMessage (int)
2286 {
2287     // used by subclasses
2288 }
2289 
2290 //==============================================================================
addMouseListener(MouseListener * newListener,bool wantsEventsForAllNestedChildComponents)2291 void Component::addMouseListener (MouseListener* newListener,
2292                                   bool wantsEventsForAllNestedChildComponents)
2293 {
2294     // if component methods are being called from threads other than the message
2295     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
2296     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
2297 
2298     // If you register a component as a mouselistener for itself, it'll receive all the events
2299     // twice - once via the direct callback that all components get anyway, and then again as a listener!
2300     jassert ((newListener != this) || wantsEventsForAllNestedChildComponents);
2301 
2302     if (mouseListeners == nullptr)
2303         mouseListeners.reset (new MouseListenerList());
2304 
2305     mouseListeners->addListener (newListener, wantsEventsForAllNestedChildComponents);
2306 }
2307 
removeMouseListener(MouseListener * listenerToRemove)2308 void Component::removeMouseListener (MouseListener* listenerToRemove)
2309 {
2310     // if component methods are being called from threads other than the message
2311     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
2312     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
2313 
2314     if (mouseListeners != nullptr)
2315         mouseListeners->removeListener (listenerToRemove);
2316 }
2317 
2318 //==============================================================================
internalMouseEnter(MouseInputSource source,Point<float> relativePos,Time time)2319 void Component::internalMouseEnter (MouseInputSource source, Point<float> relativePos, Time time)
2320 {
2321     if (isCurrentlyBlockedByAnotherModalComponent())
2322     {
2323         // if something else is modal, always just show a normal mouse cursor
2324         source.showMouseCursor (MouseCursor::NormalCursor);
2325         return;
2326     }
2327 
2328     if (flags.repaintOnMouseActivityFlag)
2329         repaint();
2330 
2331     BailOutChecker checker (this);
2332 
2333     const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
2334                          MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
2335                          MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
2336                          this, this, time, relativePos, time, 0, false);
2337     mouseEnter (me);
2338 
2339     if (checker.shouldBailOut())
2340         return;
2341 
2342     Desktop::getInstance().getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseEnter (me); });
2343 
2344     MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseEnter, me);
2345 }
2346 
internalMouseExit(MouseInputSource source,Point<float> relativePos,Time time)2347 void Component::internalMouseExit (MouseInputSource source, Point<float> relativePos, Time time)
2348 {
2349     if (isCurrentlyBlockedByAnotherModalComponent())
2350     {
2351         // if something else is modal, always just show a normal mouse cursor
2352         source.showMouseCursor (MouseCursor::NormalCursor);
2353         return;
2354     }
2355 
2356     if (flags.repaintOnMouseActivityFlag)
2357         repaint();
2358 
2359     BailOutChecker checker (this);
2360 
2361     const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
2362                          MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
2363                          MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
2364                          this, this, time, relativePos, time, 0, false);
2365 
2366     mouseExit (me);
2367 
2368     if (checker.shouldBailOut())
2369         return;
2370 
2371     Desktop::getInstance().getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseExit (me); });
2372 
2373     MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseExit, me);
2374 }
2375 
internalMouseDown(MouseInputSource source,Point<float> relativePos,Time time,float pressure,float orientation,float rotation,float tiltX,float tiltY)2376 void Component::internalMouseDown (MouseInputSource source, Point<float> relativePos, Time time,
2377                                    float pressure, float orientation, float rotation, float tiltX, float tiltY)
2378 {
2379     auto& desktop = Desktop::getInstance();
2380     BailOutChecker checker (this);
2381 
2382     if (isCurrentlyBlockedByAnotherModalComponent())
2383     {
2384         flags.mouseDownWasBlocked = true;
2385         internalModalInputAttempt();
2386 
2387         if (checker.shouldBailOut())
2388             return;
2389 
2390         // If processing the input attempt has exited the modal loop, we'll allow the event
2391         // to be delivered..
2392         if (isCurrentlyBlockedByAnotherModalComponent())
2393         {
2394             // allow blocked mouse-events to go to global listeners..
2395             const MouseEvent me (source, relativePos, source.getCurrentModifiers(), pressure,
2396                                  orientation, rotation, tiltX, tiltY, this, this, time, relativePos,
2397                                  time, source.getNumberOfMultipleClicks(), false);
2398 
2399             desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDown (me); });
2400             return;
2401         }
2402     }
2403 
2404     flags.mouseDownWasBlocked = false;
2405 
2406     for (auto* c = this; c != nullptr; c = c->parentComponent)
2407     {
2408         if (c->isBroughtToFrontOnMouseClick())
2409         {
2410             c->toFront (true);
2411 
2412             if (checker.shouldBailOut())
2413                 return;
2414         }
2415     }
2416 
2417     if (! flags.dontFocusOnMouseClickFlag)
2418     {
2419         grabFocusInternal (focusChangedByMouseClick, true);
2420 
2421         if (checker.shouldBailOut())
2422             return;
2423     }
2424 
2425     if (flags.repaintOnMouseActivityFlag)
2426         repaint();
2427 
2428     const MouseEvent me (source, relativePos, source.getCurrentModifiers(), pressure,
2429                          orientation, rotation, tiltX, tiltY, this, this, time, relativePos,
2430                          time, source.getNumberOfMultipleClicks(), false);
2431     mouseDown (me);
2432 
2433     if (checker.shouldBailOut())
2434         return;
2435 
2436     desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDown (me); });
2437 
2438     MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseDown, me);
2439 }
2440 
internalMouseUp(MouseInputSource source,Point<float> relativePos,Time time,const ModifierKeys oldModifiers,float pressure,float orientation,float rotation,float tiltX,float tiltY)2441 void Component::internalMouseUp (MouseInputSource source, Point<float> relativePos, Time time,
2442                                  const ModifierKeys oldModifiers, float pressure, float orientation, float rotation, float tiltX, float tiltY)
2443 {
2444     if (flags.mouseDownWasBlocked && isCurrentlyBlockedByAnotherModalComponent())
2445         return;
2446 
2447     BailOutChecker checker (this);
2448 
2449     if (flags.repaintOnMouseActivityFlag)
2450         repaint();
2451 
2452     const MouseEvent me (source, relativePos, oldModifiers, pressure, orientation,
2453                          rotation, tiltX, tiltY, this, this, time,
2454                          getLocalPoint (nullptr, source.getLastMouseDownPosition()),
2455                          source.getLastMouseDownTime(),
2456                          source.getNumberOfMultipleClicks(),
2457                          source.isLongPressOrDrag());
2458     mouseUp (me);
2459 
2460     if (checker.shouldBailOut())
2461         return;
2462 
2463     auto& desktop = Desktop::getInstance();
2464     desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseUp (me); });
2465 
2466     MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseUp, me);
2467 
2468     if (checker.shouldBailOut())
2469         return;
2470 
2471     // check for double-click
2472     if (me.getNumberOfClicks() >= 2)
2473     {
2474         mouseDoubleClick (me);
2475 
2476         if (checker.shouldBailOut())
2477             return;
2478 
2479         desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseDoubleClick (me); });
2480         MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseDoubleClick, me);
2481     }
2482 }
2483 
internalMouseDrag(MouseInputSource source,Point<float> relativePos,Time time,float pressure,float orientation,float rotation,float tiltX,float tiltY)2484 void Component::internalMouseDrag (MouseInputSource source, Point<float> relativePos, Time time,
2485                                    float pressure, float orientation, float rotation, float tiltX, float tiltY)
2486 {
2487     if (! isCurrentlyBlockedByAnotherModalComponent())
2488     {
2489         BailOutChecker checker (this);
2490 
2491         const MouseEvent me (source, relativePos, source.getCurrentModifiers(),
2492                              pressure, orientation, rotation, tiltX, tiltY, this, this, time,
2493                              getLocalPoint (nullptr, source.getLastMouseDownPosition()),
2494                              source.getLastMouseDownTime(),
2495                              source.getNumberOfMultipleClicks(),
2496                              source.isLongPressOrDrag());
2497         mouseDrag (me);
2498 
2499         if (checker.shouldBailOut())
2500             return;
2501 
2502         Desktop::getInstance().getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDrag (me); });
2503 
2504         MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseDrag, me);
2505     }
2506 }
2507 
internalMouseMove(MouseInputSource source,Point<float> relativePos,Time time)2508 void Component::internalMouseMove (MouseInputSource source, Point<float> relativePos, Time time)
2509 {
2510     auto& desktop = Desktop::getInstance();
2511 
2512     if (isCurrentlyBlockedByAnotherModalComponent())
2513     {
2514         // allow blocked mouse-events to go to global listeners..
2515         desktop.sendMouseMove();
2516     }
2517     else
2518     {
2519         BailOutChecker checker (this);
2520 
2521         const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
2522                              MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
2523                              MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
2524                              this, this, time, relativePos, time, 0, false);
2525         mouseMove (me);
2526 
2527         if (checker.shouldBailOut())
2528             return;
2529 
2530         desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseMove (me); });
2531 
2532         MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseMove, me);
2533     }
2534 }
2535 
internalMouseWheel(MouseInputSource source,Point<float> relativePos,Time time,const MouseWheelDetails & wheel)2536 void Component::internalMouseWheel (MouseInputSource source, Point<float> relativePos,
2537                                     Time time, const MouseWheelDetails& wheel)
2538 {
2539     auto& desktop = Desktop::getInstance();
2540     BailOutChecker checker (this);
2541 
2542     const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
2543                          MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
2544                          MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
2545                          this, this, time, relativePos, time, 0, false);
2546 
2547     if (isCurrentlyBlockedByAnotherModalComponent())
2548     {
2549         // allow blocked mouse-events to go to global listeners..
2550         desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseWheelMove (me, wheel); });
2551     }
2552     else
2553     {
2554         mouseWheelMove (me, wheel);
2555 
2556         if (checker.shouldBailOut())
2557             return;
2558 
2559         desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseWheelMove (me, wheel); });
2560 
2561         if (! checker.shouldBailOut())
2562             MouseListenerList::template sendMouseEvent<const MouseEvent&, const MouseWheelDetails&> (*this, checker, &MouseListener::mouseWheelMove, me, wheel);
2563     }
2564 }
2565 
internalMagnifyGesture(MouseInputSource source,Point<float> relativePos,Time time,float amount)2566 void Component::internalMagnifyGesture (MouseInputSource source, Point<float> relativePos,
2567                                         Time time, float amount)
2568 {
2569     auto& desktop = Desktop::getInstance();
2570     BailOutChecker checker (this);
2571 
2572     const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
2573                          MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
2574                          MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
2575                          this, this, time, relativePos, time, 0, false);
2576 
2577     if (isCurrentlyBlockedByAnotherModalComponent())
2578     {
2579         // allow blocked mouse-events to go to global listeners..
2580         desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseMagnify (me, amount); });
2581     }
2582     else
2583     {
2584         mouseMagnify (me, amount);
2585 
2586         if (checker.shouldBailOut())
2587             return;
2588 
2589         desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseMagnify (me, amount); });
2590 
2591         if (! checker.shouldBailOut())
2592             MouseListenerList::template sendMouseEvent<const MouseEvent&, float> (*this, checker, &MouseListener::mouseMagnify, me, amount);
2593     }
2594 }
2595 
sendFakeMouseMove() const2596 void Component::sendFakeMouseMove() const
2597 {
2598     if (flags.ignoresMouseClicksFlag && ! flags.allowChildMouseClicksFlag)
2599         return;
2600 
2601     auto mainMouse = Desktop::getInstance().getMainMouseSource();
2602 
2603     if (! mainMouse.isDragging())
2604         mainMouse.triggerFakeMove();
2605 }
2606 
beginDragAutoRepeat(int interval)2607 void JUCE_CALLTYPE Component::beginDragAutoRepeat (int interval)
2608 {
2609     Desktop::getInstance().beginDragAutoRepeat (interval);
2610 }
2611 
2612 //==============================================================================
broughtToFront()2613 void Component::broughtToFront()
2614 {
2615 }
2616 
internalBroughtToFront()2617 void Component::internalBroughtToFront()
2618 {
2619     if (flags.hasHeavyweightPeerFlag)
2620         Desktop::getInstance().componentBroughtToFront (this);
2621 
2622     BailOutChecker checker (this);
2623     broughtToFront();
2624 
2625     if (checker.shouldBailOut())
2626         return;
2627 
2628     componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentBroughtToFront (*this); });
2629 
2630     if (checker.shouldBailOut())
2631         return;
2632 
2633     // When brought to the front and there's a modal component blocking this one,
2634     // we need to bring the modal one to the front instead..
2635     if (auto* cm = getCurrentlyModalComponent())
2636         if (cm->getTopLevelComponent() != getTopLevelComponent())
2637             ModalComponentManager::getInstance()->bringModalComponentsToFront (false); // very important that this is false, otherwise in Windows,
2638                                                                                        // non-front components can't get focus when another modal comp is
2639                                                                                        // active, and therefore can't receive mouse-clicks
2640 }
2641 
2642 //==============================================================================
focusGained(FocusChangeType)2643 void Component::focusGained (FocusChangeType)   {}
focusLost(FocusChangeType)2644 void Component::focusLost (FocusChangeType)     {}
focusOfChildComponentChanged(FocusChangeType)2645 void Component::focusOfChildComponentChanged (FocusChangeType) {}
2646 
internalFocusGain(FocusChangeType cause)2647 void Component::internalFocusGain (FocusChangeType cause)
2648 {
2649     internalFocusGain (cause, WeakReference<Component> (this));
2650 }
2651 
internalFocusGain(FocusChangeType cause,const WeakReference<Component> & safePointer)2652 void Component::internalFocusGain (FocusChangeType cause, const WeakReference<Component>& safePointer)
2653 {
2654     focusGained (cause);
2655 
2656     if (safePointer != nullptr)
2657         internalChildFocusChange (cause, safePointer);
2658 }
2659 
internalFocusLoss(FocusChangeType cause)2660 void Component::internalFocusLoss (FocusChangeType cause)
2661 {
2662     const WeakReference<Component> safePointer (this);
2663 
2664     focusLost (cause);
2665 
2666     if (safePointer != nullptr)
2667         internalChildFocusChange (cause, safePointer);
2668 }
2669 
internalChildFocusChange(FocusChangeType cause,const WeakReference<Component> & safePointer)2670 void Component::internalChildFocusChange (FocusChangeType cause, const WeakReference<Component>& safePointer)
2671 {
2672     const bool childIsNowFocused = hasKeyboardFocus (true);
2673 
2674     if (flags.childCompFocusedFlag != childIsNowFocused)
2675     {
2676         flags.childCompFocusedFlag = childIsNowFocused;
2677 
2678         focusOfChildComponentChanged (cause);
2679 
2680         if (safePointer == nullptr)
2681             return;
2682     }
2683 
2684     if (parentComponent != nullptr)
2685         parentComponent->internalChildFocusChange (cause, WeakReference<Component> (parentComponent));
2686 }
2687 
setWantsKeyboardFocus(bool wantsFocus)2688 void Component::setWantsKeyboardFocus (bool wantsFocus) noexcept
2689 {
2690     flags.wantsFocusFlag = wantsFocus;
2691 }
2692 
setMouseClickGrabsKeyboardFocus(bool shouldGrabFocus)2693 void Component::setMouseClickGrabsKeyboardFocus (bool shouldGrabFocus)
2694 {
2695     flags.dontFocusOnMouseClickFlag = ! shouldGrabFocus;
2696 }
2697 
getMouseClickGrabsKeyboardFocus() const2698 bool Component::getMouseClickGrabsKeyboardFocus() const noexcept
2699 {
2700     return ! flags.dontFocusOnMouseClickFlag;
2701 }
2702 
getWantsKeyboardFocus() const2703 bool Component::getWantsKeyboardFocus() const noexcept
2704 {
2705     return flags.wantsFocusFlag && ! flags.isDisabledFlag;
2706 }
2707 
setFocusContainer(bool shouldBeFocusContainer)2708 void Component::setFocusContainer (bool shouldBeFocusContainer) noexcept
2709 {
2710     flags.isFocusContainerFlag = shouldBeFocusContainer;
2711 }
2712 
isFocusContainer() const2713 bool Component::isFocusContainer() const noexcept
2714 {
2715     return flags.isFocusContainerFlag;
2716 }
2717 
2718 static const Identifier juce_explicitFocusOrderId ("_jexfo");
2719 
getExplicitFocusOrder() const2720 int Component::getExplicitFocusOrder() const
2721 {
2722     return properties [juce_explicitFocusOrderId];
2723 }
2724 
setExplicitFocusOrder(int newFocusOrderIndex)2725 void Component::setExplicitFocusOrder (int newFocusOrderIndex)
2726 {
2727     properties.set (juce_explicitFocusOrderId, newFocusOrderIndex);
2728 }
2729 
createFocusTraverser()2730 KeyboardFocusTraverser* Component::createFocusTraverser()
2731 {
2732     if (flags.isFocusContainerFlag || parentComponent == nullptr)
2733         return new KeyboardFocusTraverser();
2734 
2735     return parentComponent->createFocusTraverser();
2736 }
2737 
takeKeyboardFocus(FocusChangeType cause)2738 void Component::takeKeyboardFocus (FocusChangeType cause)
2739 {
2740     // give the focus to this component
2741     if (currentlyFocusedComponent != this)
2742     {
2743         // get the focus onto our desktop window
2744         if (auto* peer = getPeer())
2745         {
2746             const WeakReference<Component> safePointer (this);
2747             peer->grabFocus();
2748 
2749             if (peer->isFocused() && currentlyFocusedComponent != this)
2750             {
2751                 WeakReference<Component> componentLosingFocus (currentlyFocusedComponent);
2752                 currentlyFocusedComponent = this;
2753 
2754                 Desktop::getInstance().triggerFocusCallback();
2755 
2756                 // call this after setting currentlyFocusedComponent so that the one that's
2757                 // losing it has a chance to see where focus is going
2758                 if (componentLosingFocus != nullptr)
2759                     componentLosingFocus->internalFocusLoss (cause);
2760 
2761                 if (currentlyFocusedComponent == this)
2762                     internalFocusGain (cause, safePointer);
2763             }
2764         }
2765     }
2766 }
2767 
grabFocusInternal(FocusChangeType cause,bool canTryParent)2768 void Component::grabFocusInternal (FocusChangeType cause, bool canTryParent)
2769 {
2770     if (isShowing())
2771     {
2772         if (flags.wantsFocusFlag && (isEnabled() || parentComponent == nullptr))
2773         {
2774             takeKeyboardFocus (cause);
2775         }
2776         else
2777         {
2778             if (isParentOf (currentlyFocusedComponent)
2779                  && currentlyFocusedComponent->isShowing())
2780             {
2781                 // do nothing if the focused component is actually a child of ours..
2782             }
2783             else
2784             {
2785                 // find the default child component..
2786                 std::unique_ptr<KeyboardFocusTraverser> traverser (createFocusTraverser());
2787 
2788                 if (traverser != nullptr)
2789                 {
2790                     auto* defaultComp = traverser->getDefaultComponent (this);
2791                     traverser.reset();
2792 
2793                     if (defaultComp != nullptr)
2794                     {
2795                         defaultComp->grabFocusInternal (cause, false);
2796                         return;
2797                     }
2798                 }
2799 
2800                 if (canTryParent && parentComponent != nullptr)
2801                 {
2802                     // if no children want it and we're allowed to try our parent comp,
2803                     // then pass up to parent, which will try our siblings.
2804                     parentComponent->grabFocusInternal (cause, true);
2805                 }
2806             }
2807         }
2808     }
2809 }
2810 
grabKeyboardFocus()2811 void Component::grabKeyboardFocus()
2812 {
2813     // if component methods are being called from threads other than the message
2814     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
2815     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
2816 
2817     grabFocusInternal (focusChangedDirectly, true);
2818 
2819     // A component can only be focused when it's actually on the screen!
2820     // If this fails then you're probably trying to grab the focus before you've
2821     // added the component to a parent or made it visible. Or maybe one of its parent
2822     // components isn't yet visible.
2823     jassert (isShowing() || isOnDesktop());
2824 }
2825 
moveKeyboardFocusToSibling(bool moveToNext)2826 void Component::moveKeyboardFocusToSibling (bool moveToNext)
2827 {
2828     // if component methods are being called from threads other than the message
2829     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
2830     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
2831 
2832     if (parentComponent != nullptr)
2833     {
2834         std::unique_ptr<KeyboardFocusTraverser> traverser (createFocusTraverser());
2835 
2836         if (traverser != nullptr)
2837         {
2838             auto* nextComp = moveToNext ? traverser->getNextComponent (this)
2839                                         : traverser->getPreviousComponent (this);
2840             traverser.reset();
2841 
2842             if (nextComp != nullptr)
2843             {
2844                 if (nextComp->isCurrentlyBlockedByAnotherModalComponent())
2845                 {
2846                     const WeakReference<Component> nextCompPointer (nextComp);
2847                     internalModalInputAttempt();
2848 
2849                     if (nextCompPointer == nullptr || nextComp->isCurrentlyBlockedByAnotherModalComponent())
2850                         return;
2851                 }
2852 
2853                 nextComp->grabFocusInternal (focusChangedByTabKey, true);
2854                 return;
2855             }
2856         }
2857 
2858         parentComponent->moveKeyboardFocusToSibling (moveToNext);
2859     }
2860 }
2861 
hasKeyboardFocus(bool trueIfChildIsFocused) const2862 bool Component::hasKeyboardFocus (bool trueIfChildIsFocused) const
2863 {
2864     return (currentlyFocusedComponent == this)
2865             || (trueIfChildIsFocused && isParentOf (currentlyFocusedComponent));
2866 }
2867 
getCurrentlyFocusedComponent()2868 Component* JUCE_CALLTYPE Component::getCurrentlyFocusedComponent() noexcept
2869 {
2870     return currentlyFocusedComponent;
2871 }
2872 
unfocusAllComponents()2873 void JUCE_CALLTYPE Component::unfocusAllComponents()
2874 {
2875     if (auto* c = getCurrentlyFocusedComponent())
2876         c->giveAwayFocus (true);
2877 }
2878 
giveAwayFocus(bool sendFocusLossEvent)2879 void Component::giveAwayFocus (bool sendFocusLossEvent)
2880 {
2881     auto* componentLosingFocus = currentlyFocusedComponent;
2882     currentlyFocusedComponent = nullptr;
2883 
2884     if (sendFocusLossEvent && componentLosingFocus != nullptr)
2885         componentLosingFocus->internalFocusLoss (focusChangedDirectly);
2886 
2887     Desktop::getInstance().triggerFocusCallback();
2888 }
2889 
2890 //==============================================================================
isEnabled() const2891 bool Component::isEnabled() const noexcept
2892 {
2893     return (! flags.isDisabledFlag)
2894             && (parentComponent == nullptr || parentComponent->isEnabled());
2895 }
2896 
setEnabled(bool shouldBeEnabled)2897 void Component::setEnabled (bool shouldBeEnabled)
2898 {
2899     if (flags.isDisabledFlag == shouldBeEnabled)
2900     {
2901         flags.isDisabledFlag = ! shouldBeEnabled;
2902 
2903         // if any parent components are disabled, setting our flag won't make a difference,
2904         // so no need to send a change message
2905         if (parentComponent == nullptr || parentComponent->isEnabled())
2906             sendEnablementChangeMessage();
2907 
2908         BailOutChecker checker (this);
2909         componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentEnablementChanged (*this); });
2910     }
2911 }
2912 
enablementChanged()2913 void Component::enablementChanged() {}
2914 
sendEnablementChangeMessage()2915 void Component::sendEnablementChangeMessage()
2916 {
2917     const WeakReference<Component> safePointer (this);
2918 
2919     enablementChanged();
2920 
2921     if (safePointer == nullptr)
2922         return;
2923 
2924     for (int i = getNumChildComponents(); --i >= 0;)
2925     {
2926         if (auto* c = getChildComponent (i))
2927         {
2928             c->sendEnablementChangeMessage();
2929 
2930             if (safePointer == nullptr)
2931                 return;
2932         }
2933     }
2934 }
2935 
2936 //==============================================================================
isMouseOver(bool includeChildren) const2937 bool Component::isMouseOver (bool includeChildren) const
2938 {
2939     for (auto& ms : Desktop::getInstance().getMouseSources())
2940     {
2941         auto* c = ms.getComponentUnderMouse();
2942 
2943         if (c == this || (includeChildren && isParentOf (c)))
2944             if (ms.isDragging() || ! (ms.isTouch() || ms.isPen()))
2945                 if (c->reallyContains (c->getLocalPoint (nullptr, ms.getScreenPosition()).roundToInt(), false))
2946                     return true;
2947     }
2948 
2949     return false;
2950 }
2951 
isMouseButtonDown(bool includeChildren) const2952 bool Component::isMouseButtonDown (bool includeChildren) const
2953 {
2954     for (auto& ms : Desktop::getInstance().getMouseSources())
2955     {
2956         auto* c = ms.getComponentUnderMouse();
2957 
2958         if (c == this || (includeChildren && isParentOf (c)))
2959             if (ms.isDragging())
2960                 return true;
2961     }
2962 
2963     return false;
2964 }
2965 
isMouseOverOrDragging(bool includeChildren) const2966 bool Component::isMouseOverOrDragging (bool includeChildren) const
2967 {
2968     for (auto& ms : Desktop::getInstance().getMouseSources())
2969     {
2970         auto* c = ms.getComponentUnderMouse();
2971 
2972         if (c == this || (includeChildren && isParentOf (c)))
2973             if (ms.isDragging() || ! ms.isTouch())
2974                 return true;
2975     }
2976 
2977     return false;
2978 }
2979 
isMouseButtonDownAnywhere()2980 bool JUCE_CALLTYPE Component::isMouseButtonDownAnywhere() noexcept
2981 {
2982     return ModifierKeys::currentModifiers.isAnyMouseButtonDown();
2983 }
2984 
getMouseXYRelative() const2985 Point<int> Component::getMouseXYRelative() const
2986 {
2987     return getLocalPoint (nullptr, Desktop::getMousePosition());
2988 }
2989 
2990 //==============================================================================
addKeyListener(KeyListener * newListener)2991 void Component::addKeyListener (KeyListener* newListener)
2992 {
2993     if (keyListeners == nullptr)
2994         keyListeners.reset (new Array<KeyListener*>());
2995 
2996     keyListeners->addIfNotAlreadyThere (newListener);
2997 }
2998 
removeKeyListener(KeyListener * listenerToRemove)2999 void Component::removeKeyListener (KeyListener* listenerToRemove)
3000 {
3001     if (keyListeners != nullptr)
3002         keyListeners->removeFirstMatchingValue (listenerToRemove);
3003 }
3004 
keyPressed(const KeyPress &)3005 bool Component::keyPressed (const KeyPress&)            { return false; }
keyStateChanged(bool)3006 bool Component::keyStateChanged (bool /*isKeyDown*/)    { return false; }
3007 
modifierKeysChanged(const ModifierKeys & modifiers)3008 void Component::modifierKeysChanged (const ModifierKeys& modifiers)
3009 {
3010     if (parentComponent != nullptr)
3011         parentComponent->modifierKeysChanged (modifiers);
3012 }
3013 
internalModifierKeysChanged()3014 void Component::internalModifierKeysChanged()
3015 {
3016     sendFakeMouseMove();
3017     modifierKeysChanged (ModifierKeys::currentModifiers);
3018 }
3019 
3020 //==============================================================================
BailOutChecker(Component * component)3021 Component::BailOutChecker::BailOutChecker (Component* component)
3022     : safePointer (component)
3023 {
3024     jassert (component != nullptr);
3025 }
3026 
shouldBailOut() const3027 bool Component::BailOutChecker::shouldBailOut() const noexcept
3028 {
3029     return safePointer == nullptr;
3030 }
3031 
3032 } // namespace juce
3033