Browse Source

New class: CallOutBox, which produces a nice popup box - see the juce demo/widgets/buttons/"click to change colour" button for an example. SVG fix.

tags/2021-05-28
Julian Storer 15 years ago
parent
commit
1f21a94753
25 changed files with 907 additions and 400 deletions
  1. +6
    -0
      Builds/Linux/Makefile
  2. +6
    -0
      Builds/MacOSX/Juce.xcodeproj/project.pbxproj
  3. +2
    -0
      Builds/VisualStudio2005/Juce.vcproj
  4. +2
    -0
      Builds/VisualStudio2008/Juce.vcproj
  5. +2
    -0
      Builds/VisualStudio2008_DLL/Juce.vcproj
  6. +6
    -0
      Builds/iPhone/Juce.xcodeproj/project.pbxproj
  7. +4
    -0
      Juce.jucer
  8. +1
    -0
      amalgamation/juce_amalgamated_template.cpp
  9. +2
    -1
      extras/Jucer (experimental)/Source/utility/jucer_ColourPropertyComponent.h
  10. +3
    -1
      extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h
  11. +0
    -206
      extras/Jucer (experimental)/Source/utility/jucer_MiscUtilities.cpp
  12. +0
    -52
      extras/Jucer (experimental)/Source/utility/jucer_MiscUtilities.h
  13. +9
    -22
      extras/juce demo/Source/demos/WidgetsDemo.cpp
  14. +275
    -47
      juce_amalgamated.cpp
  15. +125
    -11
      juce_amalgamated.h
  16. +1
    -1
      src/core/juce_StandardHeader.h
  17. +22
    -0
      src/gui/components/lookandfeel/juce_LookAndFeel.cpp
  18. +4
    -0
      src/gui/components/lookandfeel/juce_LookAndFeel.h
  19. +234
    -0
      src/gui/components/windows/juce_CallOutBox.cpp
  20. +128
    -0
      src/gui/components/windows/juce_CallOutBox.h
  21. +50
    -40
      src/gui/graphics/drawables/juce_DrawableComposite.cpp
  22. +6
    -1
      src/gui/graphics/drawables/juce_DrawableComposite.h
  23. +2
    -7
      src/gui/graphics/drawables/juce_SVGParser.cpp
  24. +14
    -11
      src/gui/graphics/geometry/juce_PathStrokeType.h
  25. +3
    -0
      src/juce_app_includes.h

+ 6
- 0
Builds/Linux/Makefile View File

@@ -213,6 +213,7 @@ OBJECTS := \
$(OBJDIR)/juce_PreferencesPanel_dbc7d503.o \
$(OBJDIR)/juce_SystemTrayIconComponent_fa664512.o \
$(OBJDIR)/juce_AlertWindow_94d8d1f5.o \
$(OBJDIR)/juce_CallOutBox_61f42d7c.o \
$(OBJDIR)/juce_ComponentPeer_1e4b4b08.o \
$(OBJDIR)/juce_DialogWindow_535b27b9.o \
$(OBJDIR)/juce_DocumentWindow_3cb9d4cc.o \
@@ -1222,6 +1223,11 @@ $(OBJDIR)/juce_AlertWindow_94d8d1f5.o: ../../src/gui/components/windows/juce_Ale
@echo "Compiling juce_AlertWindow.cpp"
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
$(OBJDIR)/juce_CallOutBox_61f42d7c.o: ../../src/gui/components/windows/juce_CallOutBox.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling juce_CallOutBox.cpp"
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
$(OBJDIR)/juce_ComponentPeer_1e4b4b08.o: ../../src/gui/components/windows/juce_ComponentPeer.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling juce_ComponentPeer.cpp"


+ 6
- 0
Builds/MacOSX/Juce.xcodeproj/project.pbxproj View File

