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