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