@@ -182,6 +182,7 @@
D9AAB4AE220010CD526C87D2 = { isa = PBXBuildFile; fileRef = A34C0E63D41CFF5E55FD1D9E; };
C732ADB05901619B14F1D6BB = { isa = PBXBuildFile; fileRef = CC04F253CB70B20B774801A9; };
075D5995E41FDD670ED35E17 = { isa = PBXBuildFile; fileRef = A5AAF4475138358F33D4904A; };
BBE02E8719411C8A7D43A401 = { isa = PBXBuildFile; fileRef = 8AEF18EE9B12D4677F96B709; };
35E3B9684ED968BAC0BC8021 = { isa = PBXBuildFile; fileRef = 2FFDC7636EFC2D7F74590A31; };
736AC4A9DA6515B92644FA02 = { isa = PBXBuildFile; fileRef = 929FEA5458430B7AE23BBB46; };
DFFBADCBC9C7E31B391BA560 = { isa = PBXBuildFile; fileRef = 090907E4FE95EE2B11C1A0E1; };
@@ -736,6 +737,8 @@
740D1808DB934123F05A1598 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_WebBrowserComponent.h; path = ../../src/gui/components/special/juce_WebBrowserComponent.h; sourceTree = SOURCE_ROOT; };
A5AAF4475138358F33D4904A = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_AlertWindow.cpp; path = ../../src/gui/components/windows/juce_AlertWindow.cpp; sourceTree = SOURCE_ROOT; };
72C4FDDDB8602591DD4F7B3B = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AlertWindow.h; path = ../../src/gui/components/windows/juce_AlertWindow.h; sourceTree = SOURCE_ROOT; };
8AEF18EE9B12D4677F96B709 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_CallOutBox.cpp; path = ../../src/gui/components/windows/juce_CallOutBox.cpp; sourceTree = SOURCE_ROOT; };
8F54431CD3A672B1EB8335BE = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_CallOutBox.h; path = ../../src/gui/components/windows/juce_CallOutBox.h; sourceTree = SOURCE_ROOT; };
2FFDC7636EFC2D7F74590A31 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_ComponentPeer.cpp; path = ../../src/gui/components/windows/juce_ComponentPeer.cpp; sourceTree = SOURCE_ROOT; };
12C66C90F3192AFFD6BCEDB6 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ComponentPeer.h; path = ../../src/gui/components/windows/juce_ComponentPeer.h; sourceTree = SOURCE_ROOT; };
929FEA5458430B7AE23BBB46 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_DialogWindow.cpp; path = ../../src/gui/components/windows/juce_DialogWindow.cpp; sourceTree = SOURCE_ROOT; };
@@ -1427,6 +1430,8 @@
48D41BA310DED74E900A5AB0 = { isa = PBXGroup; children = (
A5AAF4475138358F33D4904A,
72C4FDDDB8602591DD4F7B3B,
8AEF18EE9B12D4677F96B709,
8F54431CD3A672B1EB8335BE,
2FFDC7636EFC2D7F74590A31,
12C66C90F3192AFFD6BCEDB6,
929FEA5458430B7AE23BBB46,
@@ -2011,6 +2016,7 @@
D9AAB4AE220010CD526C87D2,
C732ADB05901619B14F1D6BB,
075D5995E41FDD670ED35E17,
BBE02E8719411C8A7D43A401,
35E3B9684ED968BAC0BC8021,
736AC4A9DA6515B92644FA02,
DFFBADCBC9C7E31B391BA560,


+ 2
- 0
Builds/VisualStudio2005/Juce.vcproj View File

@@ -559,6 +559,8 @@
<Filter Name="windows">
<File RelativePath="..\..\src\gui\components\windows\juce_AlertWindow.cpp"/>
<File RelativePath="..\..\src\gui\components\windows\juce_AlertWindow.h"/>
<File RelativePath="..\..\src\gui\components\windows\juce_CallOutBox.cpp"/>
<File RelativePath="..\..\src\gui\components\windows\juce_CallOutBox.h"/>
<File RelativePath="..\..\src\gui\components\windows\juce_ComponentPeer.cpp"/>
<File RelativePath="..\..\src\gui\components\windows\juce_ComponentPeer.h"/>
<File RelativePath="..\..\src\gui\components\windows\juce_DialogWindow.cpp"/>


+ 2
- 0
Builds/VisualStudio2008/Juce.vcproj View File

@@ -559,6 +559,8 @@
<Filter Name="windows">
<File RelativePath="..\..\src\gui\components\windows\juce_AlertWindow.cpp"/>
<File RelativePath="..\..\src\gui\components\windows\juce_AlertWindow.h"/>
<File RelativePath="..\..\src\gui\components\windows\juce_CallOutBox.cpp"/>
<File RelativePath="..\..\src\gui\components\windows\juce_CallOutBox.h"/>
<File RelativePath="..\..\src\gui\components\windows\juce_ComponentPeer.cpp"/>
<File RelativePath="..\..\src\gui\components\windows\juce_ComponentPeer.h"/>
<File RelativePath="..\..\src\gui\components\windows\juce_DialogWindow.cpp"/>


+ 2
- 0
Builds/VisualStudio2008_DLL/Juce.vcproj View File

@@ -561,6 +561,8 @@
<Filter Name="windows">
<File RelativePath="..\..\src\gui\components\windows\juce_AlertWindow.cpp"/>
<File RelativePath="..\..\src\gui\components\windows\juce_AlertWindow.h"/>
<File RelativePath="..\..\src\gui\components\windows\juce_CallOutBox.cpp"/>
<File RelativePath="..\..\src\gui\components\windows\juce_CallOutBox.h"/>
<File RelativePath="..\..\src\gui\components\windows\juce_ComponentPeer.cpp"/>
<File RelativePath="..\..\src\gui\components\windows\juce_ComponentPeer.h"/>
<File RelativePath="..\..\src\gui\components\windows\juce_DialogWindow.cpp"/>


+ 6
- 0
Builds/iPhone/Juce.xcodeproj/project.pbxproj View File

@@ -182,6 +182,7 @@
D9AAB4AE220010CD526C87D2 = { isa = PBXBuildFile; fileRef = A34C0E63D41CFF5E55FD1D9E; };
C732ADB05901619B14F1D6BB = { isa = PBXBuildFile; fileRef = CC04F253CB70B20B774801A9; };
075D5995E41FDD670ED35E17 = { isa = PBXBuildFile; fileRef = A5AAF4475138358F33D4904A; };
BBE02E8719411C8A7D43A401 = { isa = PBXBuildFile; fileRef = 8AEF18EE9B12D4677F96B709; };
35E3B9684ED968BAC0BC8021 = { isa = PBXBuildFile; fileRef = 2FFDC7636EFC2D7F74590A31; };
736AC4A9DA6515B92644FA02 = { isa = PBXBuildFile; fileRef = 929FEA5458430B7AE23BBB46; };
DFFBADCBC9C7E31B391BA560 = { isa = PBXBuildFile; fileRef = 090907E4FE95EE2B11C1A0E1; };
@@ -736,6 +737,8 @@
740D1808DB934123F05A1598 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_WebBrowserComponent.h; path = ../../src/gui/components/special/juce_WebBrowserComponent.h; sourceTree = SOURCE_ROOT; };
A5AAF4475138358F33D4904A = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_AlertWindow.cpp; path = ../../src/gui/components/windows/juce_AlertWindow.cpp; sourceTree = SOURCE_ROOT; };
72C4FDDDB8602591DD4F7B3B = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AlertWindow.h; path = ../../src/gui/components/windows/juce_AlertWindow.h; sourceTree = SOURCE_ROOT; };
8AEF18EE9B12D4677F96B709 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_CallOutBox.cpp; path = ../../src/gui/components/windows/juce_CallOutBox.cpp; sourceTree = SOURCE_ROOT; };
8F54431CD3A672B1EB8335BE = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_CallOutBox.h; path = ../../src/gui/components/windows/juce_CallOutBox.h; sourceTree = SOURCE_ROOT; };
2FFDC7636EFC2D7F74590A31 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_ComponentPeer.cpp; path = ../../src/gui/components/windows/juce_ComponentPeer.cpp; sourceTree = SOURCE_ROOT; };
12C66C90F3192AFFD6BCEDB6 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ComponentPeer.h; path = ../../src/gui/components/windows/juce_ComponentPeer.h; sourceTree = SOURCE_ROOT; };
929FEA5458430B7AE23BBB46 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_DialogWindow.cpp; path = ../../src/gui/components/windows/juce_DialogWindow.cpp; sourceTree = SOURCE_ROOT; };
@@ -1427,6 +1430,8 @@
48D41BA310DED74E900A5AB0 = { isa = PBXGroup; children = (
A5AAF4475138358F33D4904A,
72C4FDDDB8602591DD4F7B3B,
8AEF18EE9B12D4677F96B709,
8F54431CD3A672B1EB8335BE,
2FFDC7636EFC2D7F74590A31,
12C66C90F3192AFFD6BCEDB6,
929FEA5458430B7AE23BBB46,
@@ -2011,6 +2016,7 @@
D9AAB4AE220010CD526C87D2,
C732ADB05901619B14F1D6BB,
075D5995E41FDD670ED35E17,
BBE02E8719411C8A7D43A401,
35E3B9684ED968BAC0BC8021,
736AC4A9DA6515B92644FA02,
DFFBADCBC9C7E31B391BA560,


+ 4
- 0
Juce.jucer View File

@@ -887,6 +887,10 @@
file="src/gui/components/windows/juce_AlertWindow.cpp"/>
<FILE id="q2qARl9MO" name="juce_AlertWindow.h" compile="0" resource="0"
file="src/gui/components/windows/juce_AlertWindow.h"/>
<FILE id="831V8C" name="juce_CallOutBox.cpp" compile="1" resource="0"
file="src/gui/components/windows/juce_CallOutBox.cpp"/>
<FILE id="EbIvE5" name="juce_CallOutBox.h" compile="0" resource="0"
file="src/gui/components/windows/juce_CallOutBox.h"/>
<FILE id="wriH2opIu" name="juce_ComponentPeer.cpp" compile="1" resource="0"
file="src/gui/components/windows/juce_ComponentPeer.cpp"/>
<FILE id="hlmMSNcRL" name="juce_ComponentPeer.h" compile="0" resource="0"


+ 1
- 0
amalgamation/juce_amalgamated_template.cpp View File

@@ -309,6 +309,7 @@
#include "../src/gui/components/special/juce_PreferencesPanel.cpp"
#include "../src/gui/components/special/juce_SystemTrayIconComponent.cpp"
#include "../src/gui/components/windows/juce_AlertWindow.cpp"
#include "../src/gui/components/windows/juce_CallOutBox.cpp"
#include "../src/gui/components/windows/juce_ComponentPeer.cpp"
#include "../src/gui/components/windows/juce_DialogWindow.cpp"
#include "../src/gui/components/windows/juce_DocumentWindow.cpp"


+ 2
- 1
extras/Jucer (experimental)/Source/utility/jucer_ColourPropertyComponent.h View File

@@ -65,7 +65,8 @@ public:
PopupColourSelector colourSelector (colourValue, defaultColour, canResetToDefault);
colourSelector.setSize (300, 400);
CallOutBox::showModal (colourSelector, targetComp, 0 /*targetComp->getTopLevelComponent()*/);
CallOutBox c (colourSelector, *targetComp, 0 /*targetComp->getTopLevelComponent()*/);
c.runModalLoop();
}
void resized()


+ 3
- 1
extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h View File

@@ -650,7 +650,9 @@ public:
undoManager->beginNewTransaction();
PopupFillSelector popup (fillState, getDefaultGradient(), imageProvider, project, undoManager);
CallOutBox::showModal (popup, this, 0 /*getTopLevelComponent()*/);
CallOutBox c (popup, *this, 0 /*getTopLevelComponent()*/);
c.runModalLoop();
}
void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { refresh(); }


+ 0
- 206
extras/Jucer (experimental)/Source/utility/jucer_MiscUtilities.cpp View File

