From 27f1901fe63e2f13bf437ab407e988df88452cea Mon Sep 17 00:00:00 2001 From: jules Date: Sat, 7 Jul 2012 15:13:46 +0100 Subject: [PATCH] Refactored Path::addBubble, BubbleMessageComponent and BubbleComponent classes to work better and avoid duplicated code. --- .../Project/jucer_ProjectContentComponent.cpp | 2 +- .../Project/jucer_ProjectContentComponent.h | 2 +- modules/juce_graphics/geometry/juce_Path.cpp | 128 ++++++++---------- modules/juce_graphics/geometry/juce_Path.h | 31 ++--- .../layout/juce_ComponentAnimator.cpp | 10 +- .../lookandfeel/juce_LookAndFeel.cpp | 23 +--- .../misc/juce_BubbleComponent.cpp | 127 ++++++----------- .../misc/juce_BubbleComponent.h | 7 +- .../windows/juce_CallOutBox.cpp | 62 +-------- .../misc/juce_BubbleMessageComponent.cpp | 54 ++++---- .../misc/juce_BubbleMessageComponent.h | 6 +- 11 files changed, 160 insertions(+), 292 deletions(-) diff --git a/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.cpp b/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.cpp index 78103553e5..b5e6c1e4a7 100644 --- a/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.cpp +++ b/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.cpp @@ -507,7 +507,7 @@ bool ProjectContentComponent::reinvokeCommandAfterClosingPropertyEditors (const return false; } -void ProjectContentComponent::showBubbleMessage (const Point& pos, const String& text) +void ProjectContentComponent::showBubbleMessage (const Rectangle& pos, const String& text) { addChildComponent (&bubbleMessage); bubbleMessage.setAlwaysOnTop (true); diff --git a/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.h b/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.h index 2c844366cc..c05d47f4fa 100644 --- a/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.h +++ b/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.h @@ -58,7 +58,7 @@ public: void updateMissingFileStatuses(); virtual void createProjectTabs(); - void showBubbleMessage (const Point& pos, const String& text); + void showBubbleMessage (const Rectangle& pos, const String& text); void changeListenerCallback (ChangeBroadcaster*); diff --git a/modules/juce_graphics/geometry/juce_Path.cpp b/modules/juce_graphics/geometry/juce_Path.cpp index 177605a81f..4c0fb03031 100644 --- a/modules/juce_graphics/geometry/juce_Path.cpp +++ b/modules/juce_graphics/geometry/juce_Path.cpp @@ -688,80 +688,66 @@ void Path::addStar (const Point& centre, const int numberOfPoints, } } -void Path::addBubble (float x, float y, - float w, float h, - float cs, - float tipX, - float tipY, - int whichSide, - float arrowPos, - float arrowWidth) +void Path::addBubble (const Rectangle& bodyArea, + const Rectangle& maximumArea, + const Point& arrowTip, + const float cornerSize, + const float arrowBaseWidth) { - if (w > 1.0f && h > 1.0f) + const float cornerSize2 = 2.0f * cornerSize; + + startNewSubPath (bodyArea.getX() + cornerSize, bodyArea.getY()); + + const float targetLimitX = bodyArea.getX() + cornerSize + arrowBaseWidth; + const float targetLimitW = bodyArea.getWidth() - cornerSize2 - arrowBaseWidth * 2.0f; + + const float targetLimitY = bodyArea.getY() + cornerSize + arrowBaseWidth; + const float targetLimitH = bodyArea.getHeight() - cornerSize2 - arrowBaseWidth * 2.0f; + + if (Rectangle (targetLimitX, maximumArea.getY(), + targetLimitW, bodyArea.getY() - maximumArea.getY()).contains (arrowTip)) { - cs = jmin (cs, w * 0.5f, h * 0.5f); - const float cs2 = 2.0f * cs; - - startNewSubPath (x + cs, y); - - if (whichSide == 0) - { - const float halfArrowW = jmin (arrowWidth, w - cs2) * 0.5f; - const float arrowX1 = x + cs + jmax (0.0f, (w - cs2 - arrowWidth) * arrowPos - halfArrowW); - lineTo (arrowX1, y); - lineTo (tipX, tipY); - lineTo (arrowX1 + halfArrowW * 2.0f, y); - } - - lineTo (x + w - cs, y); - - if (cs > 0.0f) - addArc (x + w - cs2, y, cs2, cs2, 0, float_Pi * 0.5f); - - if (whichSide == 3) - { - const float halfArrowH = jmin (arrowWidth, h - cs2) * 0.5f; - const float arrowY1 = y + cs + jmax (0.0f, (h - cs2 - arrowWidth) * arrowPos - halfArrowH); - lineTo (x + w, arrowY1); - lineTo (tipX, tipY); - lineTo (x + w, arrowY1 + halfArrowH * 2.0f); - } - - lineTo (x + w, y + h - cs); - - if (cs > 0.0f) - addArc (x + w - cs2, y + h - cs2, cs2, cs2, float_Pi * 0.5f, float_Pi); - - if (whichSide == 2) - { - const float halfArrowW = jmin (arrowWidth, w - cs2) * 0.5f; - const float arrowX1 = x + cs + jmax (0.0f, (w - cs2 - arrowWidth) * arrowPos - halfArrowW); - lineTo (arrowX1 + halfArrowW * 2.0f, y + h); - lineTo (tipX, tipY); - lineTo (arrowX1, y + h); - } - - lineTo (x + cs, y + h); - - if (cs > 0.0f) - addArc (x, y + h - cs2, cs2, cs2, float_Pi, float_Pi * 1.5f); - - if (whichSide == 1) - { - const float halfArrowH = jmin (arrowWidth, h - cs2) * 0.5f; - const float arrowY1 = y + cs + jmax (0.0f, (h - cs2 - arrowWidth) * arrowPos - halfArrowH); - lineTo (x, arrowY1 + halfArrowH * 2.0f); - lineTo (tipX, tipY); - lineTo (x, arrowY1); - } - - lineTo (x, y + cs); - - if (cs > 0.0f) - addArc (x, y, cs2, cs2, float_Pi * 1.5f, float_Pi * 2.0f - PathHelpers::ellipseAngularIncrement); - - closeSubPath(); + lineTo (arrowTip.x - arrowBaseWidth, bodyArea.getY()); + lineTo (arrowTip.x, arrowTip.y); + lineTo (arrowTip.x + arrowBaseWidth, bodyArea.getY()); } + + lineTo (bodyArea.getRight() - cornerSize, bodyArea.getY()); + addArc (bodyArea.getRight() - cornerSize2, bodyArea.getY(), cornerSize2, cornerSize2, 0, float_Pi * 0.5f); + + if (Rectangle (bodyArea.getRight(), targetLimitY, + maximumArea.getRight() - bodyArea.getRight(), targetLimitH).contains (arrowTip)) + { + lineTo (bodyArea.getRight(), arrowTip.y - arrowBaseWidth); + lineTo (arrowTip.x, arrowTip.y); + lineTo (bodyArea.getRight(), arrowTip.y + arrowBaseWidth); + } + + lineTo (bodyArea.getRight(), bodyArea.getBottom() - cornerSize); + addArc (bodyArea.getRight() - cornerSize2, bodyArea.getBottom() - cornerSize2, cornerSize2, cornerSize2, float_Pi * 0.5f, float_Pi); + + if (Rectangle (targetLimitX, bodyArea.getBottom(), + targetLimitW, maximumArea.getBottom() - bodyArea.getBottom()).contains (arrowTip)) + { + lineTo (arrowTip.x + arrowBaseWidth, bodyArea.getBottom()); + lineTo (arrowTip.x, arrowTip.y); + lineTo (arrowTip.x - arrowBaseWidth, bodyArea.getBottom()); + } + + lineTo (bodyArea.getX() + cornerSize, bodyArea.getBottom()); + addArc (bodyArea.getX(), bodyArea.getBottom() - cornerSize2, cornerSize2, cornerSize2, float_Pi, float_Pi * 1.5f); + + if (Rectangle (maximumArea.getX(), targetLimitY, bodyArea.getX() - maximumArea.getX(), targetLimitH).contains (arrowTip)) + { + lineTo (bodyArea.getX(), arrowTip.y + arrowBaseWidth); + lineTo (arrowTip.x, arrowTip.y); + lineTo (bodyArea.getX(), arrowTip.y - arrowBaseWidth); + } + + lineTo (bodyArea.getX(), bodyArea.getY() + cornerSize); + addArc (bodyArea.getX(), bodyArea.getY(), cornerSize2, cornerSize2, float_Pi * 1.5f, float_Pi * 2.0f - 0.05f); + + closeSubPath(); } void Path::addPath (const Path& other) diff --git a/modules/juce_graphics/geometry/juce_Path.h b/modules/juce_graphics/geometry/juce_Path.h index 1192eb8645..d5cfab4448 100644 --- a/modules/juce_graphics/geometry/juce_Path.h +++ b/modules/juce_graphics/geometry/juce_Path.h @@ -519,26 +519,19 @@ public: /** Adds a speech-bubble shape to the path. - @param bodyX the left of the main body area of the bubble - @param bodyY the top of the main body area of the bubble - @param bodyW the width of the main body area of the bubble - @param bodyH the height of the main body area of the bubble - @param cornerSize the amount by which to round off the corners of the main body rectangle - @param arrowTipX the x position that the tip of the arrow should connect to - @param arrowTipY the y position that the tip of the arrow should connect to - @param whichSide the side to connect the arrow to: 0 = top, 1 = left, 2 = bottom, 3 = right - @param arrowPositionAlongEdgeProportional how far along the edge of the main rectangle the - arrow's base should be - this is a proportional distance between 0 and 1.0 - @param arrowWidth how wide the base of the arrow should be where it joins the main rectangle + @param bodyArea the area of the body of the bubble shape + @param maximumArea an area which encloses the body area and defines the limits within which + the arrow tip can be drawn - if the tip lies outside this area, the bubble + will be drawn without an arrow + @param arrowTipPosition the location of the tip of the arrow + @param cornerSize the size of the rounded corners + @param arrowBaseWidth the width of the base of the arrow where it joins the main rectangle */ - void addBubble (float bodyX, float bodyY, - float bodyW, float bodyH, - float cornerSize, - float arrowTipX, - float arrowTipY, - int whichSide, - float arrowPositionAlongEdgeProportional, - float arrowWidth); + void addBubble (const Rectangle& bodyArea, + const Rectangle& maximumArea, + const Point& arrowTipPosition, + const float cornerSize, + const float arrowBaseWidth); /** Adds another path to this one. diff --git a/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp b/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp index f3a95bbb4c..1220a4f71d 100644 --- a/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp +++ b/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp @@ -46,11 +46,11 @@ public: isMoving = (finalBounds != component->getBounds()); isChangingAlpha = (finalAlpha != component->getAlpha()); - left = component->getX(); - top = component->getY(); - right = component->getRight(); - bottom = component->getBottom(); - alpha = component->getAlpha(); + left = component->getX(); + top = component->getY(); + right = component->getRight(); + bottom = component->getBottom(); + alpha = component->getAlpha(); const double invTotalDistance = 4.0 / (startSpeed_ + endSpeed_ + 2.0); startSpeed = jmax (0.0, startSpeed_ * invTotalDistance); diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp index 662765c540..461fa86f44 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp @@ -949,26 +949,13 @@ void LookAndFeel::drawBubble (Graphics& g, float boxX, float boxY, float boxW, float boxH) { - int side = 0; + const Rectangle body (boxX, boxY, boxW, boxH); - if (tipX < boxX) - side = 1; - else if (tipX > boxX + boxW) - side = 3; - else if (tipY > boxY + boxH) - side = 2; - - const float indent = 2.0f; Path p; - p.addBubble (boxX + indent, - boxY + indent, - boxW - indent * 2.0f, - boxH - indent * 2.0f, - 5.0f, - tipX, tipY, - side, - 0.5f, - jmin (15.0f, boxW * 0.3f, boxH * 0.3f)); + p.addBubble (body, + body.getUnion (Rectangle (tipX, tipY, 1.0f, 1.0f)), + Point (tipX, tipY), + 5.0f, jmin (15.0f, boxW * 0.2f, boxH * 0.2f)); //xxx need to take comp as param for colour g.setColour (findColour (TooltipWindow::backgroundColourId).withAlpha (0.9f)); diff --git a/modules/juce_gui_basics/misc/juce_BubbleComponent.cpp b/modules/juce_gui_basics/misc/juce_BubbleComponent.cpp index bfd95c08f0..08b238b100 100644 --- a/modules/juce_gui_basics/misc/juce_BubbleComponent.cpp +++ b/modules/juce_gui_basics/misc/juce_BubbleComponent.cpp @@ -24,10 +24,7 @@ */ BubbleComponent::BubbleComponent() - : side (0), - allowablePlacements (above | below | left | right), - arrowTipX (0.0f), - arrowTipY (0.0f) + : allowablePlacements (above | below | left | right) { setInterceptsMouseClicks (false, false); @@ -42,41 +39,14 @@ BubbleComponent::~BubbleComponent() //============================================================================== void BubbleComponent::paint (Graphics& g) { - int x = content.getX(); - int y = content.getY(); - int w = content.getWidth(); - int h = content.getHeight(); + getLookAndFeel().drawBubble (g, arrowTip.x, arrowTip.y, + (float) content.getX(), (float) content.getY(), + (float) content.getWidth(), (float) content.getHeight()); - int cw, ch; - getContentSize (cw, ch); + g.setOrigin (content.getX(), content.getY()); + g.reduceClipRegion (0, 0, content.getWidth(), content.getHeight()); - if (side == 3) - x += w - cw; - else if (side != 1) - x += (w - cw) / 2; - - w = cw; - - if (side == 2) - y += h - ch; - else if (side != 0) - y += (h - ch) / 2; - - h = ch; - - getLookAndFeel().drawBubble (g, arrowTipX, arrowTipY, - (float) x, (float) y, - (float) w, (float) h); - - const int cx = x + (w - cw) / 2; - const int cy = y + (h - ch) / 2; - - const int indent = 3; - - g.setOrigin (cx + indent, cy + indent); - g.reduceClipRegion (0, 0, cw - indent * 2, ch - indent * 2); - - paintContent (g, cw - indent * 2, ch - indent * 2); + paintContent (g, content.getWidth(), content.getHeight()); } //============================================================================== @@ -89,102 +59,89 @@ void BubbleComponent::setPosition (Component* componentToPointTo) { jassert (componentToPointTo != nullptr); - Point pos; - if (getParentComponent() != nullptr) - pos = getParentComponent()->getLocalPoint (componentToPointTo, pos); + setPosition (getParentComponent()->getLocalArea (componentToPointTo, componentToPointTo->getLocalBounds())); else - pos = componentToPointTo->localPointToGlobal (pos); - - setPosition (Rectangle (pos.x, pos.y, componentToPointTo->getWidth(), componentToPointTo->getHeight())); + setPosition (componentToPointTo->getScreenBounds()); } -void BubbleComponent::setPosition (const int arrowTipX_, - const int arrowTipY_) +void BubbleComponent::setPosition (const Point& pos) { - setPosition (Rectangle (arrowTipX_, arrowTipY_, 1, 1)); + setPosition (Rectangle (pos.x, pos.y, 1, 1)); } //============================================================================== void BubbleComponent::setPosition (const Rectangle& rectangleToPointTo) { - Rectangle availableSpace (getParentComponent() != nullptr ? getParentComponent()->getLocalBounds() - : getParentMonitorArea()); - int x = 0; - int y = 0; - int w = 150; - int h = 30; + const int edgeSpace = 15; + const int arrowLength = 10; - getContentSize (w, h); - w += 30; - h += 30; + { + int contentW = 150, contentH = 30; + getContentSize (contentW, contentH); + content.setBounds (edgeSpace, edgeSpace, contentW, contentH); + } - const float edgeIndent = 2.0f; - const int arrowLength = jmin (10, h / 3, w / 3); + int totalW = content.getWidth() + edgeSpace * 2; + int totalH = content.getHeight() + edgeSpace * 2; + int targetX, targetY; - int spaceAbove = ((allowablePlacements & above) != 0) ? jmax (0, rectangleToPointTo.getY() - availableSpace.getY()) : -1; + const Rectangle availableSpace (getParentComponent() != nullptr ? getParentComponent()->getLocalBounds() + : getParentMonitorArea()); + + int spaceAbove = ((allowablePlacements & above) != 0) ? jmax (0, rectangleToPointTo.getY() - availableSpace.getY()) : -1; int spaceBelow = ((allowablePlacements & below) != 0) ? jmax (0, availableSpace.getBottom() - rectangleToPointTo.getBottom()) : -1; - int spaceLeft = ((allowablePlacements & left) != 0) ? jmax (0, rectangleToPointTo.getX() - availableSpace.getX()) : -1; - int spaceRight = ((allowablePlacements & right) != 0) ? jmax (0, availableSpace.getRight() - rectangleToPointTo.getRight()) : -1; + int spaceLeft = ((allowablePlacements & left) != 0) ? jmax (0, rectangleToPointTo.getX() - availableSpace.getX()) : -1; + int spaceRight = ((allowablePlacements & right) != 0) ? jmax (0, availableSpace.getRight() - rectangleToPointTo.getRight()) : -1; // look at whether the component is elongated, and if so, try to position next to its longer dimension. if (rectangleToPointTo.getWidth() > rectangleToPointTo.getHeight() * 2 - && (spaceAbove > h + 20 || spaceBelow > h + 20)) + && (spaceAbove > totalH + 20 || spaceBelow > totalH + 20)) { spaceLeft = spaceRight = 0; } else if (rectangleToPointTo.getWidth() < rectangleToPointTo.getHeight() / 2 - && (spaceLeft > w + 20 || spaceRight > w + 20)) + && (spaceLeft > totalW + 20 || spaceRight > totalW + 20)) { spaceAbove = spaceBelow = 0; } if (jmax (spaceAbove, spaceBelow) >= jmax (spaceLeft, spaceRight)) { - x = rectangleToPointTo.getX() + (rectangleToPointTo.getWidth() - w) / 2; - arrowTipX = w * 0.5f; - content.setSize (w, h - arrowLength); + targetX = rectangleToPointTo.getCentre().x; + arrowTip.x = totalW * 0.5f; if (spaceAbove >= spaceBelow) { // above - y = rectangleToPointTo.getY() - h; - content.setPosition (0, 0); - arrowTipY = h - edgeIndent; - side = 2; + targetY = rectangleToPointTo.getY(); + arrowTip.y = content.getBottom() + arrowLength; } else { // below - y = rectangleToPointTo.getBottom(); - content.setPosition (0, arrowLength); - arrowTipY = edgeIndent; - side = 0; + targetY = rectangleToPointTo.getBottom(); + arrowTip.y = content.getY() - arrowLength; } } else { - y = rectangleToPointTo.getY() + (rectangleToPointTo.getHeight() - h) / 2; - arrowTipY = h * 0.5f; - content.setSize (w - arrowLength, h); + targetY = rectangleToPointTo.getCentre().y; + arrowTip.y = totalH * 0.5f; if (spaceLeft > spaceRight) { // on the left - x = rectangleToPointTo.getX() - w; - content.setPosition (0, 0); - arrowTipX = w - edgeIndent; - side = 3; + targetX = rectangleToPointTo.getX(); + arrowTip.x = content.getRight() + arrowLength; } else { // on the right - x = rectangleToPointTo.getRight(); - content.setPosition (arrowLength, 0); - arrowTipX = edgeIndent; - side = 1; + targetX = rectangleToPointTo.getRight(); + arrowTip.x = content.getX() - arrowLength; } } - setBounds (x, y, w, h); + setBounds (targetX - arrowTip.x, targetY - arrowTip.y, totalW, totalH); } diff --git a/modules/juce_gui_basics/misc/juce_BubbleComponent.h b/modules/juce_gui_basics/misc/juce_BubbleComponent.h index 7695e62581..8fc82624b4 100644 --- a/modules/juce_gui_basics/misc/juce_BubbleComponent.h +++ b/modules/juce_gui_basics/misc/juce_BubbleComponent.h @@ -108,8 +108,7 @@ public: on where there's the most space, honouring any restrictions that were set with setAllowedPlacement(). */ - void setPosition (int arrowTipX, - int arrowTipY); + void setPosition (const Point& arrowTipPosition); /** Moves and resizes the bubble to point at a given rectangle. @@ -145,8 +144,8 @@ public: private: Rectangle content; - int side, allowablePlacements; - float arrowTipX, arrowTipY; + Point arrowTip; + int allowablePlacements; DropShadowEffect shadow; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BubbleComponent); diff --git a/modules/juce_gui_basics/windows/juce_CallOutBox.cpp b/modules/juce_gui_basics/windows/juce_CallOutBox.cpp index 515ec4b7e5..7447698bd8 100644 --- a/modules/juce_gui_basics/windows/juce_CallOutBox.cpp +++ b/modules/juce_gui_basics/windows/juce_CallOutBox.cpp @@ -199,63 +199,9 @@ void CallOutBox::refreshPath() outline.clear(); const float gap = 4.5f; - const float cornerSize = 9.0f; - const float cornerSize2 = 2.0f * cornerSize; - const float arrowBaseWidth = arrowSize * 0.7f; - const Rectangle area (content.getBounds().toFloat().expanded (gap, gap)); - const Point target (targetPoint - getPosition().toFloat()); - - outline.startNewSubPath (area.getX() + cornerSize, area.getY()); - - const float targetLimitX = area.getX() + cornerSize + arrowBaseWidth; - const float targetLimitW = area.getWidth() - cornerSize2 - arrowBaseWidth * 2.0f; - - const float targetLimitY = area.getY() + cornerSize + arrowBaseWidth; - const float targetLimitH = area.getHeight() - cornerSize2 - arrowBaseWidth * 2.0f; - - if (Rectangle (targetLimitX, 1.0f, - targetLimitW, area.getY() - 2.0f).contains (target)) - { - outline.lineTo (target.x - arrowBaseWidth, area.getY()); - outline.lineTo (target.x, target.y); - outline.lineTo (target.x + arrowBaseWidth, area.getY()); - } - - outline.lineTo (area.getRight() - cornerSize, area.getY()); - outline.addArc (area.getRight() - cornerSize2, area.getY(), cornerSize2, cornerSize2, 0, float_Pi * 0.5f); - - if (Rectangle (area.getRight() + 1.0f, targetLimitY, - getWidth() - area.getRight() - 2.0f, targetLimitH).contains (target)) - { - outline.lineTo (area.getRight(), target.y - arrowBaseWidth); - outline.lineTo (target.x, target.y); - outline.lineTo (area.getRight(), target.y + arrowBaseWidth); - } - - outline.lineTo (area.getRight(), area.getBottom() - cornerSize); - outline.addArc (area.getRight() - cornerSize2, area.getBottom() - cornerSize2, cornerSize2, cornerSize2, float_Pi * 0.5f, float_Pi); - - if (Rectangle (targetLimitX, area.getBottom() + 1.0f, - targetLimitW, getHeight() - area.getBottom() - 2.0f).contains (target)) - { - outline.lineTo (target.x + arrowBaseWidth, area.getBottom()); - outline.lineTo (target.x, target.y); - outline.lineTo (target.x - arrowBaseWidth, area.getBottom()); - } - - outline.lineTo (area.getX() + cornerSize, area.getBottom()); - outline.addArc (area.getX(), area.getBottom() - cornerSize2, cornerSize2, cornerSize2, float_Pi, float_Pi * 1.5f); - - if (Rectangle (1.0f, targetLimitY, area.getX() - 2.0f, targetLimitH).contains (target)) - { - outline.lineTo (area.getX(), target.y + arrowBaseWidth); - outline.lineTo (target.x, target.y); - outline.lineTo (area.getX(), target.y - arrowBaseWidth); - } - - outline.lineTo (area.getX(), area.getY() + cornerSize); - outline.addArc (area.getX(), area.getY(), cornerSize2, cornerSize2, float_Pi * 1.5f, float_Pi * 2.0f - 0.05f); - - outline.closeSubPath(); + outline.addBubble (content.getBounds().toFloat().expanded (gap, gap), + getLocalBounds().toFloat(), + targetPoint - getPosition().toFloat(), + 9.0f, arrowSize * 0.7f); } diff --git a/modules/juce_gui_extra/misc/juce_BubbleMessageComponent.cpp b/modules/juce_gui_extra/misc/juce_BubbleMessageComponent.cpp index 4455db62b1..3f3cff3142 100644 --- a/modules/juce_gui_extra/misc/juce_BubbleMessageComponent.cpp +++ b/modules/juce_gui_extra/misc/juce_BubbleMessageComponent.cpp @@ -31,17 +31,16 @@ BubbleMessageComponent::BubbleMessageComponent (int fadeOutLengthMs) BubbleMessageComponent::~BubbleMessageComponent() { - Desktop::getInstance().getAnimator().fadeOut (this, fadeOutLength); } -void BubbleMessageComponent::showAt (int x, int y, +void BubbleMessageComponent::showAt (const Rectangle& pos, const AttributedString& text, const int numMillisecondsBeforeRemoving, const bool removeWhenMouseClicked, const bool deleteSelfAfterUse) { createLayout (text); - setPosition (x, y); + setPosition (pos); init (numMillisecondsBeforeRemoving, removeWhenMouseClicked, deleteSelfAfterUse); } @@ -65,55 +64,56 @@ void BubbleMessageComponent::init (const int numMillisecondsBeforeRemoving, const bool removeWhenMouseClicked, const bool deleteSelfAfterUse) { + setAlpha (1.0f); setVisible (true); - deleteAfterUse = deleteSelfAfterUse; - if (numMillisecondsBeforeRemoving > 0) - expiryTime = Time::getMillisecondCounter() + numMillisecondsBeforeRemoving; - else - expiryTime = 0; - - startTimer (77); + expiryTime = numMillisecondsBeforeRemoving > 0 + ? (Time::getMillisecondCounter() + numMillisecondsBeforeRemoving) : 0; mouseClickCounter = Desktop::getInstance().getMouseButtonClickCounter(); if (! (removeWhenMouseClicked && isShowing())) mouseClickCounter += 0xfffff; + startTimer (77); repaint(); } +const float bubblePaddingX = 20.0f; +const float bubblePaddingY = 14.0f; + void BubbleMessageComponent::getContentSize (int& w, int& h) { - w = 20 + (int) textLayout.getWidth(); - h = 20 + (int) textLayout.getHeight(); + w = (int) (bubblePaddingX + textLayout.getWidth()); + h = (int) (bubblePaddingY + textLayout.getHeight()); } void BubbleMessageComponent::paintContent (Graphics& g, int w, int h) { g.setColour (findColour (TooltipWindow::textColourId)); - textLayout.draw (g, Rectangle (6.0f, 6.0f, w - 12.0f, h - 12.0f)); + textLayout.draw (g, Rectangle (bubblePaddingX / 2.0f, bubblePaddingY / 2.0f, + w - bubblePaddingX, h - bubblePaddingY)); } void BubbleMessageComponent::timerCallback() { if (Desktop::getInstance().getMouseButtonClickCounter() > mouseClickCounter) - { - stopTimer(); + hide (false); + else if (expiryTime != 0 && Time::getMillisecondCounter() > expiryTime) + hide (true); +} + +void BubbleMessageComponent::hide (const bool fadeOut) +{ + stopTimer(); + + if (fadeOut) + Desktop::getInstance().getAnimator().fadeOut (this, fadeOutLength); + else setVisible (false); - if (deleteAfterUse) - delete this; - } - else if (expiryTime != 0 && Time::getMillisecondCounter() > expiryTime) - { - stopTimer(); - - if (deleteAfterUse) - delete this; - else - Desktop::getInstance().getAnimator().fadeOut (this, fadeOutLength); - } + if (deleteAfterUse) + delete this; } diff --git a/modules/juce_gui_extra/misc/juce_BubbleMessageComponent.h b/modules/juce_gui_extra/misc/juce_BubbleMessageComponent.h index 830b9e2be8..a41a3a7c1f 100644 --- a/modules/juce_gui_extra/misc/juce_BubbleMessageComponent.h +++ b/modules/juce_gui_extra/misc/juce_BubbleMessageComponent.h @@ -65,8 +65,7 @@ public: For details about exactly how it decides where to position itself, see BubbleComponent::updatePosition(). - @param x the x co-ordinate of end of the bubble's tail - @param y the y co-ordinate of end of the bubble's tail + @param position the coords of the object to point to @param message the text to display @param numMillisecondsBeforeRemoving how long to leave it on the screen before removing itself from its parent compnent. If this is 0 or less, it @@ -76,7 +75,7 @@ public: @param deleteSelfAfterUse if true, then the component will delete itself after it becomes invisible */ - void showAt (int x, int y, + void showAt (const Rectangle& position, const AttributedString& message, int numMillisecondsBeforeRemoving, bool removeWhenMouseClicked = true, @@ -125,6 +124,7 @@ private: void init (int numMillisecondsBeforeRemoving, bool removeWhenMouseClicked, bool deleteSelfAfterUse); + void hide (bool fadeOut); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BubbleMessageComponent); };