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().getMainDisplay().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 (currentlyFocusedComponent == this || isParentOf (currentlyFocusedComponent))
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 (currentlyFocusedComponent == this || isParentOf (currentlyFocusedComponent))
550             {
551                 if (parentComponent != nullptr)
552                     parentComponent->grabKeyboardFocus();
553                 else
554                     giveAwayFocus (true);
555             }
556         }
557 
558         if (safePointer != nullptr)
559         {
560             sendVisibilityChangeMessage();
561 
562             if (safePointer != nullptr && flags.hasHeavyweightPeerFlag)
563             {
564                 if (auto* peer = getPeer())
565                 {
566                     peer->setVisible (shouldBeVisible);
567                     internalHierarchyChanged();
568                 }
569             }
570         }
571     }
572 }
573 
visibilityChanged()574 void Component::visibilityChanged() {}
575 
sendVisibilityChangeMessage()576 void Component::sendVisibilityChangeMessage()
577 {
578     BailOutChecker checker (this);
579     visibilityChanged();
580 
581     if (! checker.shouldBailOut())
582         componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentVisibilityChanged (*this); });
583 }
584 
isShowing() const585 bool Component::isShowing() const
586 {
587     if (! flags.visibleFlag)
588         return false;
589 
590     if (parentComponent != nullptr)
591         return parentComponent->isShowing();
592 
593     if (auto* peer = getPeer())
594         return ! peer->isMinimised();
595 
596     return false;
597 }
598 
599 //==============================================================================
getWindowHandle() const600 void* Component::getWindowHandle() const
601 {
602     if (auto* peer = getPeer())
603         return peer->getNativeHandle();
604 
605     return nullptr;
606 }
607 
608 //==============================================================================
addToDesktop(int styleWanted,void * nativeWindowToAttachTo)609 void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo)
610 {
611     // if component methods are being called from threads other than the message
612     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
613     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
614 
615     if (isOpaque())
616         styleWanted &= ~ComponentPeer::windowIsSemiTransparent;
617     else
618         styleWanted |= ComponentPeer::windowIsSemiTransparent;
619 
620     // don't use getPeer(), so that we only get the peer that's specifically
621     // for this comp, and not for one of its parents.
622     auto* peer = ComponentPeer::getPeerFor (this);
623 
624     if (peer == nullptr || styleWanted != peer->getStyleFlags())
625     {
626         const WeakReference<Component> safePointer (this);
627 
628        #if JUCE_LINUX
629         // it's wise to give the component a non-zero size before
630         // putting it on the desktop, as X windows get confused by this, and
631         // a (1, 1) minimum size is enforced here.
632         setSize (jmax (1, getWidth()),
633                  jmax (1, getHeight()));
634        #endif
635 
636         auto topLeft = getScreenPosition();
637 
638         bool wasFullscreen = false;
639         bool wasMinimised = false;
640         ComponentBoundsConstrainer* currentConstrainer = nullptr;
641         Rectangle<int> oldNonFullScreenBounds;
642         int oldRenderingEngine = -1;
643 
644         if (peer != nullptr)
645         {
646             std::unique_ptr<ComponentPeer> oldPeerToDelete (peer);
647 
648             wasFullscreen = peer->isFullScreen();
649             wasMinimised = peer->isMinimised();
650             currentConstrainer = peer->getConstrainer();
651             oldNonFullScreenBounds = peer->getNonFullScreenBounds();
652             oldRenderingEngine = peer->getCurrentRenderingEngine();
653 
654             flags.hasHeavyweightPeerFlag = false;
655             Desktop::getInstance().removeDesktopComponent (this);
656             internalHierarchyChanged(); // give comps a chance to react to the peer change before the old peer is deleted.
657 
658             if (safePointer == nullptr)
659                 return;
660 
661             setTopLeftPosition (topLeft);
662         }
663 
664         if (parentComponent != nullptr)
665             parentComponent->removeChildComponent (this);
666 
667         if (safePointer != nullptr)
668         {
669             flags.hasHeavyweightPeerFlag = true;
670 
671             peer = createNewPeer (styleWanted, nativeWindowToAttachTo);
672 
673             Desktop::getInstance().addDesktopComponent (this);
674 
675             boundsRelativeToParent.setPosition (topLeft);
676             peer->updateBounds();
677 
678             if (oldRenderingEngine >= 0)
679                 peer->setCurrentRenderingEngine (oldRenderingEngine);
680 
681             peer->setVisible (isVisible());
682 
683             peer = ComponentPeer::getPeerFor (this);
684 
685             if (peer == nullptr)
686                 return;
687 
688             if (wasFullscreen)
689             {
690                 peer->setFullScreen (true);
691                 peer->setNonFullScreenBounds (oldNonFullScreenBounds);
692             }
693 
694             if (wasMinimised)
695                 peer->setMinimised (true);
696 
697            #if JUCE_WINDOWS
698             if (isAlwaysOnTop())
699                 peer->setAlwaysOnTop (true);
700            #endif
701 
702             peer->setConstrainer (currentConstrainer);
703 
704             repaint();
705             internalHierarchyChanged();
706         }
707     }
708 }
709 
removeFromDesktop()710 void Component::removeFromDesktop()
711 {
712     // if component methods are being called from threads other than the message
713     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
714     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
715 
716     if (flags.hasHeavyweightPeerFlag)
717     {
718         ComponentHelpers::releaseAllCachedImageResources (*this);
719 
720         auto* peer = ComponentPeer::getPeerFor (this);
721         jassert (peer != nullptr);
722 
723         flags.hasHeavyweightPeerFlag = false;
724         delete peer;
725 
726         Desktop::getInstance().removeDesktopComponent (this);
727     }
728 }
729 
isOnDesktop() const730 bool Component::isOnDesktop() const noexcept
731 {
732     return flags.hasHeavyweightPeerFlag;
733 }
734 
getPeer() const735 ComponentPeer* Component::getPeer() const
736 {
737     if (flags.hasHeavyweightPeerFlag)
738         return ComponentPeer::getPeerFor (this);
739 
740     if (parentComponent == nullptr)
741         return nullptr;
742 
743     return parentComponent->getPeer();
744 }
745 
userTriedToCloseWindow()746 void Component::userTriedToCloseWindow()
747 {
748     /* This means that the user's trying to get rid of your window with the 'close window' system
749        menu option (on windows) or possibly the task manager - you should really handle this
750        and delete or hide your component in an appropriate way.
751 
752        If you want to ignore the event and don't want to trigger this assertion, just override
753        this method and do nothing.
754     */
755     jassertfalse;
756 }
757 
minimisationStateChanged(bool)758 void Component::minimisationStateChanged (bool) {}
759 
getDesktopScaleFactor() const760 float Component::getDesktopScaleFactor() const  { return Desktop::getInstance().getGlobalScaleFactor(); }
761 
762 //==============================================================================
setOpaque(bool shouldBeOpaque)763 void Component::setOpaque (bool shouldBeOpaque)
764 {
765     if (shouldBeOpaque != flags.opaqueFlag)
766     {
767         flags.opaqueFlag = shouldBeOpaque;
768 
769         if (flags.hasHeavyweightPeerFlag)
770             if (auto* peer = ComponentPeer::getPeerFor (this))
771                 addToDesktop (peer->getStyleFlags());  // recreates the heavyweight window
772 
773         repaint();
774     }
775 }
776 
isOpaque() const777 bool Component::isOpaque() const noexcept
778 {
779     return flags.opaqueFlag;
780 }
781 
782 //==============================================================================
783 struct StandardCachedComponentImage  : public CachedComponentImage
784 {
StandardCachedComponentImagejuce::StandardCachedComponentImage785     StandardCachedComponentImage (Component& c) noexcept : owner (c)  {}
786 
paintjuce::StandardCachedComponentImage787     void paint (Graphics& g) override
788     {
789         scale = g.getInternalContext().getPhysicalPixelScaleFactor();
790         auto compBounds = owner.getLocalBounds();
791         auto imageBounds = compBounds * scale;
792 
793         if (image.isNull() || image.getBounds() != imageBounds)
794         {
795             image = Image (owner.isOpaque() ? Image::RGB
796                                             : Image::ARGB,
797                            jmax (1, imageBounds.getWidth()),
798                            jmax (1, imageBounds.getHeight()),
799                            ! owner.isOpaque());
800 
801             validArea.clear();
802         }
803 
804         if (! validArea.containsRectangle (compBounds))
805         {
806             Graphics imG (image);
807             auto& lg = imG.getInternalContext();
808 
809             lg.addTransform (AffineTransform::scale (scale));
810 
811             for (auto& i : validArea)
812                 lg.excludeClipRectangle (i);
813 
814             if (! owner.isOpaque())
815             {
816                 lg.setFill (Colours::transparentBlack);
817                 lg.fillRect (compBounds, true);
818                 lg.setFill (Colours::black);
819             }
820 
821             owner.paintEntireComponent (imG, true);
822         }
823 
824         validArea = compBounds;
825 
826         g.setColour (Colours::black.withAlpha (owner.getAlpha()));
827         g.drawImageTransformed (image, AffineTransform::scale ((float) compBounds.getWidth()  / (float) imageBounds.getWidth(),
828                                                                (float) compBounds.getHeight() / (float) imageBounds.getHeight()), false);
829     }
830 
invalidateAlljuce::StandardCachedComponentImage831     bool invalidateAll() override                            { validArea.clear(); return true; }
invalidatejuce::StandardCachedComponentImage832     bool invalidate (const Rectangle<int>& area) override    { validArea.subtract (area); return true; }
releaseResourcesjuce::StandardCachedComponentImage833     void releaseResources() override                         { image = Image(); }
834 
835 private:
836     Image image;
837     RectangleList<int> validArea;
838     Component& owner;
839     float scale = 1.0f;
840 
841     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandardCachedComponentImage)
842 };
843 
setCachedComponentImage(CachedComponentImage * newCachedImage)844 void Component::setCachedComponentImage (CachedComponentImage* newCachedImage)
845 {
846     if (cachedImage.get() != newCachedImage)
847     {
848         cachedImage.reset (newCachedImage);
849         repaint();
850     }
851 }
852 
setBufferedToImage(bool shouldBeBuffered)853 void Component::setBufferedToImage (bool shouldBeBuffered)
854 {
855     // This assertion means that this component is already using a custom CachedComponentImage,
856     // so by calling setBufferedToImage, you'll be deleting the custom one - this is almost certainly
857     // not what you wanted to happen... If you really do know what you're doing here, and want to
858     // avoid this assertion, just call setCachedComponentImage (nullptr) before setBufferedToImage().
859     jassert (cachedImage == nullptr || dynamic_cast<StandardCachedComponentImage*> (cachedImage.get()) != nullptr);
860 
861     if (shouldBeBuffered)
862     {
863         if (cachedImage == nullptr)
864             cachedImage.reset (new StandardCachedComponentImage (*this));
865     }
866     else
867     {
868         cachedImage.reset();
869     }
870 }
871 
872 //==============================================================================
reorderChildInternal(int sourceIndex,int destIndex)873 void Component::reorderChildInternal (int sourceIndex, int destIndex)
874 {
875     if (sourceIndex != destIndex)
876     {
877         auto* c = childComponentList.getUnchecked (sourceIndex);
878         jassert (c != nullptr);
879         c->repaintParent();
880 
881         childComponentList.move (sourceIndex, destIndex);
882 
883         sendFakeMouseMove();
884         internalChildrenChanged();
885     }
886 }
887 
toFront(bool setAsForeground)888 void Component::toFront (bool setAsForeground)
889 {
890     // if component methods are being called from threads other than the message
891     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
892     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
893 
894     if (flags.hasHeavyweightPeerFlag)
895     {
896         if (auto* peer = getPeer())
897         {
898             peer->toFront (setAsForeground);
899 
900             if (setAsForeground && ! hasKeyboardFocus (true))
901                 grabKeyboardFocus();
902         }
903     }
904     else if (parentComponent != nullptr)
905     {
906         auto& childList = parentComponent->childComponentList;
907 
908         if (childList.getLast() != this)
909         {
910             auto index = childList.indexOf (this);
911 
912             if (index >= 0)
913             {
914                 int insertIndex = -1;
915 
916                 if (! flags.alwaysOnTopFlag)
917                 {
918                     insertIndex = childList.size() - 1;
919 
920                     while (insertIndex > 0 && childList.getUnchecked (insertIndex)->isAlwaysOnTop())
921                         --insertIndex;
922                 }
923 
924                 parentComponent->reorderChildInternal (index, insertIndex);
925             }
926         }
927 
928         if (setAsForeground)
929         {
930             internalBroughtToFront();
931 
932             if (isShowing())
933                 grabKeyboardFocus();
934         }
935     }
936 }
937 
toBehind(Component * other)938 void Component::toBehind (Component* other)
939 {
940     if (other != nullptr && other != this)
941     {
942         // the two components must belong to the same parent..
943         jassert (parentComponent == other->parentComponent);
944 
945         if (parentComponent != nullptr)
946         {
947             auto& childList = parentComponent->childComponentList;
948             auto index = childList.indexOf (this);
949 
950             if (index >= 0 && childList [index + 1] != other)
951             {
952                 auto otherIndex = childList.indexOf (other);
953 
954                 if (otherIndex >= 0)
955                 {
956                     if (index < otherIndex)
957                         --otherIndex;
958 
959                     parentComponent->reorderChildInternal (index, otherIndex);
960                 }
961             }
962         }
963         else if (isOnDesktop())
964         {
965             jassert (other->isOnDesktop());
966 
967             if (other->isOnDesktop())
968             {
969                 auto* us = getPeer();
970                 auto* them = other->getPeer();
971                 jassert (us != nullptr && them != nullptr);
972 
973                 if (us != nullptr && them != nullptr)
974                     us->toBehind (them);
975             }
976         }
977     }
978 }
979 
toBack()980 void Component::toBack()
981 {
982     if (isOnDesktop())
983     {
984         jassertfalse; //xxx need to add this to native window
985     }
986     else if (parentComponent != nullptr)
987     {
988         auto& childList = parentComponent->childComponentList;
989 
990         if (childList.getFirst() != this)
991         {
992             auto index = childList.indexOf (this);
993 
994             if (index > 0)
995             {
996                 int insertIndex = 0;
997 
998                 if (flags.alwaysOnTopFlag)
999                     while (insertIndex < childList.size() && ! childList.getUnchecked (insertIndex)->isAlwaysOnTop())
1000                         ++insertIndex;
1001 
1002                 parentComponent->reorderChildInternal (index, insertIndex);
1003             }
1004         }
1005     }
1006 }
1007 
setAlwaysOnTop(bool shouldStayOnTop)1008 void Component::setAlwaysOnTop (bool shouldStayOnTop)
1009 {
1010     if (shouldStayOnTop != flags.alwaysOnTopFlag)
1011     {
1012         BailOutChecker checker (this);
1013 
1014         flags.alwaysOnTopFlag = shouldStayOnTop;
1015 
1016         if (isOnDesktop())
1017         {
1018             if (auto* peer = getPeer())
1019             {
1020                 if (! peer->setAlwaysOnTop (shouldStayOnTop))
1021                 {
1022                     // some kinds of peer can't change their always-on-top status, so
1023                     // for these, we'll need to create a new window
1024                     auto oldFlags = peer->getStyleFlags();
1025                     removeFromDesktop();
1026                     addToDesktop (oldFlags);
1027                 }
1028             }
1029         }
1030 
1031         if (shouldStayOnTop && ! checker.shouldBailOut())
1032             toFront (false);
1033 
1034         if (! checker.shouldBailOut())
1035             internalHierarchyChanged();
1036     }
1037 }
1038 
isAlwaysOnTop() const1039 bool Component::isAlwaysOnTop() const noexcept
1040 {
1041     return flags.alwaysOnTopFlag;
1042 }
1043 
1044 //==============================================================================
proportionOfWidth(float proportion) const1045 int Component::proportionOfWidth  (float proportion) const noexcept   { return roundToInt (proportion * (float) boundsRelativeToParent.getWidth()); }
proportionOfHeight(float proportion) const1046 int Component::proportionOfHeight (float proportion) const noexcept   { return roundToInt (proportion * (float) boundsRelativeToParent.getHeight()); }
1047 
getParentWidth() const1048 int Component::getParentWidth() const noexcept
1049 {
1050     return parentComponent != nullptr ? parentComponent->getWidth()
1051                                       : getParentMonitorArea().getWidth();
1052 }
1053 
getParentHeight() const1054 int Component::getParentHeight() const noexcept
1055 {
1056     return parentComponent != nullptr ? parentComponent->getHeight()
1057                                       : getParentMonitorArea().getHeight();
1058 }
1059 
getParentMonitorArea() const1060 Rectangle<int> Component::getParentMonitorArea() const
1061 {
1062     return Desktop::getInstance().getDisplays().findDisplayForRect (getScreenBounds()).userArea;
1063 }
1064 
getScreenX() const1065 int Component::getScreenX() const                       { return getScreenPosition().x; }
getScreenY() const1066 int Component::getScreenY() const                       { return getScreenPosition().y; }
getScreenPosition() const1067 Point<int>     Component::getScreenPosition() const     { return localPointToGlobal (Point<int>()); }
getScreenBounds() const1068 Rectangle<int> Component::getScreenBounds() const       { return localAreaToGlobal (getLocalBounds()); }
1069 
getLocalPoint(const Component * source,Point<int> point) const1070 Point<int>       Component::getLocalPoint (const Component* source, Point<int> point) const       { return ComponentHelpers::convertCoordinate (this, source, point); }
getLocalPoint(const Component * source,Point<float> point) const1071 Point<float>     Component::getLocalPoint (const Component* source, Point<float> point) const     { return ComponentHelpers::convertCoordinate (this, source, point); }
getLocalArea(const Component * source,Rectangle<int> area) const1072 Rectangle<int>   Component::getLocalArea  (const Component* source, Rectangle<int> area) const    { return ComponentHelpers::convertCoordinate (this, source, area); }
getLocalArea(const Component * source,Rectangle<float> area) const1073 Rectangle<float> Component::getLocalArea  (const Component* source, Rectangle<float> area) const  { return ComponentHelpers::convertCoordinate (this, source, area); }
1074 
localPointToGlobal(Point<int> point) const1075 Point<int>       Component::localPointToGlobal (Point<int> point) const       { return ComponentHelpers::convertCoordinate (nullptr, this, point); }
localPointToGlobal(Point<float> point) const1076 Point<float>     Component::localPointToGlobal (Point<float> point) const     { return ComponentHelpers::convertCoordinate (nullptr, this, point); }
localAreaToGlobal(Rectangle<int> area) const1077 Rectangle<int>   Component::localAreaToGlobal  (Rectangle<int> area) const    { return ComponentHelpers::convertCoordinate (nullptr, this, area); }
localAreaToGlobal(Rectangle<float> area) const1078 Rectangle<float> Component::localAreaToGlobal  (Rectangle<float> area) const  { return ComponentHelpers::convertCoordinate (nullptr, this, area); }
1079 
1080 //==============================================================================
setBounds(int x,int y,int w,int h)1081 void Component::setBounds (int x, int y, int w, int h)
1082 {
1083     // if component methods are being called from threads other than the message
1084     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
1085     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
1086 
1087     if (w < 0) w = 0;
1088     if (h < 0) h = 0;
1089 
1090     const bool wasResized  = (getWidth() != w || getHeight() != h);
1091     const bool wasMoved    = (getX() != x || getY() != y);
1092 
1093    #if JUCE_DEBUG
1094     // It's a very bad idea to try to resize a window during its paint() method!
1095     jassert (! (flags.isInsidePaintCall && wasResized && isOnDesktop()));
1096    #endif
1097 
1098     if (wasMoved || wasResized)
1099     {
1100         const bool showing = isShowing();
1101 
1102         if (showing)
1103         {
1104             // send a fake mouse move to trigger enter/exit messages if needed..
1105             sendFakeMouseMove();
1106 
1107             if (! flags.hasHeavyweightPeerFlag)
1108                 repaintParent();
1109         }
1110 
1111         boundsRelativeToParent.setBounds (x, y, w, h);
1112 
1113         if (showing)
1114         {
1115             if (wasResized)
1116                 repaint();
1117             else if (! flags.hasHeavyweightPeerFlag)
1118                 repaintParent();
1119         }
1120         else if (cachedImage != nullptr)
1121         {
1122             cachedImage->invalidateAll();
1123         }
1124 
1125         flags.isMoveCallbackPending = wasMoved;
1126         flags.isResizeCallbackPending = wasResized;
1127 
1128         if (flags.hasHeavyweightPeerFlag)
1129             if (auto* peer = getPeer())
1130                 peer->updateBounds();
1131 
1132         sendMovedResizedMessagesIfPending();
1133     }
1134 }
1135 
sendMovedResizedMessagesIfPending()1136 void Component::sendMovedResizedMessagesIfPending()
1137 {
1138     const bool wasMoved   = flags.isMoveCallbackPending;
1139     const bool wasResized = flags.isResizeCallbackPending;
1140 
1141     if (wasMoved || wasResized)
1142     {
1143         flags.isMoveCallbackPending = false;
1144         flags.isResizeCallbackPending = false;
1145 
1146         sendMovedResizedMessages (wasMoved, wasResized);
1147     }
1148 }
1149 
sendMovedResizedMessages(bool wasMoved,bool wasResized)1150 void Component::sendMovedResizedMessages (bool wasMoved, bool wasResized)
1151 {
1152     BailOutChecker checker (this);
1153 
1154     if (wasMoved)
1155     {
1156         moved();
1157 
1158         if (checker.shouldBailOut())
1159             return;
1160     }
1161 
1162     if (wasResized)
1163     {
1164         resized();
1165 
1166         if (checker.shouldBailOut())
1167             return;
1168 
1169         for (int i = childComponentList.size(); --i >= 0;)
1170         {
1171             childComponentList.getUnchecked(i)->parentSizeChanged();
1172 
1173             if (checker.shouldBailOut())
1174                 return;
1175 
1176             i = jmin (i, childComponentList.size());
1177         }
1178     }
1179 
1180     if (parentComponent != nullptr)
1181         parentComponent->childBoundsChanged (this);
1182 
1183     if (! checker.shouldBailOut())
1184         componentListeners.callChecked (checker, [=] (ComponentListener& l) { l.componentMovedOrResized (*this, wasMoved, wasResized); });
1185 }
1186 
setSize(int w,int h)1187 void Component::setSize (int w, int h)                  { setBounds (getX(), getY(), w, h); }
1188 
setTopLeftPosition(int x,int y)1189 void Component::setTopLeftPosition (int x, int y)       { setTopLeftPosition ({ x, y }); }
setTopLeftPosition(Point<int> pos)1190 void Component::setTopLeftPosition (Point<int> pos)     { setBounds (pos.x, pos.y, getWidth(), getHeight()); }
1191 
setTopRightPosition(int x,int y)1192 void Component::setTopRightPosition (int x, int y)      { setTopLeftPosition (x - getWidth(), y); }
setBounds(Rectangle<int> r)1193 void Component::setBounds (Rectangle<int> r)            { setBounds (r.getX(), r.getY(), r.getWidth(), r.getHeight()); }
1194 
setCentrePosition(Point<int> p)1195 void Component::setCentrePosition (Point<int> p)        { setBounds (getBounds().withCentre (p.transformedBy (getTransform().inverted()))); }
setCentrePosition(int x,int y)1196 void Component::setCentrePosition (int x, int y)        { setCentrePosition ({ x, y }); }
1197 
setCentreRelative(float x,float y)1198 void Component::setCentreRelative (float x, float y)
1199 {
1200     setCentrePosition (roundToInt ((float) getParentWidth()  * x),
1201                        roundToInt ((float) getParentHeight() * y));
1202 }
1203 
setBoundsRelative(Rectangle<float> target)1204 void Component::setBoundsRelative (Rectangle<float> target)
1205 {
1206     setBounds ((target * Point<float> ((float) getParentWidth(),
1207                                        (float) getParentHeight())).toNearestInt());
1208 }
1209 
setBoundsRelative(float x,float y,float w,float h)1210 void Component::setBoundsRelative (float x, float y, float w, float h)
1211 {
1212     setBoundsRelative ({ x, y, w, h });
1213 }
1214 
centreWithSize(int width,int height)1215 void Component::centreWithSize (int width, int height)
1216 {
1217     auto parentArea = ComponentHelpers::getParentOrMainMonitorBounds (*this)
1218                           .transformedBy (getTransform().inverted());
1219 
1220     setBounds (parentArea.getCentreX() - width / 2,
1221                parentArea.getCentreY() - height / 2,
1222                width, height);
1223 }
1224 
setBoundsInset(BorderSize<int> borders)1225 void Component::setBoundsInset (BorderSize<int> borders)
1226 {
1227     setBounds (borders.subtractedFrom (ComponentHelpers::getParentOrMainMonitorBounds (*this)));
1228 }
1229 
setBoundsToFit(Rectangle<int> targetArea,Justification justification,bool onlyReduceInSize)1230 void Component::setBoundsToFit (Rectangle<int> targetArea, Justification justification, bool onlyReduceInSize)
1231 {
1232     if (getLocalBounds().isEmpty() || targetArea.isEmpty())
1233     {
1234         // it's no good calling this method unless both the component and
1235         // target rectangle have a finite size.
1236         jassertfalse;
1237         return;
1238     }
1239 
1240     auto sourceArea = targetArea.withZeroOrigin();
1241 
1242     if (onlyReduceInSize
1243          && getWidth() <= targetArea.getWidth()
1244          && getHeight() <= targetArea.getHeight())
1245     {
1246         sourceArea = getLocalBounds();
1247     }
1248     else
1249     {
1250         auto sourceRatio = getHeight() / (double) getWidth();
1251         auto targetRatio = targetArea.getHeight() / (double) targetArea.getWidth();
1252 
1253         if (sourceRatio <= targetRatio)
1254             sourceArea.setHeight (jmin (targetArea.getHeight(),
1255                                         roundToInt (targetArea.getWidth() * sourceRatio)));
1256         else
1257             sourceArea.setWidth (jmin (targetArea.getWidth(),
1258                                        roundToInt (targetArea.getHeight() / sourceRatio)));
1259     }
1260 
1261     if (! sourceArea.isEmpty())
1262         setBounds (justification.appliedToRectangle (sourceArea, targetArea));
1263 }
1264 
1265 //==============================================================================
setTransform(const AffineTransform & newTransform)1266 void Component::setTransform (const AffineTransform& newTransform)
1267 {
1268     // If you pass in a transform with no inverse, the component will have no dimensions,
1269     // and there will be all sorts of maths errors when converting coordinates.
1270     jassert (! newTransform.isSingularity());
1271 
1272     if (newTransform.isIdentity())
1273     {
1274         if (affineTransform != nullptr)
1275         {
1276             repaint();
1277             affineTransform.reset();
1278             repaint();
1279             sendMovedResizedMessages (false, false);
1280         }
1281     }
1282     else if (affineTransform == nullptr)
1283     {
1284         repaint();
1285         affineTransform.reset (new AffineTransform (newTransform));
1286         repaint();
1287         sendMovedResizedMessages (false, false);
1288     }
1289     else if (*affineTransform != newTransform)
1290     {
1291         repaint();
1292         *affineTransform = newTransform;
1293         repaint();
1294         sendMovedResizedMessages (false, false);
1295     }
1296 }
1297 
isTransformed() const1298 bool Component::isTransformed() const noexcept
1299 {
1300     return affineTransform != nullptr;
1301 }
1302 
getTransform() const1303 AffineTransform Component::getTransform() const
1304 {
1305     return affineTransform != nullptr ? *affineTransform : AffineTransform();
1306 }
1307 
getApproximateScaleFactorForComponent(Component * targetComponent)1308 float Component::getApproximateScaleFactorForComponent (Component* targetComponent)
1309 {
1310     AffineTransform transform;
1311 
1312     for (auto* target = targetComponent; target != nullptr; target = target->getParentComponent())
1313     {
1314         transform = transform.followedBy (target->getTransform());
1315 
1316         if (target->isOnDesktop())
1317             transform = transform.scaled (target->getDesktopScaleFactor());
1318     }
1319 
1320     auto transformScale = std::sqrt (std::abs (transform.getDeterminant()));
1321     return transformScale / Desktop::getInstance().getGlobalScaleFactor();
1322 }
1323 
1324 //==============================================================================
hitTest(int x,int y)1325 bool Component::hitTest (int x, int y)
1326 {
1327     if (! flags.ignoresMouseClicksFlag)
1328         return true;
1329 
1330     if (flags.allowChildMouseClicksFlag)
1331     {
1332         for (int i = childComponentList.size(); --i >= 0;)
1333         {
1334             auto& child = *childComponentList.getUnchecked (i);
1335 
1336             if (child.isVisible()
1337                  && ComponentHelpers::hitTest (child, ComponentHelpers::convertFromParentSpace (child, Point<int> (x, y))))
1338                 return true;
1339         }
1340     }
1341 
1342     return false;
1343 }
1344 
setInterceptsMouseClicks(bool allowClicks,bool allowClicksOnChildComponents)1345 void Component::setInterceptsMouseClicks (bool allowClicks,
1346                                           bool allowClicksOnChildComponents) noexcept
1347 {
1348     flags.ignoresMouseClicksFlag = ! allowClicks;
1349     flags.allowChildMouseClicksFlag = allowClicksOnChildComponents;
1350 }
1351 
getInterceptsMouseClicks(bool & allowsClicksOnThisComponent,bool & allowsClicksOnChildComponents) const1352 void Component::getInterceptsMouseClicks (bool& allowsClicksOnThisComponent,
1353                                           bool& allowsClicksOnChildComponents) const noexcept
1354 {
1355     allowsClicksOnThisComponent = ! flags.ignoresMouseClicksFlag;
1356     allowsClicksOnChildComponents = flags.allowChildMouseClicksFlag;
1357 }
1358 
contains(Point<int> point)1359 bool Component::contains (Point<int> point)
1360 {
1361     if (ComponentHelpers::hitTest (*this, point))
1362     {
1363         if (parentComponent != nullptr)
1364             return parentComponent->contains (ComponentHelpers::convertToParentSpace (*this, point));
1365 
1366         if (flags.hasHeavyweightPeerFlag)
1367             if (auto* peer = getPeer())
1368                 return peer->contains (ComponentHelpers::localPositionToRawPeerPos (*this, point), true);
1369     }
1370 
1371     return false;
1372 }
1373 
reallyContains(Point<int> point,bool returnTrueIfWithinAChild)1374 bool Component::reallyContains (Point<int> point, bool returnTrueIfWithinAChild)
1375 {
1376     if (! contains (point))
1377         return false;
1378 
1379     auto* top = getTopLevelComponent();
1380     auto* compAtPosition = top->getComponentAt (top->getLocalPoint (this, point));
1381 
1382     return (compAtPosition == this) || (returnTrueIfWithinAChild && isParentOf (compAtPosition));
1383 }
1384 
getComponentAt(Point<int> position)1385 Component* Component::getComponentAt (Point<int> position)
1386 {
1387     if (flags.visibleFlag && ComponentHelpers::hitTest (*this, position))
1388     {
1389         for (int i = childComponentList.size(); --i >= 0;)
1390         {
1391             auto* child = childComponentList.getUnchecked(i);
1392 
1393             child = child->getComponentAt (ComponentHelpers::convertFromParentSpace (*child, position));
1394 
1395             if (child != nullptr)
1396                 return child;
1397         }
1398 
1399         return this;
1400     }
1401 
1402     return nullptr;
1403 }
1404 
getComponentAt(int x,int y)1405 Component* Component::getComponentAt (int x, int y)
1406 {
1407     return getComponentAt ({ x, y });
1408 }
1409 
1410 //==============================================================================
addChildComponent(Component & child,int zOrder)1411 void Component::addChildComponent (Component& child, int zOrder)
1412 {
1413     // if component methods are being called from threads other than the message
1414     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
1415     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
1416 
1417     jassert (this != &child); // adding a component to itself!?
1418 
1419     if (child.parentComponent != this)
1420     {
1421         if (child.parentComponent != nullptr)
1422             child.parentComponent->removeChildComponent (&child);
1423         else
1424             child.removeFromDesktop();
1425 
1426         child.parentComponent = this;
1427 
1428         if (child.isVisible())
1429             child.repaintParent();
1430 
1431         if (! child.isAlwaysOnTop())
1432         {
1433             if (zOrder < 0 || zOrder > childComponentList.size())
1434                 zOrder = childComponentList.size();
1435 
1436             while (zOrder > 0)
1437             {
1438                 if (! childComponentList.getUnchecked (zOrder - 1)->isAlwaysOnTop())
1439                     break;
1440 
1441                 --zOrder;
1442             }
1443         }
1444 
1445         childComponentList.insert (zOrder, &child);
1446 
1447         child.internalHierarchyChanged();
1448         internalChildrenChanged();
1449     }
1450 }
1451 
addAndMakeVisible(Component & child,int zOrder)1452 void Component::addAndMakeVisible (Component& child, int zOrder)
1453 {
1454     child.setVisible (true);
1455     addChildComponent (child, zOrder);
1456 }
1457 
addChildComponent(Component * child,int zOrder)1458 void Component::addChildComponent (Component* child, int zOrder)
1459 {
1460     if (child != nullptr)
1461         addChildComponent (*child, zOrder);
1462 }
1463 
addAndMakeVisible(Component * child,int zOrder)1464 void Component::addAndMakeVisible (Component* child, int zOrder)
1465 {
1466     if (child != nullptr)
1467         addAndMakeVisible (*child, zOrder);
1468 }
1469 
addChildAndSetID(Component * child,const String & childID)1470 void Component::addChildAndSetID (Component* child, const String& childID)
1471 {
1472     if (child != nullptr)
1473     {
1474         child->setComponentID (childID);
1475         addAndMakeVisible (child);
1476     }
1477 }
1478 
removeChildComponent(Component * child)1479 void Component::removeChildComponent (Component* child)
1480 {
1481     removeChildComponent (childComponentList.indexOf (child), true, true);
1482 }
1483 
removeChildComponent(int index)1484 Component* Component::removeChildComponent (int index)
1485 {
1486     return removeChildComponent (index, true, true);
1487 }
1488 
removeChildComponent(int index,bool sendParentEvents,bool sendChildEvents)1489 Component* Component::removeChildComponent (int index, bool sendParentEvents, bool sendChildEvents)
1490 {
1491     // if component methods are being called from threads other than the message
1492     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
1493     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN
1494 
1495     auto* child = childComponentList [index];
1496 
1497     if (child != nullptr)
1498     {
1499         sendParentEvents = sendParentEvents && child->isShowing();
1500 
1501         if (sendParentEvents)
1502         {
1503             sendFakeMouseMove();
1504 
1505             if (child->isVisible())
1506                 child->repaintParent();
1507         }
1508 
1509         childComponentList.remove (index);
1510         child->parentComponent = nullptr;
1511 
1512         ComponentHelpers::releaseAllCachedImageResources (*child);
1513 
1514         // (NB: there are obscure situations where child->isShowing() = false, but it still has the focus)
1515         if (currentlyFocusedComponent == child || child->isParentOf (currentlyFocusedComponent))
1516         {
1517             if (sendParentEvents)
1518             {
1519                 const WeakReference<Component> thisPointer (this);
1520 
1521                 giveAwayFocus (sendChildEvents || currentlyFocusedComponent != child);
1522 
1523                 if (thisPointer == nullptr)
1524                     return child;
1525 
1526                 grabKeyboardFocus();
1527             }
1528             else
1529             {
1530                 giveAwayFocus (sendChildEvents || currentlyFocusedComponent != child);
1531             }
1532         }
1533 
1534         if (sendChildEvents)
1535             child->internalHierarchyChanged();
1536 
1537         if (sendParentEvents)
1538             internalChildrenChanged();
1539     }
1540 
1541     return child;
1542 }
1543 
1544 //==============================================================================
removeAllChildren()1545 void Component::removeAllChildren()
1546 {
1547     while (! childComponentList.isEmpty())
1548         removeChildComponent (childComponentList.size() - 1);
1549 }
1550 
deleteAllChildren()1551 void Component::deleteAllChildren()
1552 {
1553     while (! childComponentList.isEmpty())
1554         delete (removeChildComponent (childComponentList.size() - 1));
1555 }
1556 
getNumChildComponents() const1557 int Component::getNumChildComponents() const noexcept
1558 {
1559     return childComponentList.size();
1560 }
1561 
getChildComponent(int index) const1562 Component* Component::getChildComponent (int index) const noexcept
1563 {
1564     return childComponentList[index];
1565 }
1566 
getIndexOfChildComponent(const Component * child) const1567 int Component::getIndexOfChildComponent (const Component* child) const noexcept
1568 {
1569     return childComponentList.indexOf (const_cast<Component*> (child));
1570 }
1571 
findChildWithID(StringRef targetID) const1572 Component* Component::findChildWithID (StringRef targetID) const noexcept
1573 {
1574     for (auto* c : childComponentList)
1575         if (c->componentID == targetID)
1576             return c;
1577 
1578     return nullptr;
1579 }
1580 
getTopLevelComponent() const1581 Component* Component::getTopLevelComponent() const noexcept
1582 {
1583     auto* comp = this;
1584 
1585     while (comp->parentComponent != nullptr)
1586         comp = comp->parentComponent;
1587 
1588     return const_cast<Component*> (comp);
1589 }
1590 
isParentOf(const Component * possibleChild) const1591 bool Component::isParentOf (const Component* possibleChild) const noexcept
1592 {
1593     while (possibleChild != nullptr)
1594     {
1595         possibleChild = possibleChild->parentComponent;
1596 
1597         if (possibleChild == this)
1598             return true;
1599     }
1600 
1601     return false;
1602 }
1603 
1604 //==============================================================================
parentHierarchyChanged()1605 void Component::parentHierarchyChanged() {}
childrenChanged()1606 void Component::childrenChanged() {}
1607 
internalChildrenChanged()1608 void Component::internalChildrenChanged()
1609 {
1610     if (componentListeners.isEmpty())
1611     {
1612         childrenChanged();
1613     }
1614     else
1615     {
1616         BailOutChecker checker (this);
1617 
1618         childrenChanged();
1619 
1620         if (! checker.shouldBailOut())
1621             componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentChildrenChanged (*this); });
1622     }
1623 }
1624 
internalHierarchyChanged()1625 void Component::internalHierarchyChanged()
1626 {
1627     BailOutChecker checker (this);
1628 
1629     parentHierarchyChanged();
1630 
1631     if (checker.shouldBailOut())
1632         return;
1633 
1634     componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentParentHierarchyChanged (*this); });
1635 
1636     if (checker.shouldBailOut())
1637         return;
1638 
1639     for (int i = childComponentList.size(); --i >= 0;)
1640     {
1641         childComponentList.getUnchecked (i)->internalHierarchyChanged();
1642 
1643         if (checker.shouldBailOut())
1644         {
1645             // you really shouldn't delete the parent component during a callback telling you
1646             // that it's changed..
1647             jassertfalse;
1648             return;
1649         }
1650 
1651         i = jmin (i, childComponentList.size());
1652     }
1653 }
1654 
1655 //==============================================================================
1656 #if JUCE_MODAL_LOOPS_PERMITTED
runModalLoop()1657 int Component::runModalLoop()
1658 {
1659     if (! MessageManager::getInstance()->isThisTheMessageThread())
1660     {
1661         // use a callback so this can be called from non-gui threads
1662         return (int) (pointer_sized_int) MessageManager::getInstance()
1663                                            ->callFunctionOnMessageThread (&ComponentHelpers::runModalLoopCallback, this);
1664     }
1665 
1666     if (! isCurrentlyModal (false))
1667         enterModalState (true);
1668 
1669     return ModalComponentManager::getInstance()->runEventLoopForCurrentComponent();
1670 }
1671 #endif
1672 
1673 //==============================================================================
enterModalState(bool shouldTakeKeyboardFocus,ModalComponentManager::Callback * callback,bool deleteWhenDismissed)1674 void Component::enterModalState (bool shouldTakeKeyboardFocus,
1675                                  ModalComponentManager::Callback* callback,
1676                                  bool deleteWhenDismissed)
1677 {
1678     // if component methods are being called from threads other than the message
1679     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
1680     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
1681 
1682     if (! isCurrentlyModal (false))
1683     {
1684         auto& mcm = *ModalComponentManager::getInstance();
1685         mcm.startModal (this, deleteWhenDismissed);
1686         mcm.attachCallback (this, callback);
1687 
1688         setVisible (true);
1689 
1690         if (shouldTakeKeyboardFocus)
1691             grabKeyboardFocus();
1692     }
1693     else
1694     {
1695         // Probably a bad idea to try to make a component modal twice!
1696         jassertfalse;
1697     }
1698 }
1699 
exitModalState(int returnValue)1700 void Component::exitModalState (int returnValue)
1701 {
1702     if (isCurrentlyModal (false))
1703     {
1704         if (MessageManager::getInstance()->isThisTheMessageThread())
1705         {
1706             auto& mcm = *ModalComponentManager::getInstance();
1707             mcm.endModal (this, returnValue);
1708             mcm.bringModalComponentsToFront();
1709 
1710             // If any of the mouse sources are over another Component when we exit the modal state then send a mouse enter event
1711             for (auto& ms : Desktop::getInstance().getMouseSources())
1712                 if (auto* c = ms.getComponentUnderMouse())
1713                     c->internalMouseEnter (ms, ms.getScreenPosition(), Time::getCurrentTime());
1714         }
1715         else
1716         {
1717             WeakReference<Component> target (this);
1718 
1719             MessageManager::callAsync ([=]
1720             {
1721                 if (auto* c = target.get())
1722                     c->exitModalState (returnValue);
1723             });
1724         }
1725     }
1726 }
1727 
isCurrentlyModal(bool onlyConsiderForemostModalComponent) const1728 bool Component::isCurrentlyModal (bool onlyConsiderForemostModalComponent) const noexcept
1729 {
1730     auto& mcm = *ModalComponentManager::getInstance();
1731 
1732     return onlyConsiderForemostModalComponent ? mcm.isFrontModalComponent (this)
1733                                               : mcm.isModal (this);
1734 }
1735 
isCurrentlyBlockedByAnotherModalComponent() const1736 bool Component::isCurrentlyBlockedByAnotherModalComponent() const
1737 {
1738     auto* mc = getCurrentlyModalComponent();
1739 
1740     return ! (mc == nullptr || mc == this || mc->isParentOf (this)
1741                || mc->canModalEventBeSentToComponent (this));
1742 }
1743 
getNumCurrentlyModalComponents()1744 int JUCE_CALLTYPE Component::getNumCurrentlyModalComponents() noexcept
1745 {
1746     return ModalComponentManager::getInstance()->getNumModalComponents();
1747 }
1748 
getCurrentlyModalComponent(int index)1749 Component* JUCE_CALLTYPE Component::getCurrentlyModalComponent (int index) noexcept
1750 {
1751     return ModalComponentManager::getInstance()->getModalComponent (index);
1752 }
1753 
1754 //==============================================================================
setBroughtToFrontOnMouseClick(bool shouldBeBroughtToFront)1755 void Component::setBroughtToFrontOnMouseClick (bool shouldBeBroughtToFront) noexcept
1756 {
1757     flags.bringToFrontOnClickFlag = shouldBeBroughtToFront;
1758 }
1759 
isBroughtToFrontOnMouseClick() const1760 bool Component::isBroughtToFrontOnMouseClick() const noexcept
1761 {
1762     return flags.bringToFrontOnClickFlag;
1763 }
1764 
1765 //==============================================================================
setMouseCursor(const MouseCursor & newCursor)1766 void Component::setMouseCursor (const MouseCursor& newCursor)
1767 {
1768     if (cursor != newCursor)
1769     {
1770         cursor = newCursor;
1771 
1772         if (flags.visibleFlag)
1773             updateMouseCursor();
1774     }
1775 }
1776 
getMouseCursor()1777 MouseCursor Component::getMouseCursor()
1778 {
1779     return cursor;
1780 }
1781 
updateMouseCursor() const1782 void Component::updateMouseCursor() const
1783 {
1784     Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
1785 }
1786 
1787 //==============================================================================
setRepaintsOnMouseActivity(bool shouldRepaint)1788 void Component::setRepaintsOnMouseActivity (bool shouldRepaint) noexcept
1789 {
1790     flags.repaintOnMouseActivityFlag = shouldRepaint;
1791 }
1792 
1793 //==============================================================================
getAlpha() const1794 float Component::getAlpha() const noexcept
1795 {
1796     return (255 - componentTransparency) / 255.0f;
1797 }
1798 
setAlpha(float newAlpha)1799 void Component::setAlpha (float newAlpha)
1800 {
1801     auto newIntAlpha = (uint8) (255 - jlimit (0, 255, roundToInt (newAlpha * 255.0)));
1802 
1803     if (componentTransparency != newIntAlpha)
1804     {
1805         componentTransparency = newIntAlpha;
1806         alphaChanged();
1807     }
1808 }
1809 
alphaChanged()1810 void Component::alphaChanged()
1811 {
1812     if (flags.hasHeavyweightPeerFlag)
1813     {
1814         if (auto* peer = getPeer())
1815             peer->setAlpha (getAlpha());
1816     }
1817     else
1818     {
1819         repaint();
1820     }
1821 }
1822 
1823 //==============================================================================
repaint()1824 void Component::repaint()
1825 {
1826     internalRepaintUnchecked (getLocalBounds(), true);
1827 }
1828 
repaint(int x,int y,int w,int h)1829 void Component::repaint (int x, int y, int w, int h)
1830 {
1831     internalRepaint ({ x, y, w, h });
1832 }
1833 
repaint(Rectangle<int> area)1834 void Component::repaint (Rectangle<int> area)
1835 {
1836     internalRepaint (area);
1837 }
1838 
repaintParent()1839 void Component::repaintParent()
1840 {
1841     if (parentComponent != nullptr)
1842         parentComponent->internalRepaint (ComponentHelpers::convertToParentSpace (*this, getLocalBounds()));
1843 }
1844 
internalRepaint(Rectangle<int> area)1845 void Component::internalRepaint (Rectangle<int> area)
1846 {
1847     area = area.getIntersection (getLocalBounds());
1848 
1849     if (! area.isEmpty())
1850         internalRepaintUnchecked (area, false);
1851 }
1852 
internalRepaintUnchecked(Rectangle<int> area,bool isEntireComponent)1853 void Component::internalRepaintUnchecked (Rectangle<int> area, bool isEntireComponent)
1854 {
1855     // if component methods are being called from threads other than the message
1856     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
1857     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
1858 
1859     if (flags.visibleFlag)
1860     {
1861         if (cachedImage != nullptr)
1862             if (! (isEntireComponent ? cachedImage->invalidateAll()
1863                                      : cachedImage->invalidate (area)))
1864                 return;
1865 
1866         if (area.isEmpty())
1867             return;
1868 
1869         if (flags.hasHeavyweightPeerFlag)
1870         {
1871             if (auto* peer = getPeer())
1872             {
1873                 // Tweak the scaling so that the component's integer size exactly aligns with the peer's scaled size
1874                 auto peerBounds = peer->getBounds();
1875                 auto scaled = area * Point<float> ((float) peerBounds.getWidth()  / (float) getWidth(),
1876                                                    (float) peerBounds.getHeight() / (float) getHeight());
1877 
1878                 peer->repaint (affineTransform != nullptr ? scaled.transformedBy (*affineTransform) : scaled);
1879             }
1880         }
1881         else
1882         {
1883             if (parentComponent != nullptr)
1884                 parentComponent->internalRepaint (ComponentHelpers::convertToParentSpace (*this, area));
1885         }
1886     }
1887 }
1888 
1889 //==============================================================================
paint(Graphics &)1890 void Component::paint (Graphics&)
1891 {
1892     // if your component is marked as opaque, you must implement a paint
1893     // method and ensure that its entire area is completely painted.
1894     jassert (getBounds().isEmpty() || ! isOpaque());
1895 }
1896 
paintOverChildren(Graphics &)1897 void Component::paintOverChildren (Graphics&)
1898 {
1899     // all painting is done in the subclasses
1900 }
1901 
1902 //==============================================================================
paintWithinParentContext(Graphics & g)1903 void Component::paintWithinParentContext (Graphics& g)
1904 {
1905     g.setOrigin (getPosition());
1906 
1907     if (cachedImage != nullptr)
1908         cachedImage->paint (g);
1909     else
1910         paintEntireComponent (g, false);
1911 }
1912 
paintComponentAndChildren(Graphics & g)1913 void Component::paintComponentAndChildren (Graphics& g)
1914 {
1915     auto clipBounds = g.getClipBounds();
1916 
1917     if (flags.dontClipGraphicsFlag)
1918     {
1919         paint (g);
1920     }
1921     else
1922     {
1923         Graphics::ScopedSaveState ss (g);
1924 
1925         if (! (ComponentHelpers::clipObscuredRegions (*this, g, clipBounds, {}) && g.isClipEmpty()))
1926             paint (g);
1927     }
1928 
1929     for (int i = 0; i < childComponentList.size(); ++i)
1930     {
1931         auto& child = *childComponentList.getUnchecked (i);
1932 
1933         if (child.isVisible())
1934         {
1935             if (child.affineTransform != nullptr)
1936             {
1937                 Graphics::ScopedSaveState ss (g);
1938 
1939                 g.addTransform (*child.affineTransform);
1940 
1941                 if ((child.flags.dontClipGraphicsFlag && ! g.isClipEmpty()) || g.reduceClipRegion (child.getBounds()))
1942                     child.paintWithinParentContext (g);
1943             }
1944             else if (clipBounds.intersects (child.getBounds()))
1945             {
1946                 Graphics::ScopedSaveState ss (g);
1947 
1948                 if (child.flags.dontClipGraphicsFlag)
1949                 {
1950                     child.paintWithinParentContext (g);
1951                 }
1952                 else if (g.reduceClipRegion (child.getBounds()))
1953                 {
1954                     bool nothingClipped = true;
1955 
1956                     for (int j = i + 1; j < childComponentList.size(); ++j)
1957                     {
1958                         auto& sibling = *childComponentList.getUnchecked (j);
1959 
1960                         if (sibling.flags.opaqueFlag && sibling.isVisible() && sibling.affineTransform == nullptr)
1961                         {
1962                             nothingClipped = false;
1963                             g.excludeClipRegion (sibling.getBounds());
1964                         }
1965                     }
1966 
1967                     if (nothingClipped || ! g.isClipEmpty())
1968                         child.paintWithinParentContext (g);
1969                 }
1970             }
1971         }
1972     }
1973 
1974     Graphics::ScopedSaveState ss (g);
1975     paintOverChildren (g);
1976 }
1977 
paintEntireComponent(Graphics & g,bool ignoreAlphaLevel)1978 void Component::paintEntireComponent (Graphics& g, bool ignoreAlphaLevel)
1979 {
1980     // If sizing a top-level-window and the OS paint message is delivered synchronously
1981     // before resized() is called, then we'll invoke the callback here, to make sure
1982     // the components inside have had a chance to sort their sizes out..
1983    #if JUCE_DEBUG
1984     if (! flags.isInsidePaintCall) // (avoids an assertion in plugins hosted in WaveLab)
1985    #endif
1986         sendMovedResizedMessagesIfPending();
1987 
1988    #if JUCE_DEBUG
1989     flags.isInsidePaintCall = true;
1990    #endif
1991 
1992     if (effect != nullptr)
1993     {
1994         auto scale = g.getInternalContext().getPhysicalPixelScaleFactor();
1995 
1996         auto scaledBounds = getLocalBounds() * scale;
1997 
1998         Image effectImage (flags.opaqueFlag ? Image::RGB : Image::ARGB,
1999                            scaledBounds.getWidth(), scaledBounds.getHeight(), ! flags.opaqueFlag);
2000         {
2001             Graphics g2 (effectImage);
2002             g2.addTransform (AffineTransform::scale ((float) scaledBounds.getWidth()  / (float) getWidth(),
2003                                                      (float) scaledBounds.getHeight() / (float) getHeight()));
2004             paintComponentAndChildren (g2);
2005         }
2006 
2007         Graphics::ScopedSaveState ss (g);
2008 
2009         g.addTransform (AffineTransform::scale (1.0f / scale));
2010         effect->applyEffect (effectImage, g, scale, ignoreAlphaLevel ? 1.0f : getAlpha());
2011     }
2012     else if (componentTransparency > 0 && ! ignoreAlphaLevel)
2013     {
2014         if (componentTransparency < 255)
2015         {
2016             g.beginTransparencyLayer (getAlpha());
2017             paintComponentAndChildren (g);
2018             g.endTransparencyLayer();
2019         }
2020     }
2021     else
2022     {
2023         paintComponentAndChildren (g);
2024     }
2025 
2026    #if JUCE_DEBUG
2027     flags.isInsidePaintCall = false;
2028    #endif
2029 }
2030 
setPaintingIsUnclipped(bool shouldPaintWithoutClipping)2031 void Component::setPaintingIsUnclipped (bool shouldPaintWithoutClipping) noexcept
2032 {
2033     flags.dontClipGraphicsFlag = shouldPaintWithoutClipping;
2034 }
2035 
isPaintingUnclipped() const2036 bool Component::isPaintingUnclipped() const noexcept
2037 {
2038     return flags.dontClipGraphicsFlag;
2039 }
2040 
2041 //==============================================================================
createComponentSnapshot(Rectangle<int> areaToGrab,bool clipImageToComponentBounds,float scaleFactor)2042 Image Component::createComponentSnapshot (Rectangle<int> areaToGrab,
2043                                           bool clipImageToComponentBounds, float scaleFactor)
2044 {
2045     auto r = areaToGrab;
2046 
2047     if (clipImageToComponentBounds)
2048         r = r.getIntersection (getLocalBounds());
2049 
2050     if (r.isEmpty())
2051         return {};
2052 
2053     auto w = roundToInt (scaleFactor * (float) r.getWidth());
2054     auto h = roundToInt (scaleFactor * (float) r.getHeight());
2055 
2056     Image image (flags.opaqueFlag ? Image::RGB : Image::ARGB, w, h, true);
2057 
2058     Graphics g (image);
2059 
2060     if (w != getWidth() || h != getHeight())
2061         g.addTransform (AffineTransform::scale ((float) w / (float) r.getWidth(),
2062                                                 (float) h / (float) r.getHeight()));
2063     g.setOrigin (-r.getPosition());
2064 
2065     paintEntireComponent (g, true);
2066 
2067     return image;
2068 }
2069 
setComponentEffect(ImageEffectFilter * newEffect)2070 void Component::setComponentEffect (ImageEffectFilter* newEffect)
2071 {
2072     if (effect != newEffect)
2073     {
2074         effect = newEffect;
2075         repaint();
2076     }
2077 }
2078 
2079 //==============================================================================
getLookAndFeel() const2080 LookAndFeel& Component::getLookAndFeel() const noexcept
2081 {
2082     for (auto* c = this; c != nullptr; c = c->parentComponent)
2083         if (auto lf = c->lookAndFeel.get())
2084             return *lf;
2085 
2086     return LookAndFeel::getDefaultLookAndFeel();
2087 }
2088 
setLookAndFeel(LookAndFeel * newLookAndFeel)2089 void Component::setLookAndFeel (LookAndFeel* newLookAndFeel)
2090 {
2091     if (lookAndFeel != newLookAndFeel)
2092     {
2093         lookAndFeel = newLookAndFeel;
2094         sendLookAndFeelChange();
2095     }
2096 }
2097 
lookAndFeelChanged()2098 void Component::lookAndFeelChanged() {}
colourChanged()2099 void Component::colourChanged() {}
2100 
sendLookAndFeelChange()2101 void Component::sendLookAndFeelChange()
2102 {
2103     const WeakReference<Component> safePointer (this);
2104     repaint();
2105     lookAndFeelChanged();
2106 
2107     if (safePointer != nullptr)
2108     {
2109         colourChanged();
2110 
2111         if (safePointer != nullptr)
2112         {
2113             for (int i = childComponentList.size(); --i >= 0;)
2114             {
2115                 childComponentList.getUnchecked (i)->sendLookAndFeelChange();
2116 
2117                 if (safePointer == nullptr)
2118                     return;
2119 
2120                 i = jmin (i, childComponentList.size());
2121             }
2122         }
2123     }
2124 }
2125 
findColour(int colourID,bool inheritFromParent) const2126 Colour Component::findColour (int colourID, bool inheritFromParent) const
2127 {
2128     if (auto* v = properties.getVarPointer (ComponentHelpers::getColourPropertyID (colourID)))
2129         return Colour ((uint32) static_cast<int> (*v));
2130 
2131     if (inheritFromParent && parentComponent != nullptr
2132          && (lookAndFeel == nullptr || ! lookAndFeel->isColourSpecified (colourID)))
2133         return parentComponent->findColour (colourID, true);
2134 
2135     return getLookAndFeel().findColour (colourID);
2136 }
2137 
isColourSpecified(int colourID) const2138 bool Component::isColourSpecified (int colourID) const
2139 {
2140     return properties.contains (ComponentHelpers::getColourPropertyID (colourID));
2141 }
2142 
removeColour(int colourID)2143 void Component::removeColour (int colourID)
2144 {
2145     if (properties.remove (ComponentHelpers::getColourPropertyID (colourID)))
2146         colourChanged();
2147 }
2148 
setColour(int colourID,Colour colour)2149 void Component::setColour (int colourID, Colour colour)
2150 {
2151     if (properties.set (ComponentHelpers::getColourPropertyID (colourID), (int) colour.getARGB()))
2152         colourChanged();
2153 }
2154 
copyAllExplicitColoursTo(Component & target) const2155 void Component::copyAllExplicitColoursTo (Component& target) const
2156 {
2157     bool changed = false;
2158 
2159     for (int i = properties.size(); --i >= 0;)
2160     {
2161         auto name = properties.getName(i);
2162 
2163         if (name.toString().startsWith (colourPropertyPrefix))
2164             if (target.properties.set (name, properties [name]))
2165                 changed = true;
2166     }
2167 
2168     if (changed)
2169         target.colourChanged();
2170 }
2171 
2172 //==============================================================================
Positioner(Component & c)2173 Component::Positioner::Positioner (Component& c) noexcept  : component (c)
2174 {
2175 }
2176 
getPositioner() const2177 Component::Positioner* Component::getPositioner() const noexcept
2178 {
2179     return positioner.get();
2180 }
2181 
setPositioner(Positioner * newPositioner)2182 void Component::setPositioner (Positioner* newPositioner)
2183 {
2184     // You can only assign a positioner to the component that it was created for!
2185     jassert (newPositioner == nullptr || this == &(newPositioner->getComponent()));
2186     positioner.reset (newPositioner);
2187 }
2188 
2189 //==============================================================================
getLocalBounds() const2190 Rectangle<int> Component::getLocalBounds() const noexcept
2191 {
2192     return boundsRelativeToParent.withZeroOrigin();
2193 }
2194 
getBoundsInParent() const2195 Rectangle<int> Component::getBoundsInParent() const noexcept
2196 {
2197     return affineTransform == nullptr ? boundsRelativeToParent
2198                                       : boundsRelativeToParent.transformedBy (*affineTransform);
2199 }
2200 
2201 //==============================================================================
mouseEnter(const MouseEvent &)2202 void Component::mouseEnter (const MouseEvent&)          {}
mouseExit(const MouseEvent &)2203 void Component::mouseExit  (const MouseEvent&)          {}
mouseDown(const MouseEvent &)2204 void Component::mouseDown  (const MouseEvent&)          {}
mouseUp(const MouseEvent &)2205 void Component::mouseUp    (const MouseEvent&)          {}
mouseDrag(const MouseEvent &)2206 void Component::mouseDrag  (const MouseEvent&)          {}
mouseMove(const MouseEvent &)2207 void Component::mouseMove  (const MouseEvent&)          {}
mouseDoubleClick(const MouseEvent &)2208 void Component::mouseDoubleClick (const MouseEvent&)    {}
2209 
mouseWheelMove(const MouseEvent & e,const MouseWheelDetails & wheel)2210 void Component::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel)
2211 {
2212     // the base class just passes this event up to its parent..
2213     if (parentComponent != nullptr)
2214         parentComponent->mouseWheelMove (e.getEventRelativeTo (parentComponent), wheel);
2215 }
2216 
mouseMagnify(const MouseEvent & e,float magnifyAmount)2217 void Component::mouseMagnify (const MouseEvent& e, float magnifyAmount)
2218 {
2219     // the base class just passes this event up to its parent..
2220     if (parentComponent != nullptr)
2221         parentComponent->mouseMagnify (e.getEventRelativeTo (parentComponent), magnifyAmount);
2222 }
2223 
2224 //==============================================================================
resized()2225 void Component::resized()                       {}
moved()2226 void Component::moved()                         {}
childBoundsChanged(Component *)2227 void Component::childBoundsChanged (Component*) {}
parentSizeChanged()2228 void Component::parentSizeChanged()             {}
2229 
addComponentListener(ComponentListener * newListener)2230 void Component::addComponentListener (ComponentListener* newListener)
2231 {
2232     // if component methods are being called from threads other than the message
2233     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
2234    #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS
2235     if (getParentComponent() != nullptr)
2236     {
2237         JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
2238     }
2239    #endif
2240 
2241     componentListeners.add (newListener);
2242 }
2243 
removeComponentListener(ComponentListener * listenerToRemove)2244 void Component::removeComponentListener (ComponentListener* listenerToRemove)
2245 {
2246     componentListeners.remove (listenerToRemove);
2247 }
2248 
2249 //==============================================================================
inputAttemptWhenModal()2250 void Component::inputAttemptWhenModal()
2251 {
2252     ModalComponentManager::getInstance()->bringModalComponentsToFront();
2253     getLookAndFeel().playAlertSound();
2254 }
2255 
canModalEventBeSentToComponent(const Component *)2256 bool Component::canModalEventBeSentToComponent (const Component*)
2257 {
2258     return false;
2259 }
2260 
internalModalInputAttempt()2261 void Component::internalModalInputAttempt()
2262 {
2263     if (auto* current = getCurrentlyModalComponent())
2264         current->inputAttemptWhenModal();
2265 }
2266 
2267 //==============================================================================
postCommandMessage(int commandID)2268 void Component::postCommandMessage (int commandID)
2269 {
2270     WeakReference<Component> target (this);
2271 
2272     MessageManager::callAsync ([=]
2273     {
2274         if (auto* c = target.get())
2275             c->handleCommandMessage (commandID);
2276     });
2277 }
2278 
handleCommandMessage(int)2279 void Component::handleCommandMessage (int)
2280 {
2281     // used by subclasses
2282 }
2283 
2284 //==============================================================================
addMouseListener(MouseListener * newListener,bool wantsEventsForAllNestedChildComponents)2285 void Component::addMouseListener (MouseListener* newListener,
2286                                   bool wantsEventsForAllNestedChildComponents)
2287 {
2288     // if component methods are being called from threads other than the message
2289     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
2290     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
2291 
2292     // If you register a component as a mouselistener for itself, it'll receive all the events
2293     // twice - once via the direct callback that all components get anyway, and then again as a listener!
2294     jassert ((newListener != this) || wantsEventsForAllNestedChildComponents);
2295 
2296     if (mouseListeners == nullptr)
2297         mouseListeners.reset (new MouseListenerList());
2298 
2299     mouseListeners->addListener (newListener, wantsEventsForAllNestedChildComponents);
2300 }
2301 
removeMouseListener(MouseListener * listenerToRemove)2302 void Component::removeMouseListener (MouseListener* listenerToRemove)
2303 {
2304     // if component methods are being called from threads other than the message
2305     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
2306     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
2307 
2308     if (mouseListeners != nullptr)
2309         mouseListeners->removeListener (listenerToRemove);
2310 }
2311 
2312 //==============================================================================
internalMouseEnter(MouseInputSource source,Point<float> relativePos,Time time)2313 void Component::internalMouseEnter (MouseInputSource source, Point<float> relativePos, Time time)
2314 {
2315     if (isCurrentlyBlockedByAnotherModalComponent())
2316     {
2317         // if something else is modal, always just show a normal mouse cursor
2318         source.showMouseCursor (MouseCursor::NormalCursor);
2319         return;
2320     }
2321 
2322     if (flags.repaintOnMouseActivityFlag)
2323         repaint();
2324 
2325     BailOutChecker checker (this);
2326 
2327     const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
2328                          MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
2329                          MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
2330                          this, this, time, relativePos, time, 0, false);
2331     mouseEnter (me);
2332 
2333     if (checker.shouldBailOut())
2334         return;
2335 
2336     Desktop::getInstance().getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseEnter (me); });
2337 
2338     MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseEnter, me);
2339 }
2340 
internalMouseExit(MouseInputSource source,Point<float> relativePos,Time time)2341 void Component::internalMouseExit (MouseInputSource source, Point<float> relativePos, Time time)
2342 {
2343     if (isCurrentlyBlockedByAnotherModalComponent())
2344     {
2345         // if something else is modal, always just show a normal mouse cursor
2346         source.showMouseCursor (MouseCursor::NormalCursor);
2347         return;
2348     }
2349 
2350     if (flags.repaintOnMouseActivityFlag)
2351         repaint();
2352 
2353     BailOutChecker checker (this);
2354 
2355     const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
2356                          MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
2357                          MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
2358                          this, this, time, relativePos, time, 0, false);
2359 
2360     mouseExit (me);
2361 
2362     if (checker.shouldBailOut())
2363         return;
2364 
2365     Desktop::getInstance().getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseExit (me); });
2366 
2367     MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseExit, me);
2368 }
2369 
internalMouseDown(MouseInputSource source,Point<float> relativePos,Time time,float pressure,float orientation,float rotation,float tiltX,float tiltY)2370 void Component::internalMouseDown (MouseInputSource source, Point<float> relativePos, Time time,
2371                                    float pressure, float orientation, float rotation, float tiltX, float tiltY)
2372 {
2373     auto& desktop = Desktop::getInstance();
2374     BailOutChecker checker (this);
2375 
2376     if (isCurrentlyBlockedByAnotherModalComponent())
2377     {
2378         flags.mouseDownWasBlocked = true;
2379         internalModalInputAttempt();
2380 
2381         if (checker.shouldBailOut())
2382             return;
2383 
2384         // If processing the input attempt has exited the modal loop, we'll allow the event
2385         // to be delivered..
2386         if (isCurrentlyBlockedByAnotherModalComponent())
2387         {
2388             // allow blocked mouse-events to go to global listeners..
2389             const MouseEvent me (source, relativePos, source.getCurrentModifiers(), pressure,
2390                                  orientation, rotation, tiltX, tiltY, this, this, time, relativePos,
2391                                  time, source.getNumberOfMultipleClicks(), false);
2392 
2393             desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDown (me); });
2394             return;
2395         }
2396     }
2397 
2398     flags.mouseDownWasBlocked = false;
2399 
2400     for (auto* c = this; c != nullptr; c = c->parentComponent)
2401     {
2402         if (c->isBroughtToFrontOnMouseClick())
2403         {
2404             c->toFront (true);
2405 
2406             if (checker.shouldBailOut())
2407                 return;
2408         }
2409     }
2410 
2411     if (! flags.dontFocusOnMouseClickFlag)
2412     {
2413         grabFocusInternal (focusChangedByMouseClick, true);
2414 
2415         if (checker.shouldBailOut())
2416             return;
2417     }
2418 
2419     if (flags.repaintOnMouseActivityFlag)
2420         repaint();
2421 
2422     const MouseEvent me (source, relativePos, source.getCurrentModifiers(), pressure,
2423                          orientation, rotation, tiltX, tiltY, this, this, time, relativePos,
2424                          time, source.getNumberOfMultipleClicks(), false);
2425     mouseDown (me);
2426 
2427     if (checker.shouldBailOut())
2428         return;
2429 
2430     desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDown (me); });
2431 
2432     MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseDown, me);
2433 }
2434 
internalMouseUp(MouseInputSource source,Point<float> relativePos,Time time,const ModifierKeys oldModifiers,float pressure,float orientation,float rotation,float tiltX,float tiltY)2435 void Component::internalMouseUp (MouseInputSource source, Point<float> relativePos, Time time,
2436                                  const ModifierKeys oldModifiers, float pressure, float orientation, float rotation, float tiltX, float tiltY)
2437 {
2438     if (flags.mouseDownWasBlocked && isCurrentlyBlockedByAnotherModalComponent())
2439         return;
2440 
2441     BailOutChecker checker (this);
2442 
2443     if (flags.repaintOnMouseActivityFlag)
2444         repaint();
2445 
2446     const MouseEvent me (source, relativePos, oldModifiers, pressure, orientation,
2447                          rotation, tiltX, tiltY, this, this, time,
2448                          getLocalPoint (nullptr, source.getLastMouseDownPosition()),
2449                          source.getLastMouseDownTime(),
2450                          source.getNumberOfMultipleClicks(),
2451                          source.isLongPressOrDrag());
2452     mouseUp (me);
2453 
2454     if (checker.shouldBailOut())
2455         return;
2456 
2457     auto& desktop = Desktop::getInstance();
2458     desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseUp (me); });
2459 
2460     MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseUp, me);
2461 
2462     if (checker.shouldBailOut())
2463         return;
2464 
2465     // check for double-click
2466     if (me.getNumberOfClicks() >= 2)
2467     {
2468         mouseDoubleClick (me);
2469 
2470         if (checker.shouldBailOut())
2471             return;
2472 
2473         desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseDoubleClick (me); });
2474         MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseDoubleClick, me);
2475     }
2476 }
2477 
internalMouseDrag(MouseInputSource source,Point<float> relativePos,Time time,float pressure,float orientation,float rotation,float tiltX,float tiltY)2478 void Component::internalMouseDrag (MouseInputSource source, Point<float> relativePos, Time time,
2479                                    float pressure, float orientation, float rotation, float tiltX, float tiltY)
2480 {
2481     if (! isCurrentlyBlockedByAnotherModalComponent())
2482     {
2483         BailOutChecker checker (this);
2484 
2485         const MouseEvent me (source, relativePos, source.getCurrentModifiers(),
2486                              pressure, orientation, rotation, tiltX, tiltY, this, this, time,
2487                              getLocalPoint (nullptr, source.getLastMouseDownPosition()),
2488                              source.getLastMouseDownTime(),
2489                              source.getNumberOfMultipleClicks(),
2490                              source.isLongPressOrDrag());
2491         mouseDrag (me);
2492 
2493         if (checker.shouldBailOut())
2494             return;
2495 
2496         Desktop::getInstance().getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseDrag (me); });
2497 
2498         MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseDrag, me);
2499     }
2500 }
2501 
internalMouseMove(MouseInputSource source,Point<float> relativePos,Time time)2502 void Component::internalMouseMove (MouseInputSource source, Point<float> relativePos, Time time)
2503 {
2504     auto& desktop = Desktop::getInstance();
2505 
2506     if (isCurrentlyBlockedByAnotherModalComponent())
2507     {
2508         // allow blocked mouse-events to go to global listeners..
2509         desktop.sendMouseMove();
2510     }
2511     else
2512     {
2513         BailOutChecker checker (this);
2514 
2515         const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
2516                              MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
2517                              MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
2518                              this, this, time, relativePos, time, 0, false);
2519         mouseMove (me);
2520 
2521         if (checker.shouldBailOut())
2522             return;
2523 
2524         desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseMove (me); });
2525 
2526         MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseMove, me);
2527     }
2528 }
2529 
internalMouseWheel(MouseInputSource source,Point<float> relativePos,Time time,const MouseWheelDetails & wheel)2530 void Component::internalMouseWheel (MouseInputSource source, Point<float> relativePos,
2531                                     Time time, const MouseWheelDetails& wheel)
2532 {
2533     auto& desktop = Desktop::getInstance();
2534     BailOutChecker checker (this);
2535 
2536     const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
2537                          MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
2538                          MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
2539                          this, this, time, relativePos, time, 0, false);
2540 
2541     if (isCurrentlyBlockedByAnotherModalComponent())
2542     {
2543         // allow blocked mouse-events to go to global listeners..
2544         desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseWheelMove (me, wheel); });
2545     }
2546     else
2547     {
2548         mouseWheelMove (me, wheel);
2549 
2550         if (checker.shouldBailOut())
2551             return;
2552 
2553         desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseWheelMove (me, wheel); });
2554 
2555         if (! checker.shouldBailOut())
2556             MouseListenerList::template sendMouseEvent<const MouseEvent&, const MouseWheelDetails&> (*this, checker, &MouseListener::mouseWheelMove, me, wheel);
2557     }
2558 }
2559 
internalMagnifyGesture(MouseInputSource source,Point<float> relativePos,Time time,float amount)2560 void Component::internalMagnifyGesture (MouseInputSource source, Point<float> relativePos,
2561                                         Time time, float amount)
2562 {
2563     auto& desktop = Desktop::getInstance();
2564     BailOutChecker checker (this);
2565 
2566     const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
2567                          MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
2568                          MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
2569                          this, this, time, relativePos, time, 0, false);
2570 
2571     if (isCurrentlyBlockedByAnotherModalComponent())
2572     {
2573         // allow blocked mouse-events to go to global listeners..
2574         desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseMagnify (me, amount); });
2575     }
2576     else
2577     {
2578         mouseMagnify (me, amount);
2579 
2580         if (checker.shouldBailOut())
2581             return;
2582 
2583         desktop.mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseMagnify (me, amount); });
2584 
2585         if (! checker.shouldBailOut())
2586             MouseListenerList::template sendMouseEvent<const MouseEvent&, float> (*this, checker, &MouseListener::mouseMagnify, me, amount);
2587     }
2588 }
2589 
sendFakeMouseMove() const2590 void Component::sendFakeMouseMove() const
2591 {
2592     auto mainMouse = Desktop::getInstance().getMainMouseSource();
2593 
2594     if (! mainMouse.isDragging())
2595         mainMouse.triggerFakeMove();
2596 }
2597 
beginDragAutoRepeat(int interval)2598 void JUCE_CALLTYPE Component::beginDragAutoRepeat (int interval)
2599 {
2600     Desktop::getInstance().beginDragAutoRepeat (interval);
2601 }
2602 
2603 //==============================================================================
broughtToFront()2604 void Component::broughtToFront()
2605 {
2606 }
2607 
internalBroughtToFront()2608 void Component::internalBroughtToFront()
2609 {
2610     if (flags.hasHeavyweightPeerFlag)
2611         Desktop::getInstance().componentBroughtToFront (this);
2612 
2613     BailOutChecker checker (this);
2614     broughtToFront();
2615 
2616     if (checker.shouldBailOut())
2617         return;
2618 
2619     componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentBroughtToFront (*this); });
2620 
2621     if (checker.shouldBailOut())
2622         return;
2623 
2624     // When brought to the front and there's a modal component blocking this one,
2625     // we need to bring the modal one to the front instead..
2626     if (auto* cm = getCurrentlyModalComponent())
2627         if (cm->getTopLevelComponent() != getTopLevelComponent())
2628             ModalComponentManager::getInstance()->bringModalComponentsToFront (false); // very important that this is false, otherwise in Windows,
2629                                                                                        // non-front components can't get focus when another modal comp is
2630                                                                                        // active, and therefore can't receive mouse-clicks
2631 }
2632 
2633 //==============================================================================
focusGained(FocusChangeType)2634 void Component::focusGained (FocusChangeType)   {}
focusLost(FocusChangeType)2635 void Component::focusLost (FocusChangeType)     {}
focusOfChildComponentChanged(FocusChangeType)2636 void Component::focusOfChildComponentChanged (FocusChangeType) {}
2637 
internalFocusGain(FocusChangeType cause)2638 void Component::internalFocusGain (FocusChangeType cause)
2639 {
2640     internalFocusGain (cause, WeakReference<Component> (this));
2641 }
2642 
internalFocusGain(FocusChangeType cause,const WeakReference<Component> & safePointer)2643 void Component::internalFocusGain (FocusChangeType cause, const WeakReference<Component>& safePointer)
2644 {
2645     focusGained (cause);
2646 
2647     if (safePointer != nullptr)
2648         internalChildFocusChange (cause, safePointer);
2649 }
2650 
internalFocusLoss(FocusChangeType cause)2651 void Component::internalFocusLoss (FocusChangeType cause)
2652 {
2653     const WeakReference<Component> safePointer (this);
2654 
2655     focusLost (cause);
2656 
2657     if (safePointer != nullptr)
2658         internalChildFocusChange (cause, safePointer);
2659 }
2660 
internalChildFocusChange(FocusChangeType cause,const WeakReference<Component> & safePointer)2661 void Component::internalChildFocusChange (FocusChangeType cause, const WeakReference<Component>& safePointer)
2662 {
2663     const bool childIsNowFocused = hasKeyboardFocus (true);
2664 
2665     if (flags.childCompFocusedFlag != childIsNowFocused)
2666     {
2667         flags.childCompFocusedFlag = childIsNowFocused;
2668 
2669         focusOfChildComponentChanged (cause);
2670 
2671         if (safePointer == nullptr)
2672             return;
2673     }
2674 
2675     if (parentComponent != nullptr)
2676         parentComponent->internalChildFocusChange (cause, WeakReference<Component> (parentComponent));
2677 }
2678 
setWantsKeyboardFocus(bool wantsFocus)2679 void Component::setWantsKeyboardFocus (bool wantsFocus) noexcept
2680 {
2681     flags.wantsFocusFlag = wantsFocus;
2682 }
2683 
setMouseClickGrabsKeyboardFocus(bool shouldGrabFocus)2684 void Component::setMouseClickGrabsKeyboardFocus (bool shouldGrabFocus)
2685 {
2686     flags.dontFocusOnMouseClickFlag = ! shouldGrabFocus;
2687 }
2688 
getMouseClickGrabsKeyboardFocus() const2689 bool Component::getMouseClickGrabsKeyboardFocus() const noexcept
2690 {
2691     return ! flags.dontFocusOnMouseClickFlag;
2692 }
2693 
getWantsKeyboardFocus() const2694 bool Component::getWantsKeyboardFocus() const noexcept
2695 {
2696     return flags.wantsFocusFlag && ! flags.isDisabledFlag;
2697 }
2698 
setFocusContainer(bool shouldBeFocusContainer)2699 void Component::setFocusContainer (bool shouldBeFocusContainer) noexcept
2700 {
2701     flags.isFocusContainerFlag = shouldBeFocusContainer;
2702 }
2703 
isFocusContainer() const2704 bool Component::isFocusContainer() const noexcept
2705 {
2706     return flags.isFocusContainerFlag;
2707 }
2708 
2709 static const Identifier juce_explicitFocusOrderId ("_jexfo");
2710 
getExplicitFocusOrder() const2711 int Component::getExplicitFocusOrder() const
2712 {
2713     return properties [juce_explicitFocusOrderId];
2714 }
2715 
setExplicitFocusOrder(int newFocusOrderIndex)2716 void Component::setExplicitFocusOrder (int newFocusOrderIndex)
2717 {
2718     properties.set (juce_explicitFocusOrderId, newFocusOrderIndex);
2719 }
2720 
createFocusTraverser()2721 KeyboardFocusTraverser* Component::createFocusTraverser()
2722 {
2723     if (flags.isFocusContainerFlag || parentComponent == nullptr)
2724         return new KeyboardFocusTraverser();
2725 
2726     return parentComponent->createFocusTraverser();
2727 }
2728 
takeKeyboardFocus(FocusChangeType cause)2729 void Component::takeKeyboardFocus (FocusChangeType cause)
2730 {
2731     // give the focus to this component
2732     if (currentlyFocusedComponent != this)
2733     {
2734         // get the focus onto our desktop window
2735         if (auto* peer = getPeer())
2736         {
2737             const WeakReference<Component> safePointer (this);
2738             peer->grabFocus();
2739 
2740             if (peer->isFocused() && currentlyFocusedComponent != this)
2741             {
2742                 WeakReference<Component> componentLosingFocus (currentlyFocusedComponent);
2743                 currentlyFocusedComponent = this;
2744 
2745                 Desktop::getInstance().triggerFocusCallback();
2746 
2747                 // call this after setting currentlyFocusedComponent so that the one that's
2748                 // losing it has a chance to see where focus is going
2749                 if (componentLosingFocus != nullptr)
2750                     componentLosingFocus->internalFocusLoss (cause);
2751 
2752                 if (currentlyFocusedComponent == this)
2753                     internalFocusGain (cause, safePointer);
2754             }
2755         }
2756     }
2757 }
2758 
grabFocusInternal(FocusChangeType cause,bool canTryParent)2759 void Component::grabFocusInternal (FocusChangeType cause, bool canTryParent)
2760 {
2761     if (isShowing())
2762     {
2763         if (flags.wantsFocusFlag && (isEnabled() || parentComponent == nullptr))
2764         {
2765             takeKeyboardFocus (cause);
2766         }
2767         else
2768         {
2769             if (isParentOf (currentlyFocusedComponent)
2770                  && currentlyFocusedComponent->isShowing())
2771             {
2772                 // do nothing if the focused component is actually a child of ours..
2773             }
2774             else
2775             {
2776                 // find the default child component..
2777                 std::unique_ptr<KeyboardFocusTraverser> traverser (createFocusTraverser());
2778 
2779                 if (traverser != nullptr)
2780                 {
2781                     auto* defaultComp = traverser->getDefaultComponent (this);
2782                     traverser.reset();
2783 
2784                     if (defaultComp != nullptr)
2785                     {
2786                         defaultComp->grabFocusInternal (cause, false);
2787                         return;
2788                     }
2789                 }
2790 
2791                 if (canTryParent && parentComponent != nullptr)
2792                 {
2793                     // if no children want it and we're allowed to try our parent comp,
2794                     // then pass up to parent, which will try our siblings.
2795                     parentComponent->grabFocusInternal (cause, true);
2796                 }
2797             }
2798         }
2799     }
2800 }
2801 
grabKeyboardFocus()2802 void Component::grabKeyboardFocus()
2803 {
2804     // if component methods are being called from threads other than the message
2805     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
2806     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
2807 
2808     grabFocusInternal (focusChangedDirectly, true);
2809 
2810     // A component can only be focused when it's actually on the screen!
2811     // If this fails then you're probably trying to grab the focus before you've
2812     // added the component to a parent or made it visible. Or maybe one of its parent
2813     // components isn't yet visible.
2814     jassert (isShowing() || isOnDesktop());
2815 }
2816 
moveKeyboardFocusToSibling(bool moveToNext)2817 void Component::moveKeyboardFocusToSibling (bool moveToNext)
2818 {
2819     // if component methods are being called from threads other than the message
2820     // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
2821     JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
2822 
2823     if (parentComponent != nullptr)
2824     {
2825         std::unique_ptr<KeyboardFocusTraverser> traverser (createFocusTraverser());
2826 
2827         if (traverser != nullptr)
2828         {
2829             auto* nextComp = moveToNext ? traverser->getNextComponent (this)
2830                                         : traverser->getPreviousComponent (this);
2831             traverser.reset();
2832 
2833             if (nextComp != nullptr)
2834             {
2835                 if (nextComp->isCurrentlyBlockedByAnotherModalComponent())
2836                 {
2837                     const WeakReference<Component> nextCompPointer (nextComp);
2838                     internalModalInputAttempt();
2839 
2840                     if (nextCompPointer == nullptr || nextComp->isCurrentlyBlockedByAnotherModalComponent())
2841                         return;
2842                 }
2843 
2844                 nextComp->grabFocusInternal (focusChangedByTabKey, true);
2845                 return;
2846             }
2847         }
2848 
2849         parentComponent->moveKeyboardFocusToSibling (moveToNext);
2850     }
2851 }
2852 
hasKeyboardFocus(bool trueIfChildIsFocused) const2853 bool Component::hasKeyboardFocus (bool trueIfChildIsFocused) const
2854 {
2855     return (currentlyFocusedComponent == this)
2856             || (trueIfChildIsFocused && isParentOf (currentlyFocusedComponent));
2857 }
2858 
getCurrentlyFocusedComponent()2859 Component* JUCE_CALLTYPE Component::getCurrentlyFocusedComponent() noexcept
2860 {
2861     return currentlyFocusedComponent;
2862 }
2863 
unfocusAllComponents()2864 void JUCE_CALLTYPE Component::unfocusAllComponents()
2865 {
2866     if (auto* c = getCurrentlyFocusedComponent())
2867         c->giveAwayFocus (true);
2868 }
2869 
giveAwayFocus(bool sendFocusLossEvent)2870 void Component::giveAwayFocus (bool sendFocusLossEvent)
2871 {
2872     auto* componentLosingFocus = currentlyFocusedComponent;
2873     currentlyFocusedComponent = nullptr;
2874 
2875     if (sendFocusLossEvent && componentLosingFocus != nullptr)
2876         componentLosingFocus->internalFocusLoss (focusChangedDirectly);
2877 
2878     Desktop::getInstance().triggerFocusCallback();
2879 }
2880 
2881 //==============================================================================
isEnabled() const2882 bool Component::isEnabled() const noexcept
2883 {
2884     return (! flags.isDisabledFlag)
2885             && (parentComponent == nullptr || parentComponent->isEnabled());
2886 }
2887 
setEnabled(bool shouldBeEnabled)2888 void Component::setEnabled (bool shouldBeEnabled)
2889 {
2890     if (flags.isDisabledFlag == shouldBeEnabled)
2891     {
2892         flags.isDisabledFlag = ! shouldBeEnabled;
2893 
2894         // if any parent components are disabled, setting our flag won't make a difference,
2895         // so no need to send a change message
2896         if (parentComponent == nullptr || parentComponent->isEnabled())
2897             sendEnablementChangeMessage();
2898 
2899         BailOutChecker checker (this);
2900         componentListeners.callChecked (checker, [this] (ComponentListener& l) { l.componentEnablementChanged (*this); });
2901     }
2902 }
2903 
enablementChanged()2904 void Component::enablementChanged() {}
2905 
sendEnablementChangeMessage()2906 void Component::sendEnablementChangeMessage()
2907 {
2908     const WeakReference<Component> safePointer (this);
2909 
2910     enablementChanged();
2911 
2912     if (safePointer == nullptr)
2913         return;
2914 
2915     for (int i = getNumChildComponents(); --i >= 0;)
2916     {
2917         if (auto* c = getChildComponent (i))
2918         {
2919             c->sendEnablementChangeMessage();
2920 
2921             if (safePointer == nullptr)
2922                 return;
2923         }
2924     }
2925 }
2926 
2927 //==============================================================================
isMouseOver(bool includeChildren) const2928 bool Component::isMouseOver (bool includeChildren) const
2929 {
2930     for (auto& ms : Desktop::getInstance().getMouseSources())
2931     {
2932         auto* c = ms.getComponentUnderMouse();
2933 
2934         if (c == this || (includeChildren && isParentOf (c)))
2935             if (ms.isDragging() || ! (ms.isTouch() || ms.isPen()))
2936                 if (c->reallyContains (c->getLocalPoint (nullptr, ms.getScreenPosition()).roundToInt(), false))
2937                     return true;
2938     }
2939 
2940     return false;
2941 }
2942 
isMouseButtonDown(bool includeChildren) const2943 bool Component::isMouseButtonDown (bool includeChildren) const
2944 {
2945     for (auto& ms : Desktop::getInstance().getMouseSources())
2946     {
2947         auto* c = ms.getComponentUnderMouse();
2948 
2949         if (c == this || (includeChildren && isParentOf (c)))
2950             if (ms.isDragging())
2951                 return true;
2952     }
2953 
2954     return false;
2955 }
2956 
isMouseOverOrDragging(bool includeChildren) const2957 bool Component::isMouseOverOrDragging (bool includeChildren) const
2958 {
2959     for (auto& ms : Desktop::getInstance().getMouseSources())
2960     {
2961         auto* c = ms.getComponentUnderMouse();
2962 
2963         if (c == this || (includeChildren && isParentOf (c)))
2964             if (ms.isDragging() || ! ms.isTouch())
2965                 return true;
2966     }
2967 
2968     return false;
2969 }
2970 
isMouseButtonDownAnywhere()2971 bool JUCE_CALLTYPE Component::isMouseButtonDownAnywhere() noexcept
2972 {
2973     return ModifierKeys::currentModifiers.isAnyMouseButtonDown();
2974 }
2975 
getMouseXYRelative() const2976 Point<int> Component::getMouseXYRelative() const
2977 {
2978     return getLocalPoint (nullptr, Desktop::getMousePosition());
2979 }
2980 
2981 //==============================================================================
addKeyListener(KeyListener * newListener)2982 void Component::addKeyListener (KeyListener* newListener)
2983 {
2984     if (keyListeners == nullptr)
2985         keyListeners.reset (new Array<KeyListener*>());
2986 
2987     keyListeners->addIfNotAlreadyThere (newListener);
2988 }
2989 
removeKeyListener(KeyListener * listenerToRemove)2990 void Component::removeKeyListener (KeyListener* listenerToRemove)
2991 {
2992     if (keyListeners != nullptr)
2993         keyListeners->removeFirstMatchingValue (listenerToRemove);
2994 }
2995 
keyPressed(const KeyPress &)2996 bool Component::keyPressed (const KeyPress&)            { return false; }
keyStateChanged(bool)2997 bool Component::keyStateChanged (bool /*isKeyDown*/)    { return false; }
2998 
modifierKeysChanged(const ModifierKeys & modifiers)2999 void Component::modifierKeysChanged (const ModifierKeys& modifiers)
3000 {
3001     if (parentComponent != nullptr)
3002         parentComponent->modifierKeysChanged (modifiers);
3003 }
3004 
internalModifierKeysChanged()3005 void Component::internalModifierKeysChanged()
3006 {
3007     sendFakeMouseMove();
3008     modifierKeysChanged (ModifierKeys::currentModifiers);
3009 }
3010 
3011 //==============================================================================
BailOutChecker(Component * component)3012 Component::BailOutChecker::BailOutChecker (Component* component)
3013     : safePointer (component)
3014 {
3015     jassert (component != nullptr);
3016 }
3017 
shouldBailOut() const3018 bool Component::BailOutChecker::shouldBailOut() const noexcept
3019 {
3020     return safePointer == nullptr;
3021 }
3022 
3023 } // namespace juce
3024