@@ -415,209 +415,3 @@ const ColourGradient FillTypeEditorComponent::getDefaultGradient() const
jassert (p != 0);
return p->getDefaultGradient();
}
//==============================================================================
CallOutBox::CallOutBox (Component& content_, const float arrowSize_)
: edge (jmax (20, (int) arrowSize_)), content (content_), arrowSize (arrowSize_)
{
addAndMakeVisible (&content);
}
CallOutBox::~CallOutBox()
{
}
//==============================================================================
void CallOutBox::showModal (Component& content, Component* targetComp, Component* parentComp, float arrowSize)
{
showModal (content,
parentComp,
parentComp == 0 ? targetComp->getParentMonitorArea()
: parentComp->getLocalBounds(),
parentComp == 0 ? targetComp->getScreenBounds()
: (targetComp->getLocalBounds() + targetComp->relativePositionToOtherComponent (parentComp, Point<int>())),
arrowSize);
}
void CallOutBox::showModal (Component& content, Component* parent,
const Rectangle<int>& availableAreaInParent,
const Rectangle<int>& targetAreaInParent, float arrowSize)
{
CallOutBox p (content, arrowSize);
p.updatePosition (targetAreaInParent, availableAreaInParent);
if (parent != 0)
parent->addAndMakeVisible (&p);
else
p.addToDesktop (ComponentPeer::windowIsTemporary);
p.runModalLoop();
}
//==============================================================================
void CallOutBox::paint (Graphics& g)
{
if (background.isNull())
{
DropShadowEffect shadow;
shadow.setShadowProperties (5.0f, 0.4f, 0, 2);
Image im (Image::ARGB, getWidth(), getHeight(), true);
{
Graphics g (im);
g.setColour (Colour::greyLevel (0.23f).withAlpha (0.9f));
g.fillPath (outline);
g.setColour (Colours::white.withAlpha (0.8f));
g.strokePath (outline, PathStrokeType (2.0f));
}
background = Image (Image::ARGB, getWidth(), getHeight(), true);
Graphics g (background);
shadow.applyEffect (im, g);
}
g.setColour (Colours::black);
g.drawImageAt (background, 0, 0);
}
void CallOutBox::resized()
{
content.setTopLeftPosition (edge, edge);
refreshPath();
}
void CallOutBox::moved()
{
refreshPath();
}
void CallOutBox::childBoundsChanged (Component*)
{
updatePosition (targetArea, availableArea);
}
bool CallOutBox::hitTest (int x, int y)
{
return outline.contains ((float) x, (float) y);
}
void CallOutBox::inputAttemptWhenModal()
{
exitModalState (0);
setVisible (false);
}
void CallOutBox::updatePosition (const Rectangle<int>& newTargetArea, const Rectangle<int>& newArea)
{
targetArea = newTargetArea;
availableArea = newArea;
Rectangle<int> bounds (0, 0,
content.getWidth() + edge * 2,
content.getHeight() + edge * 2);
const int hw = bounds.getWidth() / 2;
const int hh = bounds.getHeight() / 2;
const float hwReduced = (float) (hw - edge * 3);
const float hhReduced = (float) (hh - edge * 3);
const float arrowIndent = edge - arrowSize;
Point<float> targets[4] = { Point<float> ((float) targetArea.getCentreX(), (float) targetArea.getBottom()),
Point<float> ((float) targetArea.getRight(), (float) targetArea.getCentreY()),
Point<float> ((float) targetArea.getX(), (float) targetArea.getCentreY()),
Point<float> ((float) targetArea.getCentreX(), (float) targetArea.getY()) };
Line<float> lines[4] = { Line<float> (targets[0].translated (-hwReduced, hh - arrowIndent), targets[0].translated (hwReduced, hh - arrowIndent)),
Line<float> (targets[1].translated (hw - arrowIndent, -hhReduced), targets[1].translated (hw - arrowIndent, hhReduced)),
Line<float> (targets[2].translated (-(hw - arrowIndent), -hhReduced), targets[2].translated (-(hw - arrowIndent), hhReduced)),
Line<float> (targets[3].translated (-hwReduced, -(hh - arrowIndent)), targets[3].translated (hwReduced, -(hh - arrowIndent))) };
const Rectangle<float> reducedArea (newArea.reduced (hw, hh).toFloat());
float bestDist = 1.0e9f;
for (int i = 0; i < 4; ++i)
{
Line<float> constrainedLine (reducedArea.getConstrainedPoint (lines[i].getStart()),
reducedArea.getConstrainedPoint (lines[i].getEnd()));
const Point<float> centre (constrainedLine.findNearestPointTo (reducedArea.getCentre()));
float distanceFromCentre = centre.getDistanceFrom (reducedArea.getCentre());
if (! (reducedArea.contains (lines[i].getStart()) || reducedArea.contains (lines[i].getEnd())))
distanceFromCentre *= 2.0f;
if (distanceFromCentre < bestDist)
{
bestDist = distanceFromCentre;
targetPoint = targets[i];
bounds.setPosition ((int) (centre.getX() - hw),
(int) (centre.getY() - hh));
}
}
setBounds (bounds);
}
void CallOutBox::refreshPath()
{
repaint();
background = Image();
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 float left = content.getX() - gap, top = content.getY() - gap, right = content.getRight() + gap, bottom = content.getBottom() + gap;
const float targetX = targetPoint.getX() - getX(), targetY = targetPoint.getY() - getY();
outline.startNewSubPath (left + cornerSize, top);
if (targetY < edge)
{
outline.lineTo (targetX - arrowBaseWidth, top);
outline.lineTo (targetX, targetY);
outline.lineTo (targetX + arrowBaseWidth, top);
}
outline.lineTo (right - cornerSize, top);
outline.addArc (right - cornerSize2, top, cornerSize2, cornerSize2, 0, float_Pi * 0.5f);
if (targetX > right)
{
outline.lineTo (right, targetY - arrowBaseWidth);
outline.lineTo (targetX, targetY);
outline.lineTo (right, targetY + arrowBaseWidth);
}
outline.lineTo (right, bottom - cornerSize);
outline.addArc (right - cornerSize2, bottom - cornerSize2, cornerSize2, cornerSize2, float_Pi * 0.5f, float_Pi);
if (targetY > bottom)
{
outline.lineTo (targetX + arrowBaseWidth, bottom);
outline.lineTo (targetX, targetY);
outline.lineTo (targetX - arrowBaseWidth, bottom);
}
outline.lineTo (left + cornerSize, bottom);
outline.addArc (left, bottom - cornerSize2, cornerSize2, cornerSize2, float_Pi, float_Pi * 1.5f);
if (targetX < left)
{
outline.lineTo (left, targetY + arrowBaseWidth);
outline.lineTo (targetX, targetY);
outline.lineTo (left, targetY - arrowBaseWidth);
}
outline.lineTo (left, top + cornerSize);
outline.addArc (left, top, cornerSize2, cornerSize2, float_Pi * 1.5f, float_Pi * 2.0f - 0.05f);
outline.closeSubPath();
}

+ 0
- 52
extras/Jucer (experimental)/Source/utility/jucer_MiscUtilities.h View File

@@ -81,58 +81,6 @@ private:
GlyphArrangement glyphs;
};
//==============================================================================
class CallOutBox : public Component
{
public:
//==============================================================================
CallOutBox (Component& content, float arrowSize = 16.0f);
~CallOutBox();
//==============================================================================
static void showModal (Component& contentComponent, Component* targetComp,
Component* parentComp, float arrowSize = 16.0f);
static void showModal (Component& contentComponent, Component* parent,
const Rectangle<int>& availableAreaInParent,
const Rectangle<int>& targetAreaInParent,
float arrowSize = 16.0f);
//==============================================================================
void updatePosition (const Rectangle<int>& newTargetArea, const Rectangle<int>& newArea);
//==============================================================================
/** @internal */
void paint (Graphics& g);
/** @internal */
void resized();
/** @internal */
void moved();
/** @internal */
void childBoundsChanged (Component*);
/** @internal */
bool hitTest (int x, int y);
/** @internal */
void inputAttemptWhenModal();
juce_UseDebuggingNewOperator
private:
const int edge;
const float arrowSize;
Component& content;
Path outline;
Point<float> targetPoint;
Rectangle<int> availableArea, targetArea;
Image background;
void refreshPath();
CallOutBox (const CallOutBox&);
CallOutBox& operator= (const CallOutBox&);
};
//==============================================================================
class JucerToolbarButton : public ToolbarItemComponent
{


+ 9
- 22
extras/juce demo/Source/demos/WidgetsDemo.cpp View File

@@ -256,28 +256,15 @@ public:
{
// create two colour selector components for our background and
// text colour..
ColourSelector colourSelector1;
colourSelector1.setName ("background");
colourSelector1.setCurrentColour (findColour (TextButton::buttonColourId));
colourSelector1.addChangeListener (this);
ColourSelector colourSelector2;
colourSelector2.setName ("text");
colourSelector2.setCurrentColour (findColour (TextButton::textColourOffId));
colourSelector2.addChangeListener (this);
// and add the selectors as custom menu items to a PopupMenu, putting
// them in two different sub-menus..
PopupMenu m, sub1, sub2;
sub1.addCustomItem (1234, &colourSelector1, 300, 300, false);
m.addSubMenu ("background colour", sub1);
sub2.addCustomItem (1234, &colourSelector2, 300, 300, false);
m.addSubMenu ("text colour", sub2);
// and show the menu (modally)..
m.showAt (this);
ColourSelector colourSelector;
colourSelector.setName ("background");
colourSelector.setCurrentColour (findColour (TextButton::buttonColourId));
colourSelector.addChangeListener (this);
colourSelector.setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
colourSelector.setSize (300, 400);
CallOutBox callOut (colourSelector, *this, 0);
callOut.runModalLoop();
}
void changeListenerCallback (void* source)


+ 275
- 47
juce_amalgamated.cpp View File

@@ -66032,6 +66032,25 @@ const Rectangle<int> LookAndFeel::getPropertyComponentContentPosition (PropertyC
component.getWidth() - component.getWidth() / 3 - 1, component.getHeight() - 3);
}

void LookAndFeel::drawCallOutBoxBackground (CallOutBox& box, Graphics& g, const Path& path)
{
Image content (Image::ARGB, box.getWidth(), box.getHeight(), true);

{
Graphics g2 (content);

g2.setColour (Colour::greyLevel (0.23f).withAlpha (0.9f));
g2.fillPath (path);

g2.setColour (Colours::white.withAlpha (0.8f));
g2.strokePath (path, PathStrokeType (2.0f));
}

DropShadowEffect shadow;
shadow.setShadowProperties (5.0f, 0.4f, 0, 2);
shadow.applyEffect (content, g);
}

void LookAndFeel::createFileChooserHeaderText (const String& title,
const String& instructions,
GlyphArrangement& text,
@@ -76241,6 +76260,210 @@ END_JUCE_NAMESPACE
/*** End of inlined file: juce_AlertWindow.cpp ***/


/*** Start of inlined file: juce_CallOutBox.cpp ***/
BEGIN_JUCE_NAMESPACE

CallOutBox::CallOutBox (Component& contentComponent,
Component& componentToPointTo,
Component* const parentComponent)
: borderSpace (20), arrowSize (16.0f), content (contentComponent)
{
addAndMakeVisible (&content);

if (parentComponent != 0)
{
updatePosition (parentComponent->getLocalBounds(),
componentToPointTo.getLocalBounds()
+ componentToPointTo.relativePositionToOtherComponent (parentComponent, Point<int>()));

parentComponent->addAndMakeVisible (this);
}
else
{
updatePosition (componentToPointTo.getScreenBounds(),
componentToPointTo.getParentMonitorArea());

addToDesktop (ComponentPeer::windowIsTemporary);
}
}

CallOutBox::~CallOutBox()
{
}

void CallOutBox::setArrowSize (const float newSize)
{
arrowSize = newSize;
borderSpace = jmax (20, (int) arrowSize);
refreshPath();
}

void CallOutBox::paint (Graphics& g)
{
if (background.isNull())
{
background = Image (Image::ARGB, getWidth(), getHeight(), true);
Graphics g (background);
getLookAndFeel().drawCallOutBoxBackground (*this, g, outline);
}

g.setColour (Colours::black);
g.drawImageAt (background, 0, 0);
}

void CallOutBox::resized()
{
content.setTopLeftPosition (borderSpace, borderSpace);
refreshPath();
}

void CallOutBox::moved()
{
refreshPath();
}

void CallOutBox::childBoundsChanged (Component*)
{
updatePosition (targetArea, availableArea);
}

bool CallOutBox::hitTest (int x, int y)
{
return outline.contains ((float) x, (float) y);
}

void CallOutBox::inputAttemptWhenModal()
{
exitModalState (0);
setVisible (false);
}

bool CallOutBox::keyPressed (const KeyPress& key)
{
if (key.isKeyCode (KeyPress::escapeKey))
{
inputAttemptWhenModal();
return true;
}

return false;
}

void CallOutBox::updatePosition (const Rectangle<int>& newAreaToPointTo, const Rectangle<int>& newAreaToFitIn)
{
targetArea = newAreaToPointTo;
availableArea = newAreaToFitIn;

Rectangle<int> bounds (0, 0,
content.getWidth() + borderSpace * 2,
content.getHeight() + borderSpace * 2);

const int hw = bounds.getWidth() / 2;
const int hh = bounds.getHeight() / 2;
const float hwReduced = (float) (hw - borderSpace * 3);
const float hhReduced = (float) (hh - borderSpace * 3);
const float arrowIndent = borderSpace - arrowSize;

Point<float> targets[4] = { Point<float> ((float) targetArea.getCentreX(), (float) targetArea.getBottom()),
Point<float> ((float) targetArea.getRight(), (float) targetArea.getCentreY()),
Point<float> ((float) targetArea.getX(), (float) targetArea.getCentreY()),
Point<float> ((float) targetArea.getCentreX(), (float) targetArea.getY()) };

Line<float> lines[4] = { Line<float> (targets[0].translated (-hwReduced, hh - arrowIndent), targets[0].translated (hwReduced, hh - arrowIndent)),
Line<float> (targets[1].translated (hw - arrowIndent, -hhReduced), targets[1].translated (hw - arrowIndent, hhReduced)),
Line<float> (targets[2].translated (-(hw - arrowIndent), -hhReduced), targets[2].translated (-(hw - arrowIndent), hhReduced)),
Line<float> (targets[3].translated (-hwReduced, -(hh - arrowIndent)), targets[3].translated (hwReduced, -(hh - arrowIndent))) };

const Rectangle<float> centrePointArea (newAreaToFitIn.reduced (hw, hh).toFloat());

float nearest = 1.0e9f;

for (int i = 0; i < 4; ++i)
{
Line<float> constrainedLine (centrePointArea.getConstrainedPoint (lines[i].getStart()),
centrePointArea.getConstrainedPoint (lines[i].getEnd()));

const Point<float> centre (constrainedLine.findNearestPointTo (centrePointArea.getCentre()));
float distanceFromCentre = centre.getDistanceFrom (centrePointArea.getCentre());

if (! (centrePointArea.contains (lines[i].getStart()) || centrePointArea.contains (lines[i].getEnd())))
distanceFromCentre *= 2.0f;

if (distanceFromCentre < nearest)
{
nearest = distanceFromCentre;

targetPoint = targets[i];
bounds.setPosition ((int) (centre.getX() - hw),
(int) (centre.getY() - hh));
}
}

setBounds (bounds);
}

void CallOutBox::refreshPath()
{
repaint();
background = Image();
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 float left = content.getX() - gap, top = content.getY() - gap, right = content.getRight() + gap, bottom = content.getBottom() + gap;
const float targetX = targetPoint.getX() - getX(), targetY = targetPoint.getY() - getY();

outline.startNewSubPath (left + cornerSize, top);

if (targetY <= top)
{
outline.lineTo (targetX - arrowBaseWidth, top);
outline.lineTo (targetX, targetY);
outline.lineTo (targetX + arrowBaseWidth, top);
}

outline.lineTo (right - cornerSize, top);
outline.addArc (right - cornerSize2, top, cornerSize2, cornerSize2, 0, float_Pi * 0.5f);

if (targetX >= right)
{
outline.lineTo (right, targetY - arrowBaseWidth);
outline.lineTo (targetX, targetY);
outline.lineTo (right, targetY + arrowBaseWidth);
}

outline.lineTo (right, bottom - cornerSize);
outline.addArc (right - cornerSize2, bottom - cornerSize2, cornerSize2, cornerSize2, float_Pi * 0.5f, float_Pi);

if (targetY >= bottom)
{
outline.lineTo (targetX + arrowBaseWidth, bottom);
outline.lineTo (targetX, targetY);
outline.lineTo (targetX - arrowBaseWidth, bottom);
}

outline.lineTo (left + cornerSize, bottom);
outline.addArc (left, bottom - cornerSize2, cornerSize2, cornerSize2, float_Pi, float_Pi * 1.5f);

if (targetX <= left)
{
outline.lineTo (left, targetY + arrowBaseWidth);
outline.lineTo (targetX, targetY);
outline.lineTo (left, targetY - arrowBaseWidth);
}

outline.lineTo (left, top + cornerSize);
outline.addArc (left, top, cornerSize2, cornerSize2, float_Pi * 1.5f, float_Pi * 2.0f - 0.05f);

outline.closeSubPath();
}

END_JUCE_NAMESPACE
/*** End of inlined file: juce_CallOutBox.cpp ***/


/*** Start of inlined file: juce_ComponentPeer.cpp ***/
BEGIN_JUCE_NAMESPACE

@@ -84084,6 +84307,17 @@ void DrawableComposite::resetBoundingBoxToContentArea()
RelativePoint (content.left, content.bottom)));
}

void DrawableComposite::resetContentAreaAndBoundingBoxToFitChildren()
{
const Rectangle<float> bounds (getUntransformedBounds (false));

setContentArea (RelativeRectangle (RelativeCoordinate (bounds.getX(), true),
RelativeCoordinate (bounds.getRight(), true),
RelativeCoordinate (bounds.getY(), false),
RelativeCoordinate (bounds.getBottom(), false)));
resetBoundingBoxToContentArea();
}

int DrawableComposite::getNumMarkers (const bool xAxis) const throw()
{
return (xAxis ? markersX : markersY).size();
@@ -84205,7 +84439,7 @@ const RelativeCoordinate DrawableComposite::findNamedCoordinate (const String& o
return RelativeCoordinate();
}

const Rectangle<float> DrawableComposite::getUntransformedBounds() const
const Rectangle<float> DrawableComposite::getUntransformedBounds (const bool includeMarkers) const
{
Rectangle<float> bounds;

@@ -84213,58 +84447,57 @@ const Rectangle<float> DrawableComposite::getUntransformedBounds() const
for (i = 0; i < drawables.size(); ++i)
bounds = bounds.getUnion (drawables.getUnchecked(i)->getBounds());

if (markersX.size() > 0)
if (includeMarkers)
{
float minX = std::numeric_limits<float>::max();
float maxX = std::numeric_limits<float>::min();

for (i = markersX.size(); --i >= 0;)
if (markersX.size() > 0)
{
const Marker* m = markersX.getUnchecked(i);
const float pos = (float) m->position.resolve (parent);
minX = jmin (minX, pos);
maxX = jmax (maxX, pos);
}
float minX = std::numeric_limits<float>::max();
float maxX = std::numeric_limits<float>::min();

if (minX <= maxX)
{
if (bounds.getWidth() == 0)
for (i = markersX.size(); --i >= 0;)
{
bounds.setLeft (minX);
bounds.setWidth (maxX - minX);
const Marker* m = markersX.getUnchecked(i);
const float pos = (float) m->position.resolve (parent);
minX = jmin (minX, pos);
maxX = jmax (maxX, pos);
}
else

if (minX <= maxX)
{
bounds.setLeft (jmin (bounds.getX(), minX));
bounds.setRight (jmax (bounds.getRight(), maxX));
if (bounds.getHeight() > 0)
{
minX = jmin (minX, bounds.getX());
maxX = jmax (maxX, bounds.getRight());
}

bounds.setLeft (minX);
bounds.setWidth (maxX - minX);
}
}
}

if (markersY.size() > 0)
{
float minY = std::numeric_limits<float>::max();
float maxY = std::numeric_limits<float>::min();

for (i = markersY.size(); --i >= 0;)
if (markersY.size() > 0)
{
const Marker* m = markersY.getUnchecked(i);
const float pos = (float) m->position.resolve (parent);
minY = jmin (minY, pos);
maxY = jmax (maxY, pos);
}
float minY = std::numeric_limits<float>::max();
float maxY = std::numeric_limits<float>::min();

if (minY <= maxY)
{
if (bounds.getHeight() == 0)
for (i = markersY.size(); --i >= 0;)
{
bounds.setTop (minY);
bounds.setHeight (maxY - minY);
const Marker* m = markersY.getUnchecked(i);
const float pos = (float) m->position.resolve (parent);
minY = jmin (minY, pos);
maxY = jmax (maxY, pos);
}
else

if (minY <= maxY)
{
bounds.setTop (jmin (bounds.getY(), minY));
bounds.setBottom (jmax (bounds.getBottom(), maxY));
if (bounds.getHeight() > 0)
{
minY = jmin (minY, bounds.getY());
maxY = jmax (maxY, bounds.getBottom());
}

bounds.setTop (minY);
bounds.setHeight (maxY - minY);
}
}
}
@@ -84274,7 +84507,7 @@ const Rectangle<float> DrawableComposite::getUntransformedBounds() const

const Rectangle<float> DrawableComposite::getBounds() const
{
return getUntransformedBounds().transformed (calculateTransform());
return getUntransformedBounds (true).transformed (calculateTransform());
}

bool DrawableComposite::hitTest (float x, float y) const
@@ -85643,13 +85876,7 @@ public:

newState.parseSubElements (xml, drawable);

const Rectangle<float> bounds (drawable->getBounds());
drawable->setContentArea (RelativeRectangle (RelativeCoordinate (bounds.getX(), true),
RelativeCoordinate (bounds.getRight(), true),
RelativeCoordinate (bounds.getY(), false),
RelativeCoordinate (bounds.getBottom(), false)));
drawable->resetBoundingBoxToContentArea();

drawable->resetContentAreaAndBoundingBoxToFitChildren();
return drawable;
}

@@ -85711,6 +85938,7 @@ private:
parseSubElements (xml, drawable);
}

drawable->resetContentAreaAndBoundingBoxToFitChildren();
return drawable;
}



+ 125
- 11
juce_amalgamated.h View File

@@ -64,7 +64,7 @@
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 52
#define JUCE_BUILDNUMBER 13
#define JUCE_BUILDNUMBER 14

/** Current Juce version number.

@@ -22284,17 +22284,20 @@ public:

/** Applies this stroke type to a path and returns the resultant stroke as another Path.

@param destPath the resultant stroked outline shape will be copied into this path.
Note that it's ok for the source and destination Paths to be
the same object, so you can easily turn a path into a stroked version
of itself.
@param destPath the resultant stroked outline shape will be copied into this path.
Note that it's ok for the source and destination Paths to be
the same object, so you can easily turn a path into a stroked version
of itself.
@param sourcePath the path to use as the source
@param transform an optional transform to apply to the points from the source path
as they are being used
@param arrowheadStartWidth the width of the arrowhead at the start of the path
@param arrowheadStartLength the length of the arrowhead at the start of the path
@param arrowheadEndWidth the width of the arrowhead at the end of the path
@param arrowheadEndLength the length of the arrowhead at the end of the path
@param transform an optional transform to apply to the points from the source path
as they are being used
@param extraAccuracy if this is greater than 1.0, it will subdivide the path to
a higher resolution, which improved the quality if you'll later want
to enlarge the stroked path

a higher resolution, which improved the quality if you'll later want
to enlarge the stroked path
@see createDashedStroke
*/
void createStrokeWithArrowheads (Path& destPath,
@@ -53213,6 +53216,7 @@ class FileBrowserComponent;
class DirectoryContentsDisplayComponent;
class FilePreviewComponent;
class ImageButton;
class CallOutBox;

/**
LookAndFeel objects define the appearance of all the JUCE widgets, and subclasses
@@ -53719,6 +53723,8 @@ public:

virtual const Rectangle<int> getPropertyComponentContentPosition (PropertyComponent& component);

void drawCallOutBoxBackground (CallOutBox& box, Graphics& g, const Path& path);

virtual void drawLevelMeter (Graphics& g, int width, int height, float level);

virtual void drawKeymapChangeButton (Graphics& g, int width, int height, Button& button, const String& keyDescription);
@@ -57130,6 +57136,109 @@ private:
#endif
#ifndef __JUCE_ALERTWINDOW_JUCEHEADER__

#endif
#ifndef __JUCE_CALLOUTBOX_JUCEHEADER__

/*** Start of inlined file: juce_CallOutBox.h ***/
#ifndef __JUCE_CALLOUTBOX_JUCEHEADER__
#define __JUCE_CALLOUTBOX_JUCEHEADER__

/**
A box with a small arrow that can be used as a temporary pop-up window to show
extra controls when a button or other component is clicked.

Using one of these is similar to having a popup menu attached to a button or
other component - but it looks fancier, and has an arrow that can indicate the
object that it applies to.

Normally, you'd create one of these on the stack and run it modally, e.g.

@code
void mouseUp (const MouseEvent& e)
{
MyContentComponent content;
content.setSize (300, 300);

CallOutBox callOut (content, *this, 0);
callOut.runModalLoop();
}
@endcode

The call-out will resize and position itself when the content changes size.
*/
class JUCE_API CallOutBox : public Component
{
public:

/** Creates a CallOutBox.

@param contentComponent the component to display inside the call-out. This should
already have a size set (although the call-out will also
update itself when the component's size is changed later).
Obviously this component must not be deleted until the
call-out box has been deleted.
@param componentToPointTo the component that the call-out's arrow should point towards
@param parentComponent if non-zero, this is the component to add the call-out to. If
this is zero, the call-out will be added to the desktop.
*/
CallOutBox (Component& contentComponent,
Component& componentToPointTo,
Component* parentComponent);

/** Destructor. */
~CallOutBox();

/** Changes the length of the arrow. */
void setArrowSize (float newSize);

/** Updates the position and size of the box.

You shouldn't normally need to call this, unless you need more precise control over the
layout.

@param newAreaToPointTo the rectangle to make the box's arrow point to
@param newAreaToFitIn the area within which the box's position should be constrained
*/
void updatePosition (const Rectangle<int>& newAreaToPointTo,
const Rectangle<int>& newAreaToFitIn);

/** @internal */
void paint (Graphics& g);
/** @internal */
void resized();
/** @internal */
void moved();
/** @internal */
void childBoundsChanged (Component*);
/** @internal */
bool hitTest (int x, int y);
/** @internal */
void inputAttemptWhenModal();
/** @internal */
bool keyPressed (const KeyPress& key);

juce_UseDebuggingNewOperator

private:
int borderSpace;
float arrowSize;
Component& content;
Path outline;
Point<float> targetPoint;
Rectangle<int> availableArea, targetArea;
Image background;

void refreshPath();
void drawCallOutBoxBackground (Graphics& g, const Path& outline, int width, int height);

CallOutBox (const CallOutBox&);
CallOutBox& operator= (const CallOutBox&);
};

#endif // __JUCE_CALLOUTBOX_JUCEHEADER__
/*** End of inlined file: juce_CallOutBox.h ***/


#endif
#ifndef __JUCE_COMPONENTPEER_JUCEHEADER__

@@ -58558,6 +58667,11 @@ public:
*/
void resetBoundingBoxToContentArea();

/** Resets the content area and the bounding transform to fit around the area occupied
by the child components (ignoring any markers).
*/
void resetContentAreaAndBoundingBoxToFitChildren();

/** Represents a named marker position.
@see DrawableComposite::getMarker
*/
@@ -58654,7 +58768,7 @@ private:
RelativeParallelogram bounds;
OwnedArray <Marker> markersX, markersY;

const Rectangle<float> getUntransformedBounds() const;
const Rectangle<float> getUntransformedBounds (bool includeMarkers) const;
const AffineTransform calculateTransform() const;

DrawableComposite& operator= (const DrawableComposite&);


+ 1
- 1
src/core/juce_StandardHeader.h View File

@@ -33,7 +33,7 @@
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 52
#define JUCE_BUILDNUMBER 13
#define JUCE_BUILDNUMBER 14
/** Current Juce version number.


+ 22
- 0
src/gui/components/lookandfeel/juce_LookAndFeel.cpp View File

@@ -39,6 +39,7 @@ BEGIN_JUCE_NAMESPACE
#include "../windows/juce_AlertWindow.h"
#include "../windows/juce_DocumentWindow.h"
#include "../windows/juce_ResizableWindow.h"
#include "../windows/juce_CallOutBox.h"
#include "../menus/juce_MenuBarComponent.h"
#include "../menus/juce_PopupMenu.h"
#include "../layout/juce_ScrollBar.h"
@@ -2528,6 +2529,27 @@ const Rectangle<int> LookAndFeel::getPropertyComponentContentPosition (PropertyC
component.getWidth() - component.getWidth() / 3 - 1, component.getHeight() - 3);
}
//==============================================================================
void LookAndFeel::drawCallOutBoxBackground (CallOutBox& box, Graphics& g, const Path& path)
{
Image content (Image::ARGB, box.getWidth(), box.getHeight(), true);
{
Graphics g2 (content);
g2.setColour (Colour::greyLevel (0.23f).withAlpha (0.9f));
g2.fillPath (path);
g2.setColour (Colours::white.withAlpha (0.8f));
g2.strokePath (path, PathStrokeType (2.0f));
}
DropShadowEffect shadow;
shadow.setShadowProperties (5.0f, 0.4f, 0, 2);
shadow.applyEffect (content, g);
}
//==============================================================================
void LookAndFeel::createFileChooserHeaderText (const String& title,
const String& instructions,


+ 4
- 0
src/gui/components/lookandfeel/juce_LookAndFeel.h View File

@@ -57,6 +57,7 @@ class FileBrowserComponent;
class DirectoryContentsDisplayComponent;
class FilePreviewComponent;
class ImageButton;
class CallOutBox;
//==============================================================================
@@ -597,6 +598,9 @@ public:
virtual const Rectangle<int> getPropertyComponentContentPosition (PropertyComponent& component);
//==============================================================================
void drawCallOutBoxBackground (CallOutBox& box, Graphics& g, const Path& path);
//==============================================================================
virtual void drawLevelMeter (Graphics& g, int width, int height, float level);


+ 234
- 0
src/gui/components/windows/juce_CallOutBox.cpp View File

@@ -0,0 +1,234 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-10 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../../../core/juce_StandardHeader.h"
BEGIN_JUCE_NAMESPACE
#include "juce_CallOutBox.h"
#include "juce_ComponentPeer.h"
#include "../lookandfeel/juce_LookAndFeel.h"
//==============================================================================
CallOutBox::CallOutBox (Component& contentComponent,
Component& componentToPointTo,
Component* const parentComponent)
: borderSpace (20), arrowSize (16.0f), content (contentComponent)
{
addAndMakeVisible (&content);
if (parentComponent != 0)
{
updatePosition (parentComponent->getLocalBounds(),
componentToPointTo.getLocalBounds()
+ componentToPointTo.relativePositionToOtherComponent (parentComponent, Point<int>()));
parentComponent->addAndMakeVisible (this);
}
else
{
updatePosition (componentToPointTo.getScreenBounds(),
componentToPointTo.getParentMonitorArea());
addToDesktop (ComponentPeer::windowIsTemporary);
}
}
CallOutBox::~CallOutBox()
{
}
//==============================================================================
void CallOutBox::setArrowSize (const float newSize)
{
arrowSize = newSize;
borderSpace = jmax (20, (int) arrowSize);
refreshPath();
}
void CallOutBox::paint (Graphics& g)
{
if (background.isNull())
{
background = Image (Image::ARGB, getWidth(), getHeight(), true);
Graphics g (background);
getLookAndFeel().drawCallOutBoxBackground (*this, g, outline);
}
g.setColour (Colours::black);
g.drawImageAt (background, 0, 0);
}
void CallOutBox::resized()
{
content.setTopLeftPosition (borderSpace, borderSpace);
refreshPath();
}
void CallOutBox::moved()
{
refreshPath();
}
void CallOutBox::childBoundsChanged (Component*)
{
updatePosition (targetArea, availableArea);
}
bool CallOutBox::hitTest (int x, int y)
{
return outline.contains ((float) x, (float) y);
}
void CallOutBox::inputAttemptWhenModal()
{
exitModalState (0);
setVisible (false);
}
bool CallOutBox::keyPressed (const KeyPress& key)
{
if (key.isKeyCode (KeyPress::escapeKey))
{
inputAttemptWhenModal();
return true;
}
return false;
}
void CallOutBox::updatePosition (const Rectangle<int>& newAreaToPointTo, const Rectangle<int>& newAreaToFitIn)
{
targetArea = newAreaToPointTo;
availableArea = newAreaToFitIn;
Rectangle<int> bounds (0, 0,
content.getWidth() + borderSpace * 2,
content.getHeight() + borderSpace * 2);
const int hw = bounds.getWidth() / 2;
const int hh = bounds.getHeight() / 2;
const float hwReduced = (float) (hw - borderSpace * 3);
const float hhReduced = (float) (hh - borderSpace * 3);
const float arrowIndent = borderSpace - arrowSize;
Point<float> targets[4] = { Point<float> ((float) targetArea.getCentreX(), (float) targetArea.getBottom()),
Point<float> ((float) targetArea.getRight(), (float) targetArea.getCentreY()),
Point<float> ((float) targetArea.getX(), (float) targetArea.getCentreY()),
Point<float> ((float) targetArea.getCentreX(), (float) targetArea.getY()) };
Line<float> lines[4] = { Line<float> (targets[0].translated (-hwReduced, hh - arrowIndent), targets[0].translated (hwReduced, hh - arrowIndent)),
Line<float> (targets[1].translated (hw - arrowIndent, -hhReduced), targets[1].translated (hw - arrowIndent, hhReduced)),
Line<float> (targets[2].translated (-(hw - arrowIndent), -hhReduced), targets[2].translated (-(hw - arrowIndent), hhReduced)),
Line<float> (targets[3].translated (-hwReduced, -(hh - arrowIndent)), targets[3].translated (hwReduced, -(hh - arrowIndent))) };
const Rectangle<float> centrePointArea (newAreaToFitIn.reduced (hw, hh).toFloat());
float nearest = 1.0e9f;
for (int i = 0; i < 4; ++i)
{
Line<float> constrainedLine (centrePointArea.getConstrainedPoint (lines[i].getStart()),
centrePointArea.getConstrainedPoint (lines[i].getEnd()));
const Point<float> centre (constrainedLine.findNearestPointTo (centrePointArea.getCentre()));
float distanceFromCentre = centre.getDistanceFrom (centrePointArea.getCentre());
if (! (centrePointArea.contains (lines[i].getStart()) || centrePointArea.contains (lines[i].getEnd())))
distanceFromCentre *= 2.0f;
if (distanceFromCentre < nearest)
{
nearest = distanceFromCentre;
targetPoint = targets[i];
bounds.setPosition ((int) (centre.getX() - hw),
(int) (centre.getY() - hh));
}
}
setBounds (bounds);
}
void CallOutBox::refreshPath()
{
repaint();
background = Image();
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 float left = content.getX() - gap, top = content.getY() - gap, right = content.getRight() + gap, bottom = content.getBottom() + gap;
const float targetX = targetPoint.getX() - getX(), targetY = targetPoint.getY() - getY();
outline.startNewSubPath (left + cornerSize, top);
if (targetY <= top)
{
outline.lineTo (targetX - arrowBaseWidth, top);
outline.lineTo (targetX, targetY);
outline.lineTo (targetX + arrowBaseWidth, top);
}
outline.lineTo (right - cornerSize, top);
outline.addArc (right - cornerSize2, top, cornerSize2, cornerSize2, 0, float_Pi * 0.5f);
if (targetX >= right)
{
outline.lineTo (right, targetY - arrowBaseWidth);
outline.lineTo (targetX, targetY);
outline.lineTo (right, targetY + arrowBaseWidth);
}
outline.lineTo (right, bottom - cornerSize);
outline.addArc (right - cornerSize2, bottom - cornerSize2, cornerSize2, cornerSize2, float_Pi * 0.5f, float_Pi);
if (targetY >= bottom)
{
outline.lineTo (targetX + arrowBaseWidth, bottom);
outline.lineTo (targetX, targetY);
outline.lineTo (targetX - arrowBaseWidth, bottom);
}
outline.lineTo (left + cornerSize, bottom);
outline.addArc (left, bottom - cornerSize2, cornerSize2, cornerSize2, float_Pi, float_Pi * 1.5f);
if (targetX <= left)
{
outline.lineTo (left, targetY + arrowBaseWidth);
outline.lineTo (targetX, targetY);
outline.lineTo (left, targetY - arrowBaseWidth);
}
outline.lineTo (left, top + cornerSize);
outline.addArc (left, top, cornerSize2, cornerSize2, float_Pi * 1.5f, float_Pi * 2.0f - 0.05f);
outline.closeSubPath();
}
END_JUCE_NAMESPACE

+ 128
- 0
src/gui/components/windows/juce_CallOutBox.h View File

@@ -0,0 +1,128 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-10 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#ifndef __JUCE_CALLOUTBOX_JUCEHEADER__
#define __JUCE_CALLOUTBOX_JUCEHEADER__
#include "../juce_Component.h"
//==============================================================================
/**
A box with a small arrow that can be used as a temporary pop-up window to show
extra controls when a button or other component is clicked.
Using one of these is similar to having a popup menu attached to a button or
other component - but it looks fancier, and has an arrow that can indicate the
object that it applies to.
Normally, you'd create one of these on the stack and run it modally, e.g.
@code
void mouseUp (const MouseEvent& e)
{
MyContentComponent content;
content.setSize (300, 300);
CallOutBox callOut (content, *this, 0);
callOut.runModalLoop();
}
@endcode
The call-out will resize and position itself when the content changes size.
*/
class JUCE_API CallOutBox : public Component
{
public:
//==============================================================================
/** Creates a CallOutBox.
@param contentComponent the component to display inside the call-out. This should
already have a size set (although the call-out will also
update itself when the component's size is changed later).
Obviously this component must not be deleted until the
call-out box has been deleted.
@param componentToPointTo the component that the call-out's arrow should point towards
@param parentComponent if non-zero, this is the component to add the call-out to. If
this is zero, the call-out will be added to the desktop.
*/
CallOutBox (Component& contentComponent,
Component& componentToPointTo,
Component* parentComponent);
/** Destructor. */
~CallOutBox();
//==============================================================================
/** Changes the length of the arrow. */
void setArrowSize (float newSize);
/** Updates the position and size of the box.
You shouldn't normally need to call this, unless you need more precise control over the
layout.
@param newAreaToPointTo the rectangle to make the box's arrow point to
@param newAreaToFitIn the area within which the box's position should be constrained
*/
void updatePosition (const Rectangle<int>& newAreaToPointTo,
const Rectangle<int>& newAreaToFitIn);
//==============================================================================
/** @internal */
void paint (Graphics& g);
/** @internal */
void resized();
/** @internal */
void moved();
/** @internal */
void childBoundsChanged (Component*);
/** @internal */
bool hitTest (int x, int y);
/** @internal */
void inputAttemptWhenModal();
/** @internal */
bool keyPressed (const KeyPress& key);
juce_UseDebuggingNewOperator
private:
int borderSpace;
float arrowSize;
Component& content;
Path outline;
Point<float> targetPoint;
Rectangle<int> availableArea, targetArea;
Image background;
void refreshPath();
void drawCallOutBoxBackground (Graphics& g, const Path& outline, int width, int height);
CallOutBox (const CallOutBox&);
CallOutBox& operator= (const CallOutBox&);
};
#endif // __JUCE_CALLOUTBOX_JUCEHEADER__

+ 50
- 40
src/gui/graphics/drawables/juce_DrawableComposite.cpp View File

@@ -149,6 +149,17 @@ void DrawableComposite::resetBoundingBoxToContentArea()
RelativePoint (content.left, content.bottom)));
}
void DrawableComposite::resetContentAreaAndBoundingBoxToFitChildren()
{
const Rectangle<float> bounds (getUntransformedBounds (false));
setContentArea (RelativeRectangle (RelativeCoordinate (bounds.getX(), true),
RelativeCoordinate (bounds.getRight(), true),
RelativeCoordinate (bounds.getY(), false),
RelativeCoordinate (bounds.getBottom(), false)));
resetBoundingBoxToContentArea();
}
int DrawableComposite::getNumMarkers (const bool xAxis) const throw()
{
return (xAxis ? markersX : markersY).size();
@@ -271,7 +282,7 @@ const RelativeCoordinate DrawableComposite::findNamedCoordinate (const String& o
return RelativeCoordinate();
}
const Rectangle<float> DrawableComposite::getUntransformedBounds() const
const Rectangle<float> DrawableComposite::getUntransformedBounds (const bool includeMarkers) const
{
Rectangle<float> bounds;
@@ -279,58 +290,57 @@ const Rectangle<float> DrawableComposite::getUntransformedBounds() const
for (i = 0; i < drawables.size(); ++i)
bounds = bounds.getUnion (drawables.getUnchecked(i)->getBounds());
if (markersX.size() > 0)
if (includeMarkers)
{
float minX = std::numeric_limits<float>::max();
float maxX = std::numeric_limits<float>::min();
for (i = markersX.size(); --i >= 0;)
if (markersX.size() > 0)
{
const Marker* m = markersX.getUnchecked(i);
const float pos = (float) m->position.resolve (parent);
minX = jmin (minX, pos);
maxX = jmax (maxX, pos);
}
float minX = std::numeric_limits<float>::max();
float maxX = std::numeric_limits<float>::min();
if (minX <= maxX)
{
if (bounds.getWidth() == 0)
for (i = markersX.size(); --i >= 0;)
{
bounds.setLeft (minX);
bounds.setWidth (maxX - minX);
const Marker* m = markersX.getUnchecked(i);
const float pos = (float) m->position.resolve (parent);
minX = jmin (minX, pos);
maxX = jmax (maxX, pos);
}
else
if (minX <= maxX)
{
bounds.setLeft (jmin (bounds.getX(), minX));
bounds.setRight (jmax (bounds.getRight(), maxX));
if (bounds.getHeight() > 0)
{
minX = jmin (minX, bounds.getX());
maxX = jmax (maxX, bounds.getRight());
}
bounds.setLeft (minX);
bounds.setWidth (maxX - minX);
}
}
}
if (markersY.size() > 0)
{
float minY = std::numeric_limits<float>::max();
float maxY = std::numeric_limits<float>::min();
for (i = markersY.size(); --i >= 0;)
if (markersY.size() > 0)
{
const Marker* m = markersY.getUnchecked(i);
const float pos = (float) m->position.resolve (parent);
minY = jmin (minY, pos);
maxY = jmax (maxY, pos);
}
float minY = std::numeric_limits<float>::max();
float maxY = std::numeric_limits<float>::min();
if (minY <= maxY)
{
if (bounds.getHeight() == 0)
for (i = markersY.size(); --i >= 0;)
{
bounds.setTop (minY);
bounds.setHeight (maxY - minY);
const Marker* m = markersY.getUnchecked(i);
const float pos = (float) m->position.resolve (parent);
minY = jmin (minY, pos);
maxY = jmax (maxY, pos);
}
else
if (minY <= maxY)
{
bounds.setTop (jmin (bounds.getY(), minY));
bounds.setBottom (jmax (bounds.getBottom(), maxY));
if (bounds.getHeight() > 0)
{
minY = jmin (minY, bounds.getY());
maxY = jmax (maxY, bounds.getBottom());
}
bounds.setTop (minY);
bounds.setHeight (maxY - minY);
}
}
}
@@ -340,7 +350,7 @@ const Rectangle<float> DrawableComposite::getUntransformedBounds() const
const Rectangle<float> DrawableComposite::getBounds() const
{
return getUntransformedBounds().transformed (calculateTransform());
return getUntransformedBounds (true).transformed (calculateTransform());
}
bool DrawableComposite::hitTest (float x, float y) const


+ 6
- 1
src/gui/graphics/drawables/juce_DrawableComposite.h View File

@@ -148,6 +148,11 @@ public:
*/
void resetBoundingBoxToContentArea();
/** Resets the content area and the bounding transform to fit around the area occupied
by the child components (ignoring any markers).
*/
void resetContentAreaAndBoundingBoxToFitChildren();
//==============================================================================
/** Represents a named marker position.
@see DrawableComposite::getMarker
@@ -248,7 +253,7 @@ private:
RelativeParallelogram bounds;
OwnedArray <Marker> markersX, markersY;
const Rectangle<float> getUntransformedBounds() const;
const Rectangle<float> getUntransformedBounds (bool includeMarkers) const;
const AffineTransform calculateTransform() const;
DrawableComposite& operator= (const DrawableComposite&);


+ 2
- 7
src/gui/graphics/drawables/juce_SVGParser.cpp View File

@@ -130,13 +130,7 @@ public:
newState.parseSubElements (xml, drawable);
const Rectangle<float> bounds (drawable->getBounds());
drawable->setContentArea (RelativeRectangle (RelativeCoordinate (bounds.getX(), true),
RelativeCoordinate (bounds.getRight(), true),
RelativeCoordinate (bounds.getY(), false),
RelativeCoordinate (bounds.getBottom(), false)));
drawable->resetBoundingBoxToContentArea();
drawable->resetContentAreaAndBoundingBoxToFitChildren();
return drawable;
}
@@ -199,6 +193,7 @@ private:
parseSubElements (xml, drawable);
}
drawable->resetContentAreaAndBoundingBoxToFitChildren();
return drawable;
}


+ 14
- 11
src/gui/graphics/geometry/juce_PathStrokeType.h View File

@@ -140,17 +140,20 @@ public:
//==============================================================================
/** Applies this stroke type to a path and returns the resultant stroke as another Path.
@param destPath the resultant stroked outline shape will be copied into this path.
Note that it's ok for the source and destination Paths to be
the same object, so you can easily turn a path into a stroked version
of itself.
@param sourcePath the path to use as the source
@param transform an optional transform to apply to the points from the source path
as they are being used
@param extraAccuracy if this is greater than 1.0, it will subdivide the path to
a higher resolution, which improved the quality if you'll later want
to enlarge the stroked path
@param destPath the resultant stroked outline shape will be copied into this path.
Note that it's ok for the source and destination Paths to be
the same object, so you can easily turn a path into a stroked version
of itself.
@param sourcePath the path to use as the source
@param arrowheadStartWidth the width of the arrowhead at the start of the path
@param arrowheadStartLength the length of the arrowhead at the start of the path
@param arrowheadEndWidth the width of the arrowhead at the end of the path
@param arrowheadEndLength the length of the arrowhead at the end of the path
@param transform an optional transform to apply to the points from the source path
as they are being used
@param extraAccuracy if this is greater than 1.0, it will subdivide the path to
a higher resolution, which improved the quality if you'll later want
to enlarge the stroked path
@see createDashedStroke
*/
void createStrokeWithArrowheads (Path& destPath,


+ 3
- 0
src/juce_app_includes.h View File

@@ -584,6 +584,9 @@
#ifndef __JUCE_ALERTWINDOW_JUCEHEADER__
#include "gui/components/windows/juce_AlertWindow.h"
#endif
#ifndef __JUCE_CALLOUTBOX_JUCEHEADER__
#include "gui/components/windows/juce_CallOutBox.h"
#endif
#ifndef __JUCE_COMPONENTPEER_JUCEHEADER__
#include "gui/components/windows/juce_ComponentPeer.h"
#endif


Loading…
Cancel
Save