mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-13 00:04:19 +00:00
Added Animated App template and examples
This commit is contained in:
parent
fefcf7aca6
commit
ff6520a89a
1141 changed files with 438491 additions and 94 deletions
|
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
Drawable::Drawable()
|
||||
{
|
||||
setInterceptsMouseClicks (false, false);
|
||||
setPaintingIsUnclipped (true);
|
||||
}
|
||||
|
||||
Drawable::Drawable (const Drawable& other)
|
||||
: Component (other.getName())
|
||||
{
|
||||
setComponentID (other.getComponentID());
|
||||
}
|
||||
|
||||
Drawable::~Drawable()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Drawable::draw (Graphics& g, float opacity, const AffineTransform& transform) const
|
||||
{
|
||||
const_cast <Drawable*> (this)->nonConstDraw (g, opacity, transform);
|
||||
}
|
||||
|
||||
void Drawable::nonConstDraw (Graphics& g, float opacity, const AffineTransform& transform)
|
||||
{
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
|
||||
g.addTransform (AffineTransform::translation ((float) -(originRelativeToComponent.x),
|
||||
(float) -(originRelativeToComponent.y))
|
||||
.followedBy (getTransform())
|
||||
.followedBy (transform));
|
||||
|
||||
if (! g.isClipEmpty())
|
||||
{
|
||||
if (opacity < 1.0f)
|
||||
{
|
||||
g.beginTransparencyLayer (opacity);
|
||||
paintEntireComponent (g, true);
|
||||
g.endTransparencyLayer();
|
||||
}
|
||||
else
|
||||
{
|
||||
paintEntireComponent (g, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Drawable::drawAt (Graphics& g, float x, float y, float opacity) const
|
||||
{
|
||||
draw (g, opacity, AffineTransform::translation (x, y));
|
||||
}
|
||||
|
||||
void Drawable::drawWithin (Graphics& g, const Rectangle<float>& destArea,
|
||||
RectanglePlacement placement, float opacity) const
|
||||
{
|
||||
draw (g, opacity, placement.getTransformToFit (getDrawableBounds(), destArea));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
DrawableComposite* Drawable::getParent() const
|
||||
{
|
||||
return dynamic_cast <DrawableComposite*> (getParentComponent());
|
||||
}
|
||||
|
||||
void Drawable::transformContextToCorrectOrigin (Graphics& g)
|
||||
{
|
||||
g.setOrigin (originRelativeToComponent);
|
||||
}
|
||||
|
||||
void Drawable::parentHierarchyChanged()
|
||||
{
|
||||
setBoundsToEnclose (getDrawableBounds());
|
||||
}
|
||||
|
||||
void Drawable::setBoundsToEnclose (const Rectangle<float>& area)
|
||||
{
|
||||
Drawable* const parent = getParent();
|
||||
Point<int> parentOrigin;
|
||||
if (parent != nullptr)
|
||||
parentOrigin = parent->originRelativeToComponent;
|
||||
|
||||
const Rectangle<int> newBounds (area.getSmallestIntegerContainer() + parentOrigin);
|
||||
originRelativeToComponent = parentOrigin - newBounds.getPosition();
|
||||
setBounds (newBounds);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool Drawable::replaceColour (Colour original, Colour replacement)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
if (Drawable* d = dynamic_cast<Drawable*> (getChildComponent(i)))
|
||||
changed = d->replaceColour (original, replacement) || changed;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Drawable::setOriginWithOriginalSize (Point<float> originWithinParent)
|
||||
{
|
||||
setTransform (AffineTransform::translation (originWithinParent.x, originWithinParent.y));
|
||||
}
|
||||
|
||||
void Drawable::setTransformToFit (const Rectangle<float>& area, RectanglePlacement placement)
|
||||
{
|
||||
if (! area.isEmpty())
|
||||
setTransform (placement.getTransformToFit (getDrawableBounds(), area));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Drawable* Drawable::createFromImageData (const void* data, const size_t numBytes)
|
||||
{
|
||||
Drawable* result = nullptr;
|
||||
|
||||
Image image (ImageFileFormat::loadFrom (data, numBytes));
|
||||
|
||||
if (image.isValid())
|
||||
{
|
||||
DrawableImage* const di = new DrawableImage();
|
||||
di->setImage (image);
|
||||
result = di;
|
||||
}
|
||||
else
|
||||
{
|
||||
const String asString (String::createStringFromData (data, (int) numBytes));
|
||||
|
||||
XmlDocument doc (asString);
|
||||
ScopedPointer <XmlElement> outer (doc.getDocumentElement (true));
|
||||
|
||||
if (outer != nullptr && outer->hasTagName ("svg"))
|
||||
{
|
||||
ScopedPointer <XmlElement> svg (doc.getDocumentElement());
|
||||
|
||||
if (svg != nullptr)
|
||||
result = Drawable::createFromSVG (*svg);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Drawable* Drawable::createFromImageDataStream (InputStream& dataSource)
|
||||
{
|
||||
MemoryOutputStream mo;
|
||||
mo << dataSource;
|
||||
|
||||
return createFromImageData (mo.getData(), mo.getDataSize());
|
||||
}
|
||||
|
||||
Drawable* Drawable::createFromImageFile (const File& file)
|
||||
{
|
||||
FileInputStream fin (file);
|
||||
|
||||
return fin.openedOk() ? createFromImageDataStream (fin) : nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <class DrawableClass>
|
||||
class DrawableTypeHandler : public ComponentBuilder::TypeHandler
|
||||
{
|
||||
public:
|
||||
DrawableTypeHandler()
|
||||
: ComponentBuilder::TypeHandler (DrawableClass::valueTreeType)
|
||||
{
|
||||
}
|
||||
|
||||
Component* addNewComponentFromState (const ValueTree& state, Component* parent)
|
||||
{
|
||||
DrawableClass* const d = new DrawableClass();
|
||||
|
||||
if (parent != nullptr)
|
||||
parent->addAndMakeVisible (d);
|
||||
|
||||
updateComponentFromState (d, state);
|
||||
return d;
|
||||
}
|
||||
|
||||
void updateComponentFromState (Component* component, const ValueTree& state)
|
||||
{
|
||||
DrawableClass* const d = dynamic_cast <DrawableClass*> (component);
|
||||
jassert (d != nullptr);
|
||||
d->refreshFromValueTree (state, *this->getBuilder());
|
||||
}
|
||||
};
|
||||
|
||||
void Drawable::registerDrawableTypeHandlers (ComponentBuilder& builder)
|
||||
{
|
||||
builder.registerTypeHandler (new DrawableTypeHandler <DrawablePath>());
|
||||
builder.registerTypeHandler (new DrawableTypeHandler <DrawableComposite>());
|
||||
builder.registerTypeHandler (new DrawableTypeHandler <DrawableRectangle>());
|
||||
builder.registerTypeHandler (new DrawableTypeHandler <DrawableImage>());
|
||||
builder.registerTypeHandler (new DrawableTypeHandler <DrawableText>());
|
||||
}
|
||||
|
||||
Drawable* Drawable::createFromValueTree (const ValueTree& tree, ComponentBuilder::ImageProvider* imageProvider)
|
||||
{
|
||||
ComponentBuilder builder (tree);
|
||||
builder.setImageProvider (imageProvider);
|
||||
registerDrawableTypeHandlers (builder);
|
||||
|
||||
ScopedPointer<Component> comp (builder.createComponent());
|
||||
Drawable* const d = dynamic_cast<Drawable*> (static_cast <Component*> (comp));
|
||||
|
||||
if (d != nullptr)
|
||||
comp.release();
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Drawable::ValueTreeWrapperBase::ValueTreeWrapperBase (const ValueTree& state_)
|
||||
: state (state_)
|
||||
{
|
||||
}
|
||||
|
||||
String Drawable::ValueTreeWrapperBase::getID() const
|
||||
{
|
||||
return state [ComponentBuilder::idProperty];
|
||||
}
|
||||
|
||||
void Drawable::ValueTreeWrapperBase::setID (const String& newID)
|
||||
{
|
||||
if (newID.isEmpty())
|
||||
state.removeProperty (ComponentBuilder::idProperty, nullptr);
|
||||
else
|
||||
state.setProperty (ComponentBuilder::idProperty, newID, nullptr);
|
||||
}
|
||||
|
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_DRAWABLE_H_INCLUDED
|
||||
#define JUCE_DRAWABLE_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
The base class for objects which can draw themselves, e.g. polygons, images, etc.
|
||||
|
||||
@see DrawableComposite, DrawableImage, DrawablePath, DrawableText
|
||||
*/
|
||||
class JUCE_API Drawable : public Component
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** The base class can't be instantiated directly.
|
||||
|
||||
@see DrawableComposite, DrawableImage, DrawablePath, DrawableText
|
||||
*/
|
||||
Drawable();
|
||||
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~Drawable();
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a deep copy of this Drawable object.
|
||||
|
||||
Use this to create a new copy of this and any sub-objects in the tree.
|
||||
*/
|
||||
virtual Drawable* createCopy() const = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Renders this Drawable object.
|
||||
|
||||
Note that the preferred way to render a drawable in future is by using it
|
||||
as a component and adding it to a parent, so you might want to consider that
|
||||
before using this method.
|
||||
|
||||
@see drawWithin
|
||||
*/
|
||||
void draw (Graphics& g, float opacity,
|
||||
const AffineTransform& transform = AffineTransform::identity) const;
|
||||
|
||||
/** Renders the Drawable at a given offset within the Graphics context.
|
||||
|
||||
The coordinates passed-in are used to translate the object relative to its own
|
||||
origin before drawing it - this is basically a quick way of saying:
|
||||
|
||||
@code
|
||||
draw (g, AffineTransform::translation (x, y)).
|
||||
@endcode
|
||||
|
||||
Note that the preferred way to render a drawable in future is by using it
|
||||
as a component and adding it to a parent, so you might want to consider that
|
||||
before using this method.
|
||||
*/
|
||||
void drawAt (Graphics& g, float x, float y, float opacity) const;
|
||||
|
||||
/** Renders the Drawable within a rectangle, scaling it to fit neatly inside without
|
||||
changing its aspect-ratio.
|
||||
|
||||
The object can placed arbitrarily within the rectangle based on a Justification type,
|
||||
and can either be made as big as possible, or just reduced to fit.
|
||||
|
||||
Note that the preferred way to render a drawable in future is by using it
|
||||
as a component and adding it to a parent, so you might want to consider that
|
||||
before using this method.
|
||||
|
||||
@param g the graphics context to render onto
|
||||
@param destArea the target rectangle to fit the drawable into
|
||||
@param placement defines the alignment and rescaling to use to fit
|
||||
this object within the target rectangle.
|
||||
@param opacity the opacity to use, in the range 0 to 1.0
|
||||
*/
|
||||
void drawWithin (Graphics& g,
|
||||
const Rectangle<float>& destArea,
|
||||
RectanglePlacement placement,
|
||||
float opacity) const;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Resets any transformations on this drawable, and positions its origin within
|
||||
its parent component.
|
||||
*/
|
||||
void setOriginWithOriginalSize (Point<float> originWithinParent);
|
||||
|
||||
/** Sets a transform for this drawable that will position it within the specified
|
||||
area of its parent component.
|
||||
*/
|
||||
void setTransformToFit (const Rectangle<float>& areaInParent, RectanglePlacement placement);
|
||||
|
||||
/** Returns the DrawableComposite that contains this object, if there is one. */
|
||||
DrawableComposite* getParent() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to turn some kind of image file into a drawable.
|
||||
|
||||
The data could be an image that the ImageFileFormat class understands, or it
|
||||
could be SVG.
|
||||
*/
|
||||
static Drawable* createFromImageData (const void* data, size_t numBytes);
|
||||
|
||||
/** Tries to turn a stream containing some kind of image data into a drawable.
|
||||
|
||||
The data could be an image that the ImageFileFormat class understands, or it
|
||||
could be SVG.
|
||||
*/
|
||||
static Drawable* createFromImageDataStream (InputStream& dataSource);
|
||||
|
||||
/** Tries to turn a file containing some kind of image data into a drawable.
|
||||
|
||||
The data could be an image that the ImageFileFormat class understands, or it
|
||||
could be SVG.
|
||||
*/
|
||||
static Drawable* createFromImageFile (const File& file);
|
||||
|
||||
/** Attempts to parse an SVG (Scalable Vector Graphics) document, and to turn this
|
||||
into a Drawable tree.
|
||||
|
||||
The object returned must be deleted by the caller. If something goes wrong
|
||||
while parsing, it may return nullptr.
|
||||
|
||||
SVG is a pretty large and complex spec, and this doesn't aim to be a full
|
||||
implementation, but it can return the basic vector objects.
|
||||
*/
|
||||
static Drawable* createFromSVG (const XmlElement& svgDocument);
|
||||
|
||||
/** Parses an SVG path string and returns it. */
|
||||
static Path parseSVGPath (const String& svgPath);
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to create a Drawable from a previously-saved ValueTree.
|
||||
The ValueTree must have been created by the createValueTree() method.
|
||||
If there are any images used within the drawable, you'll need to provide a valid
|
||||
ImageProvider object that can be used to retrieve these images from whatever type
|
||||
of identifier is used to represent them.
|
||||
Internally, this uses a ComponentBuilder, and registerDrawableTypeHandlers().
|
||||
*/
|
||||
static Drawable* createFromValueTree (const ValueTree& tree, ComponentBuilder::ImageProvider* imageProvider);
|
||||
|
||||
/** Creates a ValueTree to represent this Drawable.
|
||||
The ValueTree that is returned can be turned back into a Drawable with createFromValueTree().
|
||||
If there are any images used in this drawable, you'll need to provide a valid ImageProvider
|
||||
object that can be used to create storable representations of them.
|
||||
*/
|
||||
virtual ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const = 0;
|
||||
|
||||
/** Returns the area that this drawble covers.
|
||||
The result is expressed in this drawable's own coordinate space, and does not take
|
||||
into account any transforms that may be applied to the component.
|
||||
*/
|
||||
virtual Rectangle<float> getDrawableBounds() const = 0;
|
||||
|
||||
/** Recursively replaces a colour that might be used for filling or stroking.
|
||||
return true if any instances of this colour were found.
|
||||
*/
|
||||
virtual bool replaceColour (Colour originalColour, Colour replacementColour);
|
||||
|
||||
//==============================================================================
|
||||
/** Internal class used to manage ValueTrees that represent Drawables. */
|
||||
class ValueTreeWrapperBase
|
||||
{
|
||||
public:
|
||||
ValueTreeWrapperBase (const ValueTree& state);
|
||||
|
||||
ValueTree& getState() noexcept { return state; }
|
||||
|
||||
String getID() const;
|
||||
void setID (const String& newID);
|
||||
|
||||
ValueTree state;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Registers a set of ComponentBuilder::TypeHandler objects that can be used to
|
||||
load all the different Drawable types from a saved state.
|
||||
@see ComponentBuilder::registerTypeHandler()
|
||||
*/
|
||||
static void registerDrawableTypeHandlers (ComponentBuilder& componentBuilder);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
friend class DrawableComposite;
|
||||
friend class DrawableShape;
|
||||
|
||||
/** @internal */
|
||||
void transformContextToCorrectOrigin (Graphics&);
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
void setBoundsToEnclose (const Rectangle<float>&);
|
||||
|
||||
Point<int> originRelativeToComponent;
|
||||
|
||||
#ifndef DOXYGEN
|
||||
/** Internal utility class used by Drawables. */
|
||||
template <class DrawableType>
|
||||
class Positioner : public RelativeCoordinatePositionerBase
|
||||
{
|
||||
public:
|
||||
Positioner (DrawableType& c)
|
||||
: RelativeCoordinatePositionerBase (c),
|
||||
owner (c)
|
||||
{}
|
||||
|
||||
bool registerCoordinates() { return owner.registerCoordinates (*this); }
|
||||
void applyToComponentBounds()
|
||||
{
|
||||
ComponentScope scope (getComponent());
|
||||
owner.recalculateCoordinates (&scope);
|
||||
}
|
||||
|
||||
void applyNewBounds (const Rectangle<int>&)
|
||||
{
|
||||
jassertfalse; // drawables can't be resized directly!
|
||||
}
|
||||
|
||||
private:
|
||||
DrawableType& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Positioner)
|
||||
};
|
||||
|
||||
Drawable (const Drawable&);
|
||||
#endif
|
||||
|
||||
private:
|
||||
void nonConstDraw (Graphics&, float opacity, const AffineTransform&);
|
||||
|
||||
Drawable& operator= (const Drawable&);
|
||||
JUCE_LEAK_DETECTOR (Drawable)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_DRAWABLE_H_INCLUDED
|
||||
|
|
@ -0,0 +1,327 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
DrawableComposite::DrawableComposite()
|
||||
: bounds (Point<float>(), Point<float> (100.0f, 0.0f), Point<float> (0.0f, 100.0f)),
|
||||
updateBoundsReentrant (false)
|
||||
{
|
||||
setContentArea (RelativeRectangle (RelativeCoordinate (0.0),
|
||||
RelativeCoordinate (100.0),
|
||||
RelativeCoordinate (0.0),
|
||||
RelativeCoordinate (100.0)));
|
||||
}
|
||||
|
||||
DrawableComposite::DrawableComposite (const DrawableComposite& other)
|
||||
: Drawable (other),
|
||||
bounds (other.bounds),
|
||||
markersX (other.markersX),
|
||||
markersY (other.markersY),
|
||||
updateBoundsReentrant (false)
|
||||
{
|
||||
for (int i = 0; i < other.getNumChildComponents(); ++i)
|
||||
if (const Drawable* const d = dynamic_cast <const Drawable*> (other.getChildComponent(i)))
|
||||
addAndMakeVisible (d->createCopy());
|
||||
}
|
||||
|
||||
DrawableComposite::~DrawableComposite()
|
||||
{
|
||||
deleteAllChildren();
|
||||
}
|
||||
|
||||
Drawable* DrawableComposite::createCopy() const
|
||||
{
|
||||
return new DrawableComposite (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Rectangle<float> DrawableComposite::getDrawableBounds() const
|
||||
{
|
||||
Rectangle<float> r;
|
||||
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
if (const Drawable* const d = dynamic_cast <const Drawable*> (getChildComponent(i)))
|
||||
r = r.getUnion (d->isTransformed() ? d->getDrawableBounds().transformedBy (d->getTransform())
|
||||
: d->getDrawableBounds());
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
MarkerList* DrawableComposite::getMarkers (bool xAxis)
|
||||
{
|
||||
return xAxis ? &markersX : &markersY;
|
||||
}
|
||||
|
||||
RelativeRectangle DrawableComposite::getContentArea() const
|
||||
{
|
||||
jassert (markersX.getNumMarkers() >= 2 && markersX.getMarker (0)->name == contentLeftMarkerName && markersX.getMarker (1)->name == contentRightMarkerName);
|
||||
jassert (markersY.getNumMarkers() >= 2 && markersY.getMarker (0)->name == contentTopMarkerName && markersY.getMarker (1)->name == contentBottomMarkerName);
|
||||
|
||||
return RelativeRectangle (markersX.getMarker(0)->position, markersX.getMarker(1)->position,
|
||||
markersY.getMarker(0)->position, markersY.getMarker(1)->position);
|
||||
}
|
||||
|
||||
void DrawableComposite::setContentArea (const RelativeRectangle& newArea)
|
||||
{
|
||||
markersX.setMarker (contentLeftMarkerName, newArea.left);
|
||||
markersX.setMarker (contentRightMarkerName, newArea.right);
|
||||
markersY.setMarker (contentTopMarkerName, newArea.top);
|
||||
markersY.setMarker (contentBottomMarkerName, newArea.bottom);
|
||||
}
|
||||
|
||||
void DrawableComposite::setBoundingBox (const RelativeParallelogram& newBounds)
|
||||
{
|
||||
if (bounds != newBounds)
|
||||
{
|
||||
bounds = newBounds;
|
||||
|
||||
if (bounds.isDynamic())
|
||||
{
|
||||
Drawable::Positioner<DrawableComposite>* const p = new Drawable::Positioner<DrawableComposite> (*this);
|
||||
setPositioner (p);
|
||||
p->apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
setPositioner (nullptr);
|
||||
recalculateCoordinates (nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableComposite::resetBoundingBoxToContentArea()
|
||||
{
|
||||
const RelativeRectangle content (getContentArea());
|
||||
|
||||
setBoundingBox (RelativeParallelogram (RelativePoint (content.left, content.top),
|
||||
RelativePoint (content.right, content.top),
|
||||
RelativePoint (content.left, content.bottom)));
|
||||
}
|
||||
|
||||
void DrawableComposite::resetContentAreaAndBoundingBoxToFitChildren()
|
||||
{
|
||||
const Rectangle<float> activeArea (getDrawableBounds());
|
||||
|
||||
setContentArea (RelativeRectangle (RelativeCoordinate (activeArea.getX()),
|
||||
RelativeCoordinate (activeArea.getRight()),
|
||||
RelativeCoordinate (activeArea.getY()),
|
||||
RelativeCoordinate (activeArea.getBottom())));
|
||||
resetBoundingBoxToContentArea();
|
||||
}
|
||||
|
||||
bool DrawableComposite::registerCoordinates (RelativeCoordinatePositionerBase& pos)
|
||||
{
|
||||
bool ok = pos.addPoint (bounds.topLeft);
|
||||
ok = pos.addPoint (bounds.topRight) && ok;
|
||||
return pos.addPoint (bounds.bottomLeft) && ok;
|
||||
}
|
||||
|
||||
void DrawableComposite::recalculateCoordinates (Expression::Scope* scope)
|
||||
{
|
||||
Point<float> resolved[3];
|
||||
bounds.resolveThreePoints (resolved, scope);
|
||||
|
||||
const Rectangle<float> content (getContentArea().resolve (scope));
|
||||
|
||||
AffineTransform t (AffineTransform::fromTargetPoints (content.getX(), content.getY(), resolved[0].x, resolved[0].y,
|
||||
content.getRight(), content.getY(), resolved[1].x, resolved[1].y,
|
||||
content.getX(), content.getBottom(), resolved[2].x, resolved[2].y));
|
||||
|
||||
if (t.isSingularity())
|
||||
t = AffineTransform::identity;
|
||||
|
||||
setTransform (t);
|
||||
}
|
||||
|
||||
void DrawableComposite::parentHierarchyChanged()
|
||||
{
|
||||
DrawableComposite* parent = getParent();
|
||||
if (parent != nullptr)
|
||||
originRelativeToComponent = parent->originRelativeToComponent - getPosition();
|
||||
}
|
||||
|
||||
void DrawableComposite::childBoundsChanged (Component*)
|
||||
{
|
||||
updateBoundsToFitChildren();
|
||||
}
|
||||
|
||||
void DrawableComposite::childrenChanged()
|
||||
{
|
||||
updateBoundsToFitChildren();
|
||||
}
|
||||
|
||||
void DrawableComposite::updateBoundsToFitChildren()
|
||||
{
|
||||
if (! updateBoundsReentrant)
|
||||
{
|
||||
const ScopedValueSetter<bool> setter (updateBoundsReentrant, true, false);
|
||||
|
||||
Rectangle<int> childArea;
|
||||
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
childArea = childArea.getUnion (getChildComponent(i)->getBoundsInParent());
|
||||
|
||||
const Point<int> delta (childArea.getPosition());
|
||||
childArea += getPosition();
|
||||
|
||||
if (childArea != getBounds())
|
||||
{
|
||||
if (! delta.isOrigin())
|
||||
{
|
||||
originRelativeToComponent -= delta;
|
||||
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
if (Component* const c = getChildComponent(i))
|
||||
c->setBounds (c->getBounds() - delta);
|
||||
}
|
||||
|
||||
setBounds (childArea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const char* const DrawableComposite::contentLeftMarkerName = "left";
|
||||
const char* const DrawableComposite::contentRightMarkerName = "right";
|
||||
const char* const DrawableComposite::contentTopMarkerName = "top";
|
||||
const char* const DrawableComposite::contentBottomMarkerName = "bottom";
|
||||
|
||||
//==============================================================================
|
||||
const Identifier DrawableComposite::valueTreeType ("Group");
|
||||
|
||||
const Identifier DrawableComposite::ValueTreeWrapper::topLeft ("topLeft");
|
||||
const Identifier DrawableComposite::ValueTreeWrapper::topRight ("topRight");
|
||||
const Identifier DrawableComposite::ValueTreeWrapper::bottomLeft ("bottomLeft");
|
||||
const Identifier DrawableComposite::ValueTreeWrapper::childGroupTag ("Drawables");
|
||||
const Identifier DrawableComposite::ValueTreeWrapper::markerGroupTagX ("MarkersX");
|
||||
const Identifier DrawableComposite::ValueTreeWrapper::markerGroupTagY ("MarkersY");
|
||||
|
||||
//==============================================================================
|
||||
DrawableComposite::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
|
||||
: ValueTreeWrapperBase (state_)
|
||||
{
|
||||
jassert (state.hasType (valueTreeType));
|
||||
}
|
||||
|
||||
ValueTree DrawableComposite::ValueTreeWrapper::getChildList() const
|
||||
{
|
||||
return state.getChildWithName (childGroupTag);
|
||||
}
|
||||
|
||||
ValueTree DrawableComposite::ValueTreeWrapper::getChildListCreating (UndoManager* undoManager)
|
||||
{
|
||||
return state.getOrCreateChildWithName (childGroupTag, undoManager);
|
||||
}
|
||||
|
||||
RelativeParallelogram DrawableComposite::ValueTreeWrapper::getBoundingBox() const
|
||||
{
|
||||
return RelativeParallelogram (state.getProperty (topLeft, "0, 0"),
|
||||
state.getProperty (topRight, "100, 0"),
|
||||
state.getProperty (bottomLeft, "0, 100"));
|
||||
}
|
||||
|
||||
void DrawableComposite::ValueTreeWrapper::setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager);
|
||||
state.setProperty (topRight, newBounds.topRight.toString(), undoManager);
|
||||
state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager);
|
||||
}
|
||||
|
||||
void DrawableComposite::ValueTreeWrapper::resetBoundingBoxToContentArea (UndoManager* undoManager)
|
||||
{
|
||||
const RelativeRectangle content (getContentArea());
|
||||
|
||||
setBoundingBox (RelativeParallelogram (RelativePoint (content.left, content.top),
|
||||
RelativePoint (content.right, content.top),
|
||||
RelativePoint (content.left, content.bottom)), undoManager);
|
||||
}
|
||||
|
||||
RelativeRectangle DrawableComposite::ValueTreeWrapper::getContentArea() const
|
||||
{
|
||||
MarkerList::ValueTreeWrapper marksX (getMarkerList (true));
|
||||
MarkerList::ValueTreeWrapper marksY (getMarkerList (false));
|
||||
|
||||
return RelativeRectangle (marksX.getMarker (marksX.getMarkerState (0)).position,
|
||||
marksX.getMarker (marksX.getMarkerState (1)).position,
|
||||
marksY.getMarker (marksY.getMarkerState (0)).position,
|
||||
marksY.getMarker (marksY.getMarkerState (1)).position);
|
||||
}
|
||||
|
||||
void DrawableComposite::ValueTreeWrapper::setContentArea (const RelativeRectangle& newArea, UndoManager* undoManager)
|
||||
{
|
||||
MarkerList::ValueTreeWrapper marksX (getMarkerListCreating (true, nullptr));
|
||||
MarkerList::ValueTreeWrapper marksY (getMarkerListCreating (false, nullptr));
|
||||
|
||||
marksX.setMarker (MarkerList::Marker (contentLeftMarkerName, newArea.left), undoManager);
|
||||
marksX.setMarker (MarkerList::Marker (contentRightMarkerName, newArea.right), undoManager);
|
||||
marksY.setMarker (MarkerList::Marker (contentTopMarkerName, newArea.top), undoManager);
|
||||
marksY.setMarker (MarkerList::Marker (contentBottomMarkerName, newArea.bottom), undoManager);
|
||||
}
|
||||
|
||||
MarkerList::ValueTreeWrapper DrawableComposite::ValueTreeWrapper::getMarkerList (bool xAxis) const
|
||||
{
|
||||
return state.getChildWithName (xAxis ? markerGroupTagX : markerGroupTagY);
|
||||
}
|
||||
|
||||
MarkerList::ValueTreeWrapper DrawableComposite::ValueTreeWrapper::getMarkerListCreating (bool xAxis, UndoManager* undoManager)
|
||||
{
|
||||
return state.getOrCreateChildWithName (xAxis ? markerGroupTagX : markerGroupTagY, undoManager);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableComposite::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder)
|
||||
{
|
||||
const ValueTreeWrapper wrapper (tree);
|
||||
setComponentID (wrapper.getID());
|
||||
|
||||
wrapper.getMarkerList (true).applyTo (markersX);
|
||||
wrapper.getMarkerList (false).applyTo (markersY);
|
||||
|
||||
setBoundingBox (wrapper.getBoundingBox());
|
||||
|
||||
builder.updateChildComponents (*this, wrapper.getChildList());
|
||||
}
|
||||
|
||||
ValueTree DrawableComposite::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const
|
||||
{
|
||||
ValueTree tree (valueTreeType);
|
||||
ValueTreeWrapper v (tree);
|
||||
|
||||
v.setID (getComponentID());
|
||||
v.setBoundingBox (bounds, nullptr);
|
||||
|
||||
ValueTree childList (v.getChildListCreating (nullptr));
|
||||
|
||||
for (int i = 0; i < getNumChildComponents(); ++i)
|
||||
{
|
||||
const Drawable* const d = dynamic_cast <const Drawable*> (getChildComponent(i));
|
||||
jassert (d != nullptr); // You can't save a mix of Drawables and normal components!
|
||||
|
||||
childList.addChild (d->createValueTree (imageProvider), -1, nullptr);
|
||||
}
|
||||
|
||||
v.getMarkerListCreating (true, nullptr).readFrom (markersX, nullptr);
|
||||
v.getMarkerListCreating (false, nullptr).readFrom (markersY, nullptr);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_DRAWABLECOMPOSITE_H_INCLUDED
|
||||
#define JUCE_DRAWABLECOMPOSITE_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A drawable object which acts as a container for a set of other Drawables.
|
||||
|
||||
@see Drawable
|
||||
*/
|
||||
class JUCE_API DrawableComposite : public Drawable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a composite Drawable. */
|
||||
DrawableComposite();
|
||||
|
||||
/** Creates a copy of a DrawableComposite. */
|
||||
DrawableComposite (const DrawableComposite&);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawableComposite();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the parallelogram that defines the target position of the content rectangle when the drawable is rendered.
|
||||
@see setContentArea
|
||||
*/
|
||||
void setBoundingBox (const RelativeParallelogram& newBoundingBox);
|
||||
|
||||
/** Returns the parallelogram that defines the target position of the content rectangle when the drawable is rendered.
|
||||
@see setBoundingBox
|
||||
*/
|
||||
const RelativeParallelogram& getBoundingBox() const noexcept { return bounds; }
|
||||
|
||||
/** Changes the bounding box transform to match the content area, so that any sub-items will
|
||||
be drawn at their untransformed positions.
|
||||
*/
|
||||
void resetBoundingBoxToContentArea();
|
||||
|
||||
/** Returns the main content rectangle.
|
||||
The content area is actually defined by the markers named "left", "right", "top" and
|
||||
"bottom", but this method is a shortcut that returns them all at once.
|
||||
@see contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName
|
||||
*/
|
||||
RelativeRectangle getContentArea() const;
|
||||
|
||||
/** Changes the main content area.
|
||||
The content area is actually defined by the markers named "left", "right", "top" and
|
||||
"bottom", but this method is a shortcut that sets them all at once.
|
||||
@see setBoundingBox, contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName
|
||||
*/
|
||||
void setContentArea (const RelativeRectangle& newArea);
|
||||
|
||||
/** Resets the content area and the bounding transform to fit around the area occupied
|
||||
by the child components (ignoring any markers).
|
||||
*/
|
||||
void resetContentAreaAndBoundingBoxToFitChildren();
|
||||
|
||||
//==============================================================================
|
||||
/** The name of the marker that defines the left edge of the content area. */
|
||||
static const char* const contentLeftMarkerName;
|
||||
/** The name of the marker that defines the right edge of the content area. */
|
||||
static const char* const contentRightMarkerName;
|
||||
/** The name of the marker that defines the top edge of the content area. */
|
||||
static const char* const contentTopMarkerName;
|
||||
/** The name of the marker that defines the bottom edge of the content area. */
|
||||
static const char* const contentBottomMarkerName;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
Drawable* createCopy() const;
|
||||
/** @internal */
|
||||
void refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder);
|
||||
/** @internal */
|
||||
ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const;
|
||||
/** @internal */
|
||||
static const Identifier valueTreeType;
|
||||
/** @internal */
|
||||
Rectangle<float> getDrawableBounds() const;
|
||||
/** @internal */
|
||||
void childBoundsChanged (Component*) override;
|
||||
/** @internal */
|
||||
void childrenChanged() override;
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
MarkerList* getMarkers (bool xAxis) override;
|
||||
|
||||
//==============================================================================
|
||||
/** Internally-used class for wrapping a DrawableComposite's state into a ValueTree. */
|
||||
class ValueTreeWrapper : public Drawable::ValueTreeWrapperBase
|
||||
{
|
||||
public:
|
||||
ValueTreeWrapper (const ValueTree& state);
|
||||
|
||||
ValueTree getChildList() const;
|
||||
ValueTree getChildListCreating (UndoManager* undoManager);
|
||||
|
||||
RelativeParallelogram getBoundingBox() const;
|
||||
void setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager);
|
||||
void resetBoundingBoxToContentArea (UndoManager* undoManager);
|
||||
|
||||
RelativeRectangle getContentArea() const;
|
||||
void setContentArea (const RelativeRectangle& newArea, UndoManager* undoManager);
|
||||
|
||||
MarkerList::ValueTreeWrapper getMarkerList (bool xAxis) const;
|
||||
MarkerList::ValueTreeWrapper getMarkerListCreating (bool xAxis, UndoManager* undoManager);
|
||||
|
||||
static const Identifier topLeft, topRight, bottomLeft;
|
||||
|
||||
private:
|
||||
static const Identifier childGroupTag, markerGroupTagX, markerGroupTagY;
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
RelativeParallelogram bounds;
|
||||
MarkerList markersX, markersY;
|
||||
bool updateBoundsReentrant;
|
||||
|
||||
friend class Drawable::Positioner<DrawableComposite>;
|
||||
bool registerCoordinates (RelativeCoordinatePositionerBase&);
|
||||
void recalculateCoordinates (Expression::Scope*);
|
||||
|
||||
void updateBoundsToFitChildren();
|
||||
|
||||
DrawableComposite& operator= (const DrawableComposite&);
|
||||
JUCE_LEAK_DETECTOR (DrawableComposite)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_DRAWABLECOMPOSITE_H_INCLUDED
|
||||
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
DrawableImage::DrawableImage()
|
||||
: opacity (1.0f),
|
||||
overlayColour (0x00000000)
|
||||
{
|
||||
bounds.topRight = RelativePoint (Point<float> (1.0f, 0.0f));
|
||||
bounds.bottomLeft = RelativePoint (Point<float> (0.0f, 1.0f));
|
||||
}
|
||||
|
||||
DrawableImage::DrawableImage (const DrawableImage& other)
|
||||
: Drawable (other),
|
||||
image (other.image),
|
||||
opacity (other.opacity),
|
||||
overlayColour (other.overlayColour),
|
||||
bounds (other.bounds)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableImage::~DrawableImage()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableImage::setImage (const Image& imageToUse)
|
||||
{
|
||||
image = imageToUse;
|
||||
setBounds (imageToUse.getBounds());
|
||||
|
||||
bounds.topLeft = RelativePoint (Point<float> (0.0f, 0.0f));
|
||||
bounds.topRight = RelativePoint (Point<float> ((float) image.getWidth(), 0.0f));
|
||||
bounds.bottomLeft = RelativePoint (Point<float> (0.0f, (float) image.getHeight()));
|
||||
recalculateCoordinates (nullptr);
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
void DrawableImage::setOpacity (const float newOpacity)
|
||||
{
|
||||
opacity = newOpacity;
|
||||
}
|
||||
|
||||
void DrawableImage::setOverlayColour (Colour newOverlayColour)
|
||||
{
|
||||
overlayColour = newOverlayColour;
|
||||
}
|
||||
|
||||
void DrawableImage::setBoundingBox (const RelativeParallelogram& newBounds)
|
||||
{
|
||||
if (bounds != newBounds)
|
||||
{
|
||||
bounds = newBounds;
|
||||
|
||||
if (bounds.isDynamic())
|
||||
{
|
||||
Drawable::Positioner<DrawableImage>* const p = new Drawable::Positioner<DrawableImage> (*this);
|
||||
setPositioner (p);
|
||||
p->apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
setPositioner (nullptr);
|
||||
recalculateCoordinates (nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool DrawableImage::registerCoordinates (RelativeCoordinatePositionerBase& pos)
|
||||
{
|
||||
bool ok = pos.addPoint (bounds.topLeft);
|
||||
ok = pos.addPoint (bounds.topRight) && ok;
|
||||
return pos.addPoint (bounds.bottomLeft) && ok;
|
||||
}
|
||||
|
||||
void DrawableImage::recalculateCoordinates (Expression::Scope* scope)
|
||||
{
|
||||
if (image.isValid())
|
||||
{
|
||||
Point<float> resolved[3];
|
||||
bounds.resolveThreePoints (resolved, scope);
|
||||
|
||||
const Point<float> tr (resolved[0] + (resolved[1] - resolved[0]) / (float) image.getWidth());
|
||||
const Point<float> bl (resolved[0] + (resolved[2] - resolved[0]) / (float) image.getHeight());
|
||||
|
||||
AffineTransform t (AffineTransform::fromTargetPoints (resolved[0].x, resolved[0].y,
|
||||
tr.x, tr.y,
|
||||
bl.x, bl.y));
|
||||
|
||||
if (t.isSingularity())
|
||||
t = AffineTransform::identity;
|
||||
|
||||
setTransform (t);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableImage::paint (Graphics& g)
|
||||
{
|
||||
if (image.isValid())
|
||||
{
|
||||
if (opacity > 0.0f && ! overlayColour.isOpaque())
|
||||
{
|
||||
g.setOpacity (opacity);
|
||||
g.drawImageAt (image, 0, 0, false);
|
||||
}
|
||||
|
||||
if (! overlayColour.isTransparent())
|
||||
{
|
||||
g.setColour (overlayColour.withMultipliedAlpha (opacity));
|
||||
g.drawImageAt (image, 0, 0, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle<float> DrawableImage::getDrawableBounds() const
|
||||
{
|
||||
return image.getBounds().toFloat();
|
||||
}
|
||||
|
||||
bool DrawableImage::hitTest (int x, int y)
|
||||
{
|
||||
return Drawable::hitTest (x, y) && image.isValid() && image.getPixelAt (x, y).getAlpha() >= 127;
|
||||
}
|
||||
|
||||
Drawable* DrawableImage::createCopy() const
|
||||
{
|
||||
return new DrawableImage (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Identifier DrawableImage::valueTreeType ("Image");
|
||||
|
||||
const Identifier DrawableImage::ValueTreeWrapper::opacity ("opacity");
|
||||
const Identifier DrawableImage::ValueTreeWrapper::overlay ("overlay");
|
||||
const Identifier DrawableImage::ValueTreeWrapper::image ("image");
|
||||
const Identifier DrawableImage::ValueTreeWrapper::topLeft ("topLeft");
|
||||
const Identifier DrawableImage::ValueTreeWrapper::topRight ("topRight");
|
||||
const Identifier DrawableImage::ValueTreeWrapper::bottomLeft ("bottomLeft");
|
||||
|
||||
//==============================================================================
|
||||
DrawableImage::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
|
||||
: ValueTreeWrapperBase (state_)
|
||||
{
|
||||
jassert (state.hasType (valueTreeType));
|
||||
}
|
||||
|
||||
var DrawableImage::ValueTreeWrapper::getImageIdentifier() const
|
||||
{
|
||||
return state [image];
|
||||
}
|
||||
|
||||
Value DrawableImage::ValueTreeWrapper::getImageIdentifierValue (UndoManager* undoManager)
|
||||
{
|
||||
return state.getPropertyAsValue (image, undoManager);
|
||||
}
|
||||
|
||||
void DrawableImage::ValueTreeWrapper::setImageIdentifier (const var& newIdentifier, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (image, newIdentifier, undoManager);
|
||||
}
|
||||
|
||||
float DrawableImage::ValueTreeWrapper::getOpacity() const
|
||||
{
|
||||
return (float) state.getProperty (opacity, 1.0);
|
||||
}
|
||||
|
||||
Value DrawableImage::ValueTreeWrapper::getOpacityValue (UndoManager* undoManager)
|
||||
{
|
||||
if (! state.hasProperty (opacity))
|
||||
state.setProperty (opacity, 1.0, undoManager);
|
||||
|
||||
return state.getPropertyAsValue (opacity, undoManager);
|
||||
}
|
||||
|
||||
void DrawableImage::ValueTreeWrapper::setOpacity (float newOpacity, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (opacity, newOpacity, undoManager);
|
||||
}
|
||||
|
||||
Colour DrawableImage::ValueTreeWrapper::getOverlayColour() const
|
||||
{
|
||||
return Colour::fromString (state [overlay].toString());
|
||||
}
|
||||
|
||||
void DrawableImage::ValueTreeWrapper::setOverlayColour (Colour newColour, UndoManager* undoManager)
|
||||
{
|
||||
if (newColour.isTransparent())
|
||||
state.removeProperty (overlay, undoManager);
|
||||
else
|
||||
state.setProperty (overlay, String::toHexString ((int) newColour.getARGB()), undoManager);
|
||||
}
|
||||
|
||||
Value DrawableImage::ValueTreeWrapper::getOverlayColourValue (UndoManager* undoManager)
|
||||
{
|
||||
return state.getPropertyAsValue (overlay, undoManager);
|
||||
}
|
||||
|
||||
RelativeParallelogram DrawableImage::ValueTreeWrapper::getBoundingBox() const
|
||||
{
|
||||
return RelativeParallelogram (state.getProperty (topLeft, "0, 0"),
|
||||
state.getProperty (topRight, "100, 0"),
|
||||
state.getProperty (bottomLeft, "0, 100"));
|
||||
}
|
||||
|
||||
void DrawableImage::ValueTreeWrapper::setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager);
|
||||
state.setProperty (topRight, newBounds.topRight.toString(), undoManager);
|
||||
state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager);
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void DrawableImage::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder)
|
||||
{
|
||||
const ValueTreeWrapper controller (tree);
|
||||
setComponentID (controller.getID());
|
||||
|
||||
const float newOpacity = controller.getOpacity();
|
||||
const Colour newOverlayColour (controller.getOverlayColour());
|
||||
|
||||
Image newImage;
|
||||
const var imageIdentifier (controller.getImageIdentifier());
|
||||
|
||||
|
||||
jassert (builder.getImageProvider() != 0 || imageIdentifier.isVoid()); // if you're using images, you need to provide something that can load and save them!
|
||||
|
||||
if (builder.getImageProvider() != nullptr)
|
||||
newImage = builder.getImageProvider()->getImageForIdentifier (imageIdentifier);
|
||||
|
||||
const RelativeParallelogram newBounds (controller.getBoundingBox());
|
||||
|
||||
if (bounds != newBounds || newOpacity != opacity
|
||||
|| overlayColour != newOverlayColour || image != newImage)
|
||||
{
|
||||
repaint();
|
||||
opacity = newOpacity;
|
||||
overlayColour = newOverlayColour;
|
||||
|
||||
if (image != newImage)
|
||||
setImage (newImage);
|
||||
|
||||
setBoundingBox (newBounds);
|
||||
}
|
||||
}
|
||||
|
||||
ValueTree DrawableImage::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const
|
||||
{
|
||||
ValueTree tree (valueTreeType);
|
||||
ValueTreeWrapper v (tree);
|
||||
|
||||
v.setID (getComponentID());
|
||||
v.setOpacity (opacity, nullptr);
|
||||
v.setOverlayColour (overlayColour, nullptr);
|
||||
v.setBoundingBox (bounds, nullptr);
|
||||
|
||||
if (image.isValid())
|
||||
{
|
||||
jassert (imageProvider != nullptr); // if you're using images, you need to provide something that can load and save them!
|
||||
|
||||
if (imageProvider != nullptr)
|
||||
v.setImageIdentifier (imageProvider->getIdentifierForImage (image), nullptr);
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_DRAWABLEIMAGE_H_INCLUDED
|
||||
#define JUCE_DRAWABLEIMAGE_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A drawable object which is a bitmap image.
|
||||
|
||||
@see Drawable
|
||||
*/
|
||||
class JUCE_API DrawableImage : public Drawable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
DrawableImage();
|
||||
DrawableImage (const DrawableImage&);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawableImage();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the image that this drawable will render. */
|
||||
void setImage (const Image& imageToUse);
|
||||
|
||||
/** Returns the current image. */
|
||||
const Image& getImage() const noexcept { return image; }
|
||||
|
||||
/** Sets the opacity to use when drawing the image. */
|
||||
void setOpacity (float newOpacity);
|
||||
|
||||
/** Returns the image's opacity. */
|
||||
float getOpacity() const noexcept { return opacity; }
|
||||
|
||||
/** Sets a colour to draw over the image's alpha channel.
|
||||
|
||||
By default this is transparent so isn't drawn, but if you set a non-transparent
|
||||
colour here, then it will be overlaid on the image, using the image's alpha
|
||||
channel as a mask.
|
||||
|
||||
This is handy for doing things like darkening or lightening an image by overlaying
|
||||
it with semi-transparent black or white.
|
||||
*/
|
||||
void setOverlayColour (Colour newOverlayColour);
|
||||
|
||||
/** Returns the overlay colour. */
|
||||
Colour getOverlayColour() const noexcept { return overlayColour; }
|
||||
|
||||
/** Sets the bounding box within which the image should be displayed. */
|
||||
void setBoundingBox (const RelativeParallelogram& newBounds);
|
||||
|
||||
/** Returns the position to which the image's top-left corner should be remapped in the target
|
||||
coordinate space when rendering this object.
|
||||
@see setTransform
|
||||
*/
|
||||
const RelativeParallelogram& getBoundingBox() const noexcept { return bounds; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
bool hitTest (int x, int y) override;
|
||||
/** @internal */
|
||||
Drawable* createCopy() const override;
|
||||
/** @internal */
|
||||
Rectangle<float> getDrawableBounds() const override;
|
||||
/** @internal */
|
||||
void refreshFromValueTree (const ValueTree& tree, ComponentBuilder&);
|
||||
/** @internal */
|
||||
ValueTree createValueTree (ComponentBuilder::ImageProvider*) const override;
|
||||
/** @internal */
|
||||
static const Identifier valueTreeType;
|
||||
|
||||
//==============================================================================
|
||||
/** Internally-used class for wrapping a DrawableImage's state into a ValueTree. */
|
||||
class ValueTreeWrapper : public Drawable::ValueTreeWrapperBase
|
||||
{
|
||||
public:
|
||||
ValueTreeWrapper (const ValueTree& state);
|
||||
|
||||
var getImageIdentifier() const;
|
||||
void setImageIdentifier (const var&, UndoManager*);
|
||||
Value getImageIdentifierValue (UndoManager*);
|
||||
|
||||
float getOpacity() const;
|
||||
void setOpacity (float newOpacity, UndoManager*);
|
||||
Value getOpacityValue (UndoManager*);
|
||||
|
||||
Colour getOverlayColour() const;
|
||||
void setOverlayColour (Colour newColour, UndoManager*);
|
||||
Value getOverlayColourValue (UndoManager*);
|
||||
|
||||
RelativeParallelogram getBoundingBox() const;
|
||||
void setBoundingBox (const RelativeParallelogram&, UndoManager*);
|
||||
|
||||
static const Identifier opacity, overlay, image, topLeft, topRight, bottomLeft;
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Image image;
|
||||
float opacity;
|
||||
Colour overlayColour;
|
||||
RelativeParallelogram bounds;
|
||||
|
||||
friend class Drawable::Positioner<DrawableImage>;
|
||||
bool registerCoordinates (RelativeCoordinatePositionerBase&);
|
||||
void recalculateCoordinates (Expression::Scope*);
|
||||
|
||||
DrawableImage& operator= (const DrawableImage&);
|
||||
JUCE_LEAK_DETECTOR (DrawableImage)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_DRAWABLEIMAGE_H_INCLUDED
|
||||
|
|
@ -0,0 +1,570 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
DrawablePath::DrawablePath()
|
||||
{
|
||||
}
|
||||
|
||||
DrawablePath::DrawablePath (const DrawablePath& other)
|
||||
: DrawableShape (other)
|
||||
{
|
||||
if (other.relativePath != nullptr)
|
||||
setPath (*other.relativePath);
|
||||
else
|
||||
setPath (other.path);
|
||||
}
|
||||
|
||||
DrawablePath::~DrawablePath()
|
||||
{
|
||||
}
|
||||
|
||||
Drawable* DrawablePath::createCopy() const
|
||||
{
|
||||
return new DrawablePath (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawablePath::setPath (const Path& newPath)
|
||||
{
|
||||
path = newPath;
|
||||
pathChanged();
|
||||
}
|
||||
|
||||
const Path& DrawablePath::getPath() const
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
const Path& DrawablePath::getStrokePath() const
|
||||
{
|
||||
return strokePath;
|
||||
}
|
||||
|
||||
void DrawablePath::applyRelativePath (const RelativePointPath& newRelativePath, Expression::Scope* scope)
|
||||
{
|
||||
Path newPath;
|
||||
newRelativePath.createPath (newPath, scope);
|
||||
|
||||
if (path != newPath)
|
||||
{
|
||||
path.swapWithPath (newPath);
|
||||
pathChanged();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class DrawablePath::RelativePositioner : public RelativeCoordinatePositionerBase
|
||||
{
|
||||
public:
|
||||
RelativePositioner (DrawablePath& comp)
|
||||
: RelativeCoordinatePositionerBase (comp),
|
||||
owner (comp)
|
||||
{
|
||||
}
|
||||
|
||||
bool registerCoordinates()
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
jassert (owner.relativePath != nullptr);
|
||||
const RelativePointPath& path = *owner.relativePath;
|
||||
|
||||
for (int i = 0; i < path.elements.size(); ++i)
|
||||
{
|
||||
RelativePointPath::ElementBase* const e = path.elements.getUnchecked(i);
|
||||
|
||||
int numPoints;
|
||||
RelativePoint* const points = e->getControlPoints (numPoints);
|
||||
|
||||
for (int j = numPoints; --j >= 0;)
|
||||
ok = addPoint (points[j]) && ok;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void applyToComponentBounds()
|
||||
{
|
||||
jassert (owner.relativePath != nullptr);
|
||||
|
||||
ComponentScope scope (getComponent());
|
||||
owner.applyRelativePath (*owner.relativePath, &scope);
|
||||
}
|
||||
|
||||
void applyNewBounds (const Rectangle<int>&)
|
||||
{
|
||||
jassertfalse; // drawables can't be resized directly!
|
||||
}
|
||||
|
||||
private:
|
||||
DrawablePath& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativePositioner)
|
||||
};
|
||||
|
||||
void DrawablePath::setPath (const RelativePointPath& newRelativePath)
|
||||
{
|
||||
if (newRelativePath.containsAnyDynamicPoints())
|
||||
{
|
||||
if (relativePath == nullptr || newRelativePath != *relativePath)
|
||||
{
|
||||
relativePath = new RelativePointPath (newRelativePath);
|
||||
|
||||
RelativePositioner* const p = new RelativePositioner (*this);
|
||||
setPositioner (p);
|
||||
p->apply();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
relativePath = nullptr;
|
||||
applyRelativePath (newRelativePath, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Identifier DrawablePath::valueTreeType ("Path");
|
||||
|
||||
const Identifier DrawablePath::ValueTreeWrapper::nonZeroWinding ("nonZeroWinding");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::point1 ("p1");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::point2 ("p2");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::point3 ("p3");
|
||||
|
||||
//==============================================================================
|
||||
DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
|
||||
: FillAndStrokeState (state_)
|
||||
{
|
||||
jassert (state.hasType (valueTreeType));
|
||||
}
|
||||
|
||||
ValueTree DrawablePath::ValueTreeWrapper::getPathState()
|
||||
{
|
||||
return state.getOrCreateChildWithName (path, nullptr);
|
||||
}
|
||||
|
||||
bool DrawablePath::ValueTreeWrapper::usesNonZeroWinding() const
|
||||
{
|
||||
return state [nonZeroWinding];
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::setUsesNonZeroWinding (bool b, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (nonZeroWinding, b, undoManager);
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::readFrom (const RelativePointPath& p, UndoManager* undoManager)
|
||||
{
|
||||
setUsesNonZeroWinding (p.usesNonZeroWinding, undoManager);
|
||||
|
||||
ValueTree pathTree (getPathState());
|
||||
pathTree.removeAllChildren (undoManager);
|
||||
|
||||
for (int i = 0; i < p.elements.size(); ++i)
|
||||
pathTree.addChild (p.elements.getUnchecked(i)->createTree(), -1, undoManager);
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::writeTo (RelativePointPath& p) const
|
||||
{
|
||||
p.usesNonZeroWinding = usesNonZeroWinding();
|
||||
RelativePoint points[3];
|
||||
|
||||
const ValueTree pathTree (state.getChildWithName (path));
|
||||
const int num = pathTree.getNumChildren();
|
||||
for (int i = 0; i < num; ++i)
|
||||
{
|
||||
const Element e (pathTree.getChild(i));
|
||||
|
||||
const int numCps = e.getNumControlPoints();
|
||||
for (int j = 0; j < numCps; ++j)
|
||||
points[j] = e.getControlPoint (j);
|
||||
|
||||
RelativePointPath::ElementBase* newElement = nullptr;
|
||||
const Identifier t (e.getType());
|
||||
|
||||
if (t == Element::startSubPathElement) newElement = new RelativePointPath::StartSubPath (points[0]);
|
||||
else if (t == Element::closeSubPathElement) newElement = new RelativePointPath::CloseSubPath();
|
||||
else if (t == Element::lineToElement) newElement = new RelativePointPath::LineTo (points[0]);
|
||||
else if (t == Element::quadraticToElement) newElement = new RelativePointPath::QuadraticTo (points[0], points[1]);
|
||||
else if (t == Element::cubicToElement) newElement = new RelativePointPath::CubicTo (points[0], points[1], points[2]);
|
||||
else jassertfalse;
|
||||
|
||||
p.addElement (newElement);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Identifier DrawablePath::ValueTreeWrapper::Element::mode ("mode");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::Element::startSubPathElement ("Move");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::Element::closeSubPathElement ("Close");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::Element::lineToElement ("Line");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::Element::quadraticToElement ("Quad");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::Element::cubicToElement ("Cubic");
|
||||
|
||||
const char* DrawablePath::ValueTreeWrapper::Element::cornerMode = "corner";
|
||||
const char* DrawablePath::ValueTreeWrapper::Element::roundedMode = "round";
|
||||
const char* DrawablePath::ValueTreeWrapper::Element::symmetricMode = "symm";
|
||||
|
||||
DrawablePath::ValueTreeWrapper::Element::Element (const ValueTree& state_)
|
||||
: state (state_)
|
||||
{
|
||||
}
|
||||
|
||||
DrawablePath::ValueTreeWrapper::Element::~Element()
|
||||
{
|
||||
}
|
||||
|
||||
DrawablePath::ValueTreeWrapper DrawablePath::ValueTreeWrapper::Element::getParent() const
|
||||
{
|
||||
return ValueTreeWrapper (state.getParent().getParent());
|
||||
}
|
||||
|
||||
DrawablePath::ValueTreeWrapper::Element DrawablePath::ValueTreeWrapper::Element::getPreviousElement() const
|
||||
{
|
||||
return Element (state.getSibling (-1));
|
||||
}
|
||||
|
||||
int DrawablePath::ValueTreeWrapper::Element::getNumControlPoints() const noexcept
|
||||
{
|
||||
const Identifier i (state.getType());
|
||||
if (i == startSubPathElement || i == lineToElement) return 1;
|
||||
if (i == quadraticToElement) return 2;
|
||||
if (i == cubicToElement) return 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
RelativePoint DrawablePath::ValueTreeWrapper::Element::getControlPoint (const int index) const
|
||||
{
|
||||
jassert (index >= 0 && index < getNumControlPoints());
|
||||
return RelativePoint (state [index == 0 ? point1 : (index == 1 ? point2 : point3)].toString());
|
||||
}
|
||||
|
||||
Value DrawablePath::ValueTreeWrapper::Element::getControlPointValue (int index, UndoManager* undoManager)
|
||||
{
|
||||
jassert (index >= 0 && index < getNumControlPoints());
|
||||
return state.getPropertyAsValue (index == 0 ? point1 : (index == 1 ? point2 : point3), undoManager);
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::Element::setControlPoint (const int index, const RelativePoint& point, UndoManager* undoManager)
|
||||
{
|
||||
jassert (index >= 0 && index < getNumControlPoints());
|
||||
state.setProperty (index == 0 ? point1 : (index == 1 ? point2 : point3), point.toString(), undoManager);
|
||||
}
|
||||
|
||||
RelativePoint DrawablePath::ValueTreeWrapper::Element::getStartPoint() const
|
||||
{
|
||||
const Identifier i (state.getType());
|
||||
|
||||
if (i == startSubPathElement)
|
||||
return getControlPoint (0);
|
||||
|
||||
jassert (i == lineToElement || i == quadraticToElement || i == cubicToElement || i == closeSubPathElement);
|
||||
|
||||
return getPreviousElement().getEndPoint();
|
||||
}
|
||||
|
||||
RelativePoint DrawablePath::ValueTreeWrapper::Element::getEndPoint() const
|
||||
{
|
||||
const Identifier i (state.getType());
|
||||
if (i == startSubPathElement || i == lineToElement) return getControlPoint (0);
|
||||
if (i == quadraticToElement) return getControlPoint (1);
|
||||
if (i == cubicToElement) return getControlPoint (2);
|
||||
|
||||
jassert (i == closeSubPathElement);
|
||||
return RelativePoint();
|
||||
}
|
||||
|
||||
float DrawablePath::ValueTreeWrapper::Element::getLength (Expression::Scope* scope) const
|
||||
{
|
||||
const Identifier i (state.getType());
|
||||
|
||||
if (i == lineToElement || i == closeSubPathElement)
|
||||
return getEndPoint().resolve (scope).getDistanceFrom (getStartPoint().resolve (scope));
|
||||
|
||||
if (i == cubicToElement)
|
||||
{
|
||||
Path p;
|
||||
p.startNewSubPath (getStartPoint().resolve (scope));
|
||||
p.cubicTo (getControlPoint (0).resolve (scope), getControlPoint (1).resolve (scope), getControlPoint (2).resolve (scope));
|
||||
return p.getLength();
|
||||
}
|
||||
|
||||
if (i == quadraticToElement)
|
||||
{
|
||||
Path p;
|
||||
p.startNewSubPath (getStartPoint().resolve (scope));
|
||||
p.quadraticTo (getControlPoint (0).resolve (scope), getControlPoint (1).resolve (scope));
|
||||
return p.getLength();
|
||||
}
|
||||
|
||||
jassert (i == startSubPathElement);
|
||||
return 0;
|
||||
}
|
||||
|
||||
String DrawablePath::ValueTreeWrapper::Element::getModeOfEndPoint() const
|
||||
{
|
||||
return state [mode].toString();
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::Element::setModeOfEndPoint (const String& newMode, UndoManager* undoManager)
|
||||
{
|
||||
if (state.hasType (cubicToElement))
|
||||
state.setProperty (mode, newMode, undoManager);
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::Element::convertToLine (UndoManager* undoManager)
|
||||
{
|
||||
const Identifier i (state.getType());
|
||||
|
||||
if (i == quadraticToElement || i == cubicToElement)
|
||||
{
|
||||
ValueTree newState (lineToElement);
|
||||
Element e (newState);
|
||||
e.setControlPoint (0, getEndPoint(), undoManager);
|
||||
state = newState;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::Element::convertToCubic (Expression::Scope* scope, UndoManager* undoManager)
|
||||
{
|
||||
const Identifier i (state.getType());
|
||||
|
||||
if (i == lineToElement || i == quadraticToElement)
|
||||
{
|
||||
ValueTree newState (cubicToElement);
|
||||
Element e (newState);
|
||||
|
||||
const RelativePoint start (getStartPoint());
|
||||
const RelativePoint end (getEndPoint());
|
||||
const Point<float> startResolved (start.resolve (scope));
|
||||
const Point<float> endResolved (end.resolve (scope));
|
||||
e.setControlPoint (0, startResolved + (endResolved - startResolved) * 0.3f, undoManager);
|
||||
e.setControlPoint (1, startResolved + (endResolved - startResolved) * 0.7f, undoManager);
|
||||
e.setControlPoint (2, end, undoManager);
|
||||
|
||||
state = newState;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::Element::convertToPathBreak (UndoManager* undoManager)
|
||||
{
|
||||
const Identifier i (state.getType());
|
||||
|
||||
if (i != startSubPathElement)
|
||||
{
|
||||
ValueTree newState (startSubPathElement);
|
||||
Element e (newState);
|
||||
e.setControlPoint (0, getEndPoint(), undoManager);
|
||||
state = newState;
|
||||
}
|
||||
}
|
||||
|
||||
namespace DrawablePathHelpers
|
||||
{
|
||||
static Point<float> findCubicSubdivisionPoint (float proportion, const Point<float> points[4])
|
||||
{
|
||||
const Point<float> mid1 (points[0] + (points[1] - points[0]) * proportion),
|
||||
mid2 (points[1] + (points[2] - points[1]) * proportion),
|
||||
mid3 (points[2] + (points[3] - points[2]) * proportion);
|
||||
|
||||
const Point<float> newCp1 (mid1 + (mid2 - mid1) * proportion),
|
||||
newCp2 (mid2 + (mid3 - mid2) * proportion);
|
||||
|
||||
return newCp1 + (newCp2 - newCp1) * proportion;
|
||||
}
|
||||
|
||||
static Point<float> findQuadraticSubdivisionPoint (float proportion, const Point<float> points[3])
|
||||
{
|
||||
const Point<float> mid1 (points[0] + (points[1] - points[0]) * proportion),
|
||||
mid2 (points[1] + (points[2] - points[1]) * proportion);
|
||||
|
||||
return mid1 + (mid2 - mid1) * proportion;
|
||||
}
|
||||
}
|
||||
|
||||
float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (Point<float> targetPoint, Expression::Scope* scope) const
|
||||
{
|
||||
using namespace DrawablePathHelpers;
|
||||
const Identifier pointType (state.getType());
|
||||
float bestProp = 0;
|
||||
|
||||
if (pointType == cubicToElement)
|
||||
{
|
||||
RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getControlPoint (1)), rp4 (getEndPoint());
|
||||
|
||||
const Point<float> points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope), rp4.resolve (scope) };
|
||||
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
|
||||
for (int i = 110; --i >= 0;)
|
||||
{
|
||||
float prop = i > 10 ? ((i - 10) / 100.0f) : (bestProp + ((i - 5) / 1000.0f));
|
||||
const Point<float> centre (findCubicSubdivisionPoint (prop, points));
|
||||
const float distance = centre.getDistanceFrom (targetPoint);
|
||||
|
||||
if (distance < bestDistance)
|
||||
{
|
||||
bestProp = prop;
|
||||
bestDistance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pointType == quadraticToElement)
|
||||
{
|
||||
RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint());
|
||||
const Point<float> points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope) };
|
||||
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
|
||||
for (int i = 110; --i >= 0;)
|
||||
{
|
||||
float prop = i > 10 ? ((i - 10) / 100.0f) : (bestProp + ((i - 5) / 1000.0f));
|
||||
const Point<float> centre (findQuadraticSubdivisionPoint ((float) prop, points));
|
||||
const float distance = centre.getDistanceFrom (targetPoint);
|
||||
|
||||
if (distance < bestDistance)
|
||||
{
|
||||
bestProp = prop;
|
||||
bestDistance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pointType == lineToElement)
|
||||
{
|
||||
RelativePoint rp1 (getStartPoint()), rp2 (getEndPoint());
|
||||
const Line<float> line (rp1.resolve (scope), rp2.resolve (scope));
|
||||
bestProp = line.findNearestProportionalPositionTo (targetPoint);
|
||||
}
|
||||
|
||||
return bestProp;
|
||||
}
|
||||
|
||||
ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (Point<float> targetPoint, Expression::Scope* scope, UndoManager* undoManager)
|
||||
{
|
||||
ValueTree newTree;
|
||||
const Identifier pointType (state.getType());
|
||||
|
||||
if (pointType == cubicToElement)
|
||||
{
|
||||
float bestProp = findProportionAlongLine (targetPoint, scope);
|
||||
|
||||
RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getControlPoint (1)), rp4 (getEndPoint());
|
||||
const Point<float> points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope), rp4.resolve (scope) };
|
||||
|
||||
const Point<float> mid1 (points[0] + (points[1] - points[0]) * bestProp),
|
||||
mid2 (points[1] + (points[2] - points[1]) * bestProp),
|
||||
mid3 (points[2] + (points[3] - points[2]) * bestProp);
|
||||
|
||||
const Point<float> newCp1 (mid1 + (mid2 - mid1) * bestProp),
|
||||
newCp2 (mid2 + (mid3 - mid2) * bestProp);
|
||||
|
||||
const Point<float> newCentre (newCp1 + (newCp2 - newCp1) * bestProp);
|
||||
|
||||
setControlPoint (0, mid1, undoManager);
|
||||
setControlPoint (1, newCp1, undoManager);
|
||||
setControlPoint (2, newCentre, undoManager);
|
||||
setModeOfEndPoint (roundedMode, undoManager);
|
||||
|
||||
Element newElement (newTree = ValueTree (cubicToElement));
|
||||
newElement.setControlPoint (0, newCp2, nullptr);
|
||||
newElement.setControlPoint (1, mid3, nullptr);
|
||||
newElement.setControlPoint (2, rp4, nullptr);
|
||||
|
||||
state.getParent().addChild (newTree, state.getParent().indexOf (state) + 1, undoManager);
|
||||
}
|
||||
else if (pointType == quadraticToElement)
|
||||
{
|
||||
float bestProp = findProportionAlongLine (targetPoint, scope);
|
||||
|
||||
RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint());
|
||||
const Point<float> points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope) };
|
||||
|
||||
const Point<float> mid1 (points[0] + (points[1] - points[0]) * bestProp),
|
||||
mid2 (points[1] + (points[2] - points[1]) * bestProp);
|
||||
|
||||
const Point<float> newCentre (mid1 + (mid2 - mid1) * bestProp);
|
||||
|
||||
setControlPoint (0, mid1, undoManager);
|
||||
setControlPoint (1, newCentre, undoManager);
|
||||
setModeOfEndPoint (roundedMode, undoManager);
|
||||
|
||||
Element newElement (newTree = ValueTree (quadraticToElement));
|
||||
newElement.setControlPoint (0, mid2, nullptr);
|
||||
newElement.setControlPoint (1, rp3, nullptr);
|
||||
|
||||
state.getParent().addChild (newTree, state.getParent().indexOf (state) + 1, undoManager);
|
||||
}
|
||||
else if (pointType == lineToElement)
|
||||
{
|
||||
RelativePoint rp1 (getStartPoint()), rp2 (getEndPoint());
|
||||
const Line<float> line (rp1.resolve (scope), rp2.resolve (scope));
|
||||
const Point<float> newPoint (line.findNearestPointTo (targetPoint));
|
||||
|
||||
setControlPoint (0, newPoint, undoManager);
|
||||
|
||||
Element newElement (newTree = ValueTree (lineToElement));
|
||||
newElement.setControlPoint (0, rp2, nullptr);
|
||||
|
||||
state.getParent().addChild (newTree, state.getParent().indexOf (state) + 1, undoManager);
|
||||
}
|
||||
else if (pointType == closeSubPathElement)
|
||||
{
|
||||
}
|
||||
|
||||
return newTree;
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::Element::removePoint (UndoManager* undoManager)
|
||||
{
|
||||
state.getParent().removeChild (state, undoManager);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawablePath::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder)
|
||||
{
|
||||
ValueTreeWrapper v (tree);
|
||||
setComponentID (v.getID());
|
||||
|
||||
refreshFillTypes (v, builder.getImageProvider());
|
||||
setStrokeType (v.getStrokeType());
|
||||
|
||||
RelativePointPath newRelativePath;
|
||||
v.writeTo (newRelativePath);
|
||||
setPath (newRelativePath);
|
||||
}
|
||||
|
||||
ValueTree DrawablePath::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const
|
||||
{
|
||||
ValueTree tree (valueTreeType);
|
||||
ValueTreeWrapper v (tree);
|
||||
|
||||
v.setID (getComponentID());
|
||||
writeTo (v, imageProvider, nullptr);
|
||||
|
||||
if (relativePath != nullptr)
|
||||
v.readFrom (*relativePath, nullptr);
|
||||
else
|
||||
v.readFrom (RelativePointPath (path), nullptr);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_DRAWABLEPATH_H_INCLUDED
|
||||
#define JUCE_DRAWABLEPATH_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A drawable object which renders a filled or outlined shape.
|
||||
|
||||
For details on how to change the fill and stroke, see the DrawableShape class.
|
||||
|
||||
@see Drawable, DrawableShape
|
||||
*/
|
||||
class JUCE_API DrawablePath : public DrawableShape
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a DrawablePath. */
|
||||
DrawablePath();
|
||||
DrawablePath (const DrawablePath&);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawablePath();
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the path that will be drawn.
|
||||
@see setFillColour, setStrokeType
|
||||
*/
|
||||
void setPath (const Path& newPath);
|
||||
|
||||
/** Sets the path using a RelativePointPath.
|
||||
Calling this will set up a Component::Positioner to automatically update the path
|
||||
if any of the points in the source path are dynamic.
|
||||
*/
|
||||
void setPath (const RelativePointPath& newPath);
|
||||
|
||||
/** Returns the current path. */
|
||||
const Path& getPath() const;
|
||||
|
||||
/** Returns the current path for the outline. */
|
||||
const Path& getStrokePath() const;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
Drawable* createCopy() const;
|
||||
/** @internal */
|
||||
void refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder);
|
||||
/** @internal */
|
||||
ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const;
|
||||
/** @internal */
|
||||
static const Identifier valueTreeType;
|
||||
|
||||
//==============================================================================
|
||||
/** Internally-used class for wrapping a DrawablePath's state into a ValueTree. */
|
||||
class ValueTreeWrapper : public DrawableShape::FillAndStrokeState
|
||||
{
|
||||
public:
|
||||
ValueTreeWrapper (const ValueTree& state);
|
||||
|
||||
bool usesNonZeroWinding() const;
|
||||
void setUsesNonZeroWinding (bool b, UndoManager* undoManager);
|
||||
|
||||
class Element
|
||||
{
|
||||
public:
|
||||
explicit Element (const ValueTree& state);
|
||||
~Element();
|
||||
|
||||
const Identifier getType() const noexcept { return state.getType(); }
|
||||
int getNumControlPoints() const noexcept;
|
||||
|
||||
RelativePoint getControlPoint (int index) const;
|
||||
Value getControlPointValue (int index, UndoManager*);
|
||||
RelativePoint getStartPoint() const;
|
||||
RelativePoint getEndPoint() const;
|
||||
void setControlPoint (int index, const RelativePoint& point, UndoManager*);
|
||||
float getLength (Expression::Scope*) const;
|
||||
|
||||
ValueTreeWrapper getParent() const;
|
||||
Element getPreviousElement() const;
|
||||
|
||||
String getModeOfEndPoint() const;
|
||||
void setModeOfEndPoint (const String& newMode, UndoManager*);
|
||||
|
||||
void convertToLine (UndoManager*);
|
||||
void convertToCubic (Expression::Scope*, UndoManager*);
|
||||
void convertToPathBreak (UndoManager* undoManager);
|
||||
ValueTree insertPoint (Point<float> targetPoint, Expression::Scope*, UndoManager*);
|
||||
void removePoint (UndoManager* undoManager);
|
||||
float findProportionAlongLine (Point<float> targetPoint, Expression::Scope*) const;
|
||||
|
||||
static const Identifier mode, startSubPathElement, closeSubPathElement,
|
||||
lineToElement, quadraticToElement, cubicToElement;
|
||||
static const char* cornerMode;
|
||||
static const char* roundedMode;
|
||||
static const char* symmetricMode;
|
||||
|
||||
ValueTree state;
|
||||
};
|
||||
|
||||
ValueTree getPathState();
|
||||
|
||||
void readFrom (const RelativePointPath& path, UndoManager* undoManager);
|
||||
void writeTo (RelativePointPath& path) const;
|
||||
|
||||
static const Identifier nonZeroWinding, point1, point2, point3;
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ScopedPointer<RelativePointPath> relativePath;
|
||||
|
||||
class RelativePositioner;
|
||||
friend class RelativePositioner;
|
||||
void applyRelativePath (const RelativePointPath&, Expression::Scope*);
|
||||
|
||||
DrawablePath& operator= (const DrawablePath&);
|
||||
JUCE_LEAK_DETECTOR (DrawablePath)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_DRAWABLEPATH_H_INCLUDED
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
DrawableRectangle::DrawableRectangle()
|
||||
{
|
||||
}
|
||||
|
||||
DrawableRectangle::DrawableRectangle (const DrawableRectangle& other)
|
||||
: DrawableShape (other),
|
||||
bounds (other.bounds),
|
||||
cornerSize (other.cornerSize)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableRectangle::~DrawableRectangle()
|
||||
{
|
||||
}
|
||||
|
||||
Drawable* DrawableRectangle::createCopy() const
|
||||
{
|
||||
return new DrawableRectangle (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableRectangle::setRectangle (const RelativeParallelogram& newBounds)
|
||||
{
|
||||
if (bounds != newBounds)
|
||||
{
|
||||
bounds = newBounds;
|
||||
rebuildPath();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableRectangle::setCornerSize (const RelativePoint& newSize)
|
||||
{
|
||||
if (cornerSize != newSize)
|
||||
{
|
||||
cornerSize = newSize;
|
||||
rebuildPath();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableRectangle::rebuildPath()
|
||||
{
|
||||
if (bounds.isDynamic() || cornerSize.isDynamic())
|
||||
{
|
||||
Drawable::Positioner<DrawableRectangle>* const p = new Drawable::Positioner<DrawableRectangle> (*this);
|
||||
setPositioner (p);
|
||||
p->apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
setPositioner (nullptr);
|
||||
recalculateCoordinates (nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawableRectangle::registerCoordinates (RelativeCoordinatePositionerBase& pos)
|
||||
{
|
||||
bool ok = pos.addPoint (bounds.topLeft);
|
||||
ok = pos.addPoint (bounds.topRight) && ok;
|
||||
ok = pos.addPoint (bounds.bottomLeft) && ok;
|
||||
return pos.addPoint (cornerSize) && ok;
|
||||
}
|
||||
|
||||
void DrawableRectangle::recalculateCoordinates (Expression::Scope* scope)
|
||||
{
|
||||
Point<float> points[3];
|
||||
bounds.resolveThreePoints (points, scope);
|
||||
|
||||
const float cornerSizeX = (float) cornerSize.x.resolve (scope);
|
||||
const float cornerSizeY = (float) cornerSize.y.resolve (scope);
|
||||
|
||||
const float w = Line<float> (points[0], points[1]).getLength();
|
||||
const float h = Line<float> (points[0], points[2]).getLength();
|
||||
|
||||
Path newPath;
|
||||
|
||||
if (cornerSizeX > 0 && cornerSizeY > 0)
|
||||
newPath.addRoundedRectangle (0, 0, w, h, cornerSizeX, cornerSizeY);
|
||||
else
|
||||
newPath.addRectangle (0, 0, w, h);
|
||||
|
||||
newPath.applyTransform (AffineTransform::fromTargetPoints (0, 0, points[0].x, points[0].y,
|
||||
w, 0, points[1].x, points[1].y,
|
||||
0, h, points[2].x, points[2].y));
|
||||
|
||||
if (path != newPath)
|
||||
{
|
||||
path.swapWithPath (newPath);
|
||||
pathChanged();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Identifier DrawableRectangle::valueTreeType ("Rectangle");
|
||||
const Identifier DrawableRectangle::ValueTreeWrapper::topLeft ("topLeft");
|
||||
const Identifier DrawableRectangle::ValueTreeWrapper::topRight ("topRight");
|
||||
const Identifier DrawableRectangle::ValueTreeWrapper::bottomLeft ("bottomLeft");
|
||||
const Identifier DrawableRectangle::ValueTreeWrapper::cornerSize ("cornerSize");
|
||||
|
||||
//==============================================================================
|
||||
DrawableRectangle::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
|
||||
: FillAndStrokeState (state_)
|
||||
{
|
||||
jassert (state.hasType (valueTreeType));
|
||||
}
|
||||
|
||||
RelativeParallelogram DrawableRectangle::ValueTreeWrapper::getRectangle() const
|
||||
{
|
||||
return RelativeParallelogram (state.getProperty (topLeft, "0, 0"),
|
||||
state.getProperty (topRight, "100, 0"),
|
||||
state.getProperty (bottomLeft, "0, 100"));
|
||||
}
|
||||
|
||||
void DrawableRectangle::ValueTreeWrapper::setRectangle (const RelativeParallelogram& newBounds, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager);
|
||||
state.setProperty (topRight, newBounds.topRight.toString(), undoManager);
|
||||
state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager);
|
||||
}
|
||||
|
||||
void DrawableRectangle::ValueTreeWrapper::setCornerSize (const RelativePoint& newSize, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (cornerSize, newSize.toString(), undoManager);
|
||||
}
|
||||
|
||||
RelativePoint DrawableRectangle::ValueTreeWrapper::getCornerSize() const
|
||||
{
|
||||
return RelativePoint (state [cornerSize]);
|
||||
}
|
||||
|
||||
Value DrawableRectangle::ValueTreeWrapper::getCornerSizeValue (UndoManager* undoManager)
|
||||
{
|
||||
return state.getPropertyAsValue (cornerSize, undoManager);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableRectangle::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder)
|
||||
{
|
||||
ValueTreeWrapper v (tree);
|
||||
setComponentID (v.getID());
|
||||
|
||||
refreshFillTypes (v, builder.getImageProvider());
|
||||
setStrokeType (v.getStrokeType());
|
||||
setRectangle (v.getRectangle());
|
||||
setCornerSize (v.getCornerSize());
|
||||
}
|
||||
|
||||
ValueTree DrawableRectangle::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const
|
||||
{
|
||||
ValueTree tree (valueTreeType);
|
||||
ValueTreeWrapper v (tree);
|
||||
|
||||
v.setID (getComponentID());
|
||||
writeTo (v, imageProvider, nullptr);
|
||||
v.setRectangle (bounds, nullptr);
|
||||
v.setCornerSize (cornerSize, nullptr);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_DRAWABLERECTANGLE_H_INCLUDED
|
||||
#define JUCE_DRAWABLERECTANGLE_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A Drawable object which draws a rectangle.
|
||||
|
||||
For details on how to change the fill and stroke, see the DrawableShape class.
|
||||
|
||||
@see Drawable, DrawableShape
|
||||
*/
|
||||
class JUCE_API DrawableRectangle : public DrawableShape
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
DrawableRectangle();
|
||||
DrawableRectangle (const DrawableRectangle&);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawableRectangle();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the rectangle's bounds. */
|
||||
void setRectangle (const RelativeParallelogram& newBounds);
|
||||
|
||||
/** Returns the rectangle's bounds. */
|
||||
const RelativeParallelogram& getRectangle() const noexcept { return bounds; }
|
||||
|
||||
/** Returns the corner size to be used. */
|
||||
const RelativePoint& getCornerSize() const noexcept { return cornerSize; }
|
||||
|
||||
/** Sets a new corner size for the rectangle */
|
||||
void setCornerSize (const RelativePoint& newSize);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
Drawable* createCopy() const;
|
||||
/** @internal */
|
||||
void refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder);
|
||||
/** @internal */
|
||||
ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const;
|
||||
/** @internal */
|
||||
static const Identifier valueTreeType;
|
||||
|
||||
//==============================================================================
|
||||
/** Internally-used class for wrapping a DrawableRectangle's state into a ValueTree. */
|
||||
class ValueTreeWrapper : public DrawableShape::FillAndStrokeState
|
||||
{
|
||||
public:
|
||||
ValueTreeWrapper (const ValueTree& state);
|
||||
|
||||
RelativeParallelogram getRectangle() const;
|
||||
void setRectangle (const RelativeParallelogram& newBounds, UndoManager*);
|
||||
|
||||
void setCornerSize (const RelativePoint& cornerSize, UndoManager*);
|
||||
RelativePoint getCornerSize() const;
|
||||
Value getCornerSizeValue (UndoManager*);
|
||||
|
||||
static const Identifier topLeft, topRight, bottomLeft, cornerSize;
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
friend class Drawable::Positioner<DrawableRectangle>;
|
||||
|
||||
RelativeParallelogram bounds;
|
||||
RelativePoint cornerSize;
|
||||
|
||||
void rebuildPath();
|
||||
bool registerCoordinates (RelativeCoordinatePositionerBase&);
|
||||
void recalculateCoordinates (Expression::Scope*);
|
||||
|
||||
DrawableRectangle& operator= (const DrawableRectangle&);
|
||||
JUCE_LEAK_DETECTOR (DrawableRectangle)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_DRAWABLERECTANGLE_H_INCLUDED
|
||||
|
|
@ -0,0 +1,472 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
DrawableShape::DrawableShape()
|
||||
: strokeType (0.0f),
|
||||
mainFill (Colours::black),
|
||||
strokeFill (Colours::black)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableShape::DrawableShape (const DrawableShape& other)
|
||||
: Drawable (other),
|
||||
strokeType (other.strokeType),
|
||||
mainFill (other.mainFill),
|
||||
strokeFill (other.strokeFill)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableShape::~DrawableShape()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class DrawableShape::RelativePositioner : public RelativeCoordinatePositionerBase
|
||||
{
|
||||
public:
|
||||
RelativePositioner (DrawableShape& comp, const DrawableShape::RelativeFillType& f, bool isMain)
|
||||
: RelativeCoordinatePositionerBase (comp),
|
||||
owner (comp),
|
||||
fill (f),
|
||||
isMainFill (isMain)
|
||||
{
|
||||
}
|
||||
|
||||
bool registerCoordinates()
|
||||
{
|
||||
bool ok = addPoint (fill.gradientPoint1);
|
||||
ok = addPoint (fill.gradientPoint2) && ok;
|
||||
return addPoint (fill.gradientPoint3) && ok;
|
||||
}
|
||||
|
||||
void applyToComponentBounds()
|
||||
{
|
||||
ComponentScope scope (owner);
|
||||
if (isMainFill ? owner.mainFill.recalculateCoords (&scope)
|
||||
: owner.strokeFill.recalculateCoords (&scope))
|
||||
owner.repaint();
|
||||
}
|
||||
|
||||
void applyNewBounds (const Rectangle<int>&)
|
||||
{
|
||||
jassertfalse; // drawables can't be resized directly!
|
||||
}
|
||||
|
||||
private:
|
||||
DrawableShape& owner;
|
||||
const DrawableShape::RelativeFillType fill;
|
||||
const bool isMainFill;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativePositioner)
|
||||
};
|
||||
|
||||
void DrawableShape::setFill (const FillType& newFill)
|
||||
{
|
||||
setFill (RelativeFillType (newFill));
|
||||
}
|
||||
|
||||
void DrawableShape::setStrokeFill (const FillType& newFill)
|
||||
{
|
||||
setStrokeFill (RelativeFillType (newFill));
|
||||
}
|
||||
|
||||
void DrawableShape::setFillInternal (RelativeFillType& fill, const RelativeFillType& newFill,
|
||||
ScopedPointer<RelativeCoordinatePositionerBase>& pos)
|
||||
{
|
||||
if (fill != newFill)
|
||||
{
|
||||
fill = newFill;
|
||||
pos = nullptr;
|
||||
|
||||
if (fill.isDynamic())
|
||||
{
|
||||
pos = new RelativePositioner (*this, fill, true);
|
||||
pos->apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
fill.recalculateCoords (nullptr);
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableShape::setFill (const RelativeFillType& newFill)
|
||||
{
|
||||
setFillInternal (mainFill, newFill, mainFillPositioner);
|
||||
}
|
||||
|
||||
void DrawableShape::setStrokeFill (const RelativeFillType& newFill)
|
||||
{
|
||||
setFillInternal (strokeFill, newFill, strokeFillPositioner);
|
||||
}
|
||||
|
||||
void DrawableShape::setStrokeType (const PathStrokeType& newStrokeType)
|
||||
{
|
||||
if (strokeType != newStrokeType)
|
||||
{
|
||||
strokeType = newStrokeType;
|
||||
strokeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableShape::setStrokeThickness (const float newThickness)
|
||||
{
|
||||
setStrokeType (PathStrokeType (newThickness, strokeType.getJointStyle(), strokeType.getEndStyle()));
|
||||
}
|
||||
|
||||
bool DrawableShape::isStrokeVisible() const noexcept
|
||||
{
|
||||
return strokeType.getStrokeThickness() > 0.0f && ! strokeFill.fill.isInvisible();
|
||||
}
|
||||
|
||||
void DrawableShape::refreshFillTypes (const FillAndStrokeState& newState, ComponentBuilder::ImageProvider* imageProvider)
|
||||
{
|
||||
setFill (newState.getFill (FillAndStrokeState::fill, imageProvider));
|
||||
setStrokeFill (newState.getFill (FillAndStrokeState::stroke, imageProvider));
|
||||
}
|
||||
|
||||
void DrawableShape::writeTo (FillAndStrokeState& state, ComponentBuilder::ImageProvider* imageProvider, UndoManager* undoManager) const
|
||||
{
|
||||
state.setFill (FillAndStrokeState::fill, mainFill, imageProvider, undoManager);
|
||||
state.setFill (FillAndStrokeState::stroke, strokeFill, imageProvider, undoManager);
|
||||
state.setStrokeType (strokeType, undoManager);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableShape::paint (Graphics& g)
|
||||
{
|
||||
transformContextToCorrectOrigin (g);
|
||||
|
||||
g.setFillType (mainFill.fill);
|
||||
g.fillPath (path);
|
||||
|
||||
if (isStrokeVisible())
|
||||
{
|
||||
g.setFillType (strokeFill.fill);
|
||||
g.fillPath (strokePath);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableShape::pathChanged()
|
||||
{
|
||||
strokeChanged();
|
||||
}
|
||||
|
||||
void DrawableShape::strokeChanged()
|
||||
{
|
||||
strokePath.clear();
|
||||
strokeType.createStrokedPath (strokePath, path, AffineTransform::identity, 4.0f);
|
||||
|
||||
setBoundsToEnclose (getDrawableBounds());
|
||||
repaint();
|
||||
}
|
||||
|
||||
Rectangle<float> DrawableShape::getDrawableBounds() const
|
||||
{
|
||||
if (isStrokeVisible())
|
||||
return strokePath.getBounds();
|
||||
|
||||
return path.getBounds();
|
||||
}
|
||||
|
||||
bool DrawableShape::hitTest (int x, int y)
|
||||
{
|
||||
bool allowsClicksOnThisComponent, allowsClicksOnChildComponents;
|
||||
getInterceptsMouseClicks (allowsClicksOnThisComponent, allowsClicksOnChildComponents);
|
||||
|
||||
if (! allowsClicksOnThisComponent)
|
||||
return false;
|
||||
|
||||
const float globalX = (float) (x - originRelativeToComponent.x);
|
||||
const float globalY = (float) (y - originRelativeToComponent.y);
|
||||
|
||||
return path.contains (globalX, globalY)
|
||||
|| (isStrokeVisible() && strokePath.contains (globalX, globalY));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
DrawableShape::RelativeFillType::RelativeFillType()
|
||||
{
|
||||
}
|
||||
|
||||
DrawableShape::RelativeFillType::RelativeFillType (const FillType& fill_)
|
||||
: fill (fill_)
|
||||
{
|
||||
if (fill.isGradient())
|
||||
{
|
||||
const ColourGradient& g = *fill.gradient;
|
||||
|
||||
gradientPoint1 = g.point1.transformedBy (fill.transform);
|
||||
gradientPoint2 = g.point2.transformedBy (fill.transform);
|
||||
gradientPoint3 = Point<float> (g.point1.x + g.point2.y - g.point1.y,
|
||||
g.point1.y + g.point1.x - g.point2.x)
|
||||
.transformedBy (fill.transform);
|
||||
fill.transform = AffineTransform::identity;
|
||||
}
|
||||
}
|
||||
|
||||
DrawableShape::RelativeFillType::RelativeFillType (const RelativeFillType& other)
|
||||
: fill (other.fill),
|
||||
gradientPoint1 (other.gradientPoint1),
|
||||
gradientPoint2 (other.gradientPoint2),
|
||||
gradientPoint3 (other.gradientPoint3)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableShape::RelativeFillType& DrawableShape::RelativeFillType::operator= (const RelativeFillType& other)
|
||||
{
|
||||
fill = other.fill;
|
||||
gradientPoint1 = other.gradientPoint1;
|
||||
gradientPoint2 = other.gradientPoint2;
|
||||
gradientPoint3 = other.gradientPoint3;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DrawableShape::RelativeFillType::operator== (const RelativeFillType& other) const
|
||||
{
|
||||
return fill == other.fill
|
||||
&& ((! fill.isGradient())
|
||||
|| (gradientPoint1 == other.gradientPoint1
|
||||
&& gradientPoint2 == other.gradientPoint2
|
||||
&& gradientPoint3 == other.gradientPoint3));
|
||||
}
|
||||
|
||||
bool DrawableShape::RelativeFillType::operator!= (const RelativeFillType& other) const
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
bool DrawableShape::RelativeFillType::recalculateCoords (Expression::Scope* scope)
|
||||
{
|
||||
if (fill.isGradient())
|
||||
{
|
||||
const Point<float> g1 (gradientPoint1.resolve (scope));
|
||||
const Point<float> g2 (gradientPoint2.resolve (scope));
|
||||
AffineTransform t;
|
||||
|
||||
ColourGradient& g = *fill.gradient;
|
||||
|
||||
if (g.isRadial)
|
||||
{
|
||||
const Point<float> g3 (gradientPoint3.resolve (scope));
|
||||
const Point<float> g3Source (g1.x + g2.y - g1.y,
|
||||
g1.y + g1.x - g2.x);
|
||||
|
||||
t = AffineTransform::fromTargetPoints (g1.x, g1.y, g1.x, g1.y,
|
||||
g2.x, g2.y, g2.x, g2.y,
|
||||
g3Source.x, g3Source.y, g3.x, g3.y);
|
||||
}
|
||||
|
||||
if (g.point1 != g1 || g.point2 != g2 || fill.transform != t)
|
||||
{
|
||||
g.point1 = g1;
|
||||
g.point2 = g2;
|
||||
fill.transform = t;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DrawableShape::RelativeFillType::isDynamic() const
|
||||
{
|
||||
return gradientPoint1.isDynamic() || gradientPoint2.isDynamic() || gradientPoint3.isDynamic();
|
||||
}
|
||||
|
||||
void DrawableShape::RelativeFillType::writeTo (ValueTree& v, ComponentBuilder::ImageProvider* imageProvider, UndoManager* undoManager) const
|
||||
{
|
||||
if (fill.isColour())
|
||||
{
|
||||
v.setProperty (FillAndStrokeState::type, "solid", undoManager);
|
||||
v.setProperty (FillAndStrokeState::colour, String::toHexString ((int) fill.colour.getARGB()), undoManager);
|
||||
}
|
||||
else if (fill.isGradient())
|
||||
{
|
||||
v.setProperty (FillAndStrokeState::type, "gradient", undoManager);
|
||||
v.setProperty (FillAndStrokeState::gradientPoint1, gradientPoint1.toString(), undoManager);
|
||||
v.setProperty (FillAndStrokeState::gradientPoint2, gradientPoint2.toString(), undoManager);
|
||||
v.setProperty (FillAndStrokeState::gradientPoint3, gradientPoint3.toString(), undoManager);
|
||||
|
||||
const ColourGradient& cg = *fill.gradient;
|
||||
v.setProperty (FillAndStrokeState::radial, cg.isRadial, undoManager);
|
||||
|
||||
String s;
|
||||
for (int i = 0; i < cg.getNumColours(); ++i)
|
||||
s << ' ' << cg.getColourPosition (i)
|
||||
<< ' ' << String::toHexString ((int) cg.getColour(i).getARGB());
|
||||
|
||||
v.setProperty (FillAndStrokeState::colours, s.trimStart(), undoManager);
|
||||
}
|
||||
else if (fill.isTiledImage())
|
||||
{
|
||||
v.setProperty (FillAndStrokeState::type, "image", undoManager);
|
||||
|
||||
if (imageProvider != nullptr)
|
||||
v.setProperty (FillAndStrokeState::imageId, imageProvider->getIdentifierForImage (fill.image), undoManager);
|
||||
|
||||
if (fill.getOpacity() < 1.0f)
|
||||
v.setProperty (FillAndStrokeState::imageOpacity, fill.getOpacity(), undoManager);
|
||||
else
|
||||
v.removeProperty (FillAndStrokeState::imageOpacity, undoManager);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawableShape::RelativeFillType::readFrom (const ValueTree& v, ComponentBuilder::ImageProvider* imageProvider)
|
||||
{
|
||||
const String newType (v [FillAndStrokeState::type].toString());
|
||||
|
||||
if (newType == "solid")
|
||||
{
|
||||
const String colourString (v [FillAndStrokeState::colour].toString());
|
||||
fill.setColour (colourString.isEmpty() ? Colours::black
|
||||
: Colour::fromString (colourString));
|
||||
return true;
|
||||
}
|
||||
else if (newType == "gradient")
|
||||
{
|
||||
ColourGradient g;
|
||||
g.isRadial = v [FillAndStrokeState::radial];
|
||||
|
||||
StringArray colourSteps;
|
||||
colourSteps.addTokens (v [FillAndStrokeState::colours].toString(), false);
|
||||
|
||||
for (int i = 0; i < colourSteps.size() / 2; ++i)
|
||||
g.addColour (colourSteps[i * 2].getDoubleValue(),
|
||||
Colour::fromString (colourSteps[i * 2 + 1]));
|
||||
|
||||
fill.setGradient (g);
|
||||
|
||||
gradientPoint1 = RelativePoint (v [FillAndStrokeState::gradientPoint1]);
|
||||
gradientPoint2 = RelativePoint (v [FillAndStrokeState::gradientPoint2]);
|
||||
gradientPoint3 = RelativePoint (v [FillAndStrokeState::gradientPoint3]);
|
||||
return true;
|
||||
}
|
||||
else if (newType == "image")
|
||||
{
|
||||
Image im;
|
||||
if (imageProvider != nullptr)
|
||||
im = imageProvider->getImageForIdentifier (v [FillAndStrokeState::imageId]);
|
||||
|
||||
fill.setTiledImage (im, AffineTransform::identity);
|
||||
fill.setOpacity ((float) v.getProperty (FillAndStrokeState::imageOpacity, 1.0f));
|
||||
return true;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Identifier DrawableShape::FillAndStrokeState::type ("type");
|
||||
const Identifier DrawableShape::FillAndStrokeState::colour ("colour");
|
||||
const Identifier DrawableShape::FillAndStrokeState::colours ("colours");
|
||||
const Identifier DrawableShape::FillAndStrokeState::fill ("Fill");
|
||||
const Identifier DrawableShape::FillAndStrokeState::stroke ("Stroke");
|
||||
const Identifier DrawableShape::FillAndStrokeState::path ("Path");
|
||||
const Identifier DrawableShape::FillAndStrokeState::jointStyle ("jointStyle");
|
||||
const Identifier DrawableShape::FillAndStrokeState::capStyle ("capStyle");
|
||||
const Identifier DrawableShape::FillAndStrokeState::strokeWidth ("strokeWidth");
|
||||
const Identifier DrawableShape::FillAndStrokeState::gradientPoint1 ("point1");
|
||||
const Identifier DrawableShape::FillAndStrokeState::gradientPoint2 ("point2");
|
||||
const Identifier DrawableShape::FillAndStrokeState::gradientPoint3 ("point3");
|
||||
const Identifier DrawableShape::FillAndStrokeState::radial ("radial");
|
||||
const Identifier DrawableShape::FillAndStrokeState::imageId ("imageId");
|
||||
const Identifier DrawableShape::FillAndStrokeState::imageOpacity ("imageOpacity");
|
||||
|
||||
DrawableShape::FillAndStrokeState::FillAndStrokeState (const ValueTree& state_)
|
||||
: Drawable::ValueTreeWrapperBase (state_)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableShape::RelativeFillType DrawableShape::FillAndStrokeState::getFill (const Identifier& fillOrStrokeType, ComponentBuilder::ImageProvider* imageProvider) const
|
||||
{
|
||||
DrawableShape::RelativeFillType f;
|
||||
f.readFrom (state.getChildWithName (fillOrStrokeType), imageProvider);
|
||||
return f;
|
||||
}
|
||||
|
||||
ValueTree DrawableShape::FillAndStrokeState::getFillState (const Identifier& fillOrStrokeType)
|
||||
{
|
||||
ValueTree v (state.getChildWithName (fillOrStrokeType));
|
||||
if (v.isValid())
|
||||
return v;
|
||||
|
||||
setFill (fillOrStrokeType, FillType (Colours::black), nullptr, nullptr);
|
||||
return getFillState (fillOrStrokeType);
|
||||
}
|
||||
|
||||
void DrawableShape::FillAndStrokeState::setFill (const Identifier& fillOrStrokeType, const RelativeFillType& newFill,
|
||||
ComponentBuilder::ImageProvider* imageProvider, UndoManager* undoManager)
|
||||
{
|
||||
ValueTree v (state.getOrCreateChildWithName (fillOrStrokeType, undoManager));
|
||||
newFill.writeTo (v, imageProvider, undoManager);
|
||||
}
|
||||
|
||||
PathStrokeType DrawableShape::FillAndStrokeState::getStrokeType() const
|
||||
{
|
||||
const String jointStyleString (state [jointStyle].toString());
|
||||
const String capStyleString (state [capStyle].toString());
|
||||
|
||||
return PathStrokeType (state [strokeWidth],
|
||||
jointStyleString == "curved" ? PathStrokeType::curved
|
||||
: (jointStyleString == "bevel" ? PathStrokeType::beveled
|
||||
: PathStrokeType::mitered),
|
||||
capStyleString == "square" ? PathStrokeType::square
|
||||
: (capStyleString == "round" ? PathStrokeType::rounded
|
||||
: PathStrokeType::butt));
|
||||
}
|
||||
|
||||
void DrawableShape::FillAndStrokeState::setStrokeType (const PathStrokeType& newStrokeType, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (strokeWidth, (double) newStrokeType.getStrokeThickness(), undoManager);
|
||||
state.setProperty (jointStyle, newStrokeType.getJointStyle() == PathStrokeType::mitered
|
||||
? "miter" : (newStrokeType.getJointStyle() == PathStrokeType::curved ? "curved" : "bevel"), undoManager);
|
||||
state.setProperty (capStyle, newStrokeType.getEndStyle() == PathStrokeType::butt
|
||||
? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager);
|
||||
}
|
||||
|
||||
static bool replaceColourInFill (DrawableShape::RelativeFillType& fill, Colour original, Colour replacement)
|
||||
{
|
||||
if (fill.fill.colour == original && fill.fill.isColour())
|
||||
{
|
||||
fill = FillType (replacement);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DrawableShape::replaceColour (Colour original, Colour replacement)
|
||||
{
|
||||
bool changed1 = replaceColourInFill (mainFill, original, replacement);
|
||||
bool changed2 = replaceColourInFill (strokeFill, original, replacement);
|
||||
return changed1 || changed2;
|
||||
}
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_DRAWABLESHAPE_H_INCLUDED
|
||||
#define JUCE_DRAWABLESHAPE_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class implementing common functionality for Drawable classes which
|
||||
consist of some kind of filled and stroked outline.
|
||||
|
||||
@see DrawablePath, DrawableRectangle
|
||||
*/
|
||||
class JUCE_API DrawableShape : public Drawable
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
DrawableShape();
|
||||
DrawableShape (const DrawableShape&);
|
||||
|
||||
public:
|
||||
/** Destructor. */
|
||||
~DrawableShape();
|
||||
|
||||
//==============================================================================
|
||||
/** A FillType wrapper that allows the gradient coordinates to be implemented using RelativePoint.
|
||||
*/
|
||||
class RelativeFillType
|
||||
{
|
||||
public:
|
||||
RelativeFillType();
|
||||
RelativeFillType (const FillType& fill);
|
||||
RelativeFillType (const RelativeFillType&);
|
||||
RelativeFillType& operator= (const RelativeFillType&);
|
||||
|
||||
bool operator== (const RelativeFillType&) const;
|
||||
bool operator!= (const RelativeFillType&) const;
|
||||
|
||||
bool isDynamic() const;
|
||||
bool recalculateCoords (Expression::Scope* scope);
|
||||
|
||||
void writeTo (ValueTree& v, ComponentBuilder::ImageProvider*, UndoManager*) const;
|
||||
bool readFrom (const ValueTree& v, ComponentBuilder::ImageProvider*);
|
||||
|
||||
//==============================================================================
|
||||
FillType fill;
|
||||
RelativePoint gradientPoint1, gradientPoint2, gradientPoint3;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a fill type for the path.
|
||||
This colour is used to fill the path - if you don't want the path to be
|
||||
filled (e.g. if you're just drawing an outline), set this to a transparent
|
||||
colour.
|
||||
|
||||
@see setPath, setStrokeFill
|
||||
*/
|
||||
void setFill (const FillType& newFill);
|
||||
|
||||
/** Sets a fill type for the path.
|
||||
This colour is used to fill the path - if you don't want the path to be
|
||||
filled (e.g. if you're just drawing an outline), set this to a transparent
|
||||
colour.
|
||||
|
||||
@see setPath, setStrokeFill
|
||||
*/
|
||||
void setFill (const RelativeFillType& newFill);
|
||||
|
||||
/** Returns the current fill type.
|
||||
@see setFill
|
||||
*/
|
||||
const RelativeFillType& getFill() const noexcept { return mainFill; }
|
||||
|
||||
/** Sets the fill type with which the outline will be drawn.
|
||||
@see setFill
|
||||
*/
|
||||
void setStrokeFill (const FillType& newStrokeFill);
|
||||
|
||||
/** Sets the fill type with which the outline will be drawn.
|
||||
@see setFill
|
||||
*/
|
||||
void setStrokeFill (const RelativeFillType& newStrokeFill);
|
||||
|
||||
/** Returns the current stroke fill.
|
||||
@see setStrokeFill
|
||||
*/
|
||||
const RelativeFillType& getStrokeFill() const noexcept { return strokeFill; }
|
||||
|
||||
/** Changes the properties of the outline that will be drawn around the path.
|
||||
If the stroke has 0 thickness, no stroke will be drawn.
|
||||
@see setStrokeThickness, setStrokeColour
|
||||
*/
|
||||
void setStrokeType (const PathStrokeType& newStrokeType);
|
||||
|
||||
/** Changes the stroke thickness.
|
||||
This is a shortcut for calling setStrokeType.
|
||||
*/
|
||||
void setStrokeThickness (float newThickness);
|
||||
|
||||
/** Returns the current outline style. */
|
||||
const PathStrokeType& getStrokeType() const noexcept { return strokeType; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
class FillAndStrokeState : public Drawable::ValueTreeWrapperBase
|
||||
{
|
||||
public:
|
||||
FillAndStrokeState (const ValueTree& state);
|
||||
|
||||
ValueTree getFillState (const Identifier& fillOrStrokeType);
|
||||
RelativeFillType getFill (const Identifier& fillOrStrokeType, ComponentBuilder::ImageProvider*) const;
|
||||
void setFill (const Identifier& fillOrStrokeType, const RelativeFillType& newFill,
|
||||
ComponentBuilder::ImageProvider*, UndoManager*);
|
||||
|
||||
PathStrokeType getStrokeType() const;
|
||||
void setStrokeType (const PathStrokeType& newStrokeType, UndoManager*);
|
||||
|
||||
static const Identifier type, colour, colours, fill, stroke, path, jointStyle, capStyle, strokeWidth,
|
||||
gradientPoint1, gradientPoint2, gradientPoint3, radial, imageId, imageOpacity;
|
||||
};
|
||||
|
||||
/** @internal */
|
||||
Rectangle<float> getDrawableBounds() const override;
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
bool hitTest (int x, int y) override;
|
||||
/** @internal */
|
||||
bool replaceColour (Colour originalColour, Colour replacementColour) override;
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Called when the cached path should be updated. */
|
||||
void pathChanged();
|
||||
/** Called when the cached stroke should be updated. */
|
||||
void strokeChanged();
|
||||
/** True if there's a stroke with a non-zero thickness and non-transparent colour. */
|
||||
bool isStrokeVisible() const noexcept;
|
||||
/** Updates the details from a FillAndStrokeState object, returning true if something changed. */
|
||||
void refreshFillTypes (const FillAndStrokeState& newState, ComponentBuilder::ImageProvider*);
|
||||
/** Writes the stroke and fill details to a FillAndStrokeState object. */
|
||||
void writeTo (FillAndStrokeState& state, ComponentBuilder::ImageProvider*, UndoManager*) const;
|
||||
|
||||
//==============================================================================
|
||||
PathStrokeType strokeType;
|
||||
Path path, strokePath;
|
||||
|
||||
private:
|
||||
class RelativePositioner;
|
||||
RelativeFillType mainFill, strokeFill;
|
||||
ScopedPointer<RelativeCoordinatePositionerBase> mainFillPositioner, strokeFillPositioner;
|
||||
|
||||
void setFillInternal (RelativeFillType& fill, const RelativeFillType& newFill,
|
||||
ScopedPointer<RelativeCoordinatePositionerBase>& positioner);
|
||||
|
||||
DrawableShape& operator= (const DrawableShape&);
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_DRAWABLESHAPE_H_INCLUDED
|
||||
|
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
DrawableText::DrawableText()
|
||||
: colour (Colours::black),
|
||||
justification (Justification::centredLeft)
|
||||
{
|
||||
setBoundingBox (RelativeParallelogram (RelativePoint (0.0f, 0.0f),
|
||||
RelativePoint (50.0f, 0.0f),
|
||||
RelativePoint (0.0f, 20.0f)));
|
||||
setFont (Font (15.0f), true);
|
||||
}
|
||||
|
||||
DrawableText::DrawableText (const DrawableText& other)
|
||||
: Drawable (other),
|
||||
bounds (other.bounds),
|
||||
fontHeight (other.fontHeight),
|
||||
fontHScale (other.fontHScale),
|
||||
font (other.font),
|
||||
text (other.text),
|
||||
colour (other.colour),
|
||||
justification (other.justification)
|
||||
{
|
||||
refreshBounds();
|
||||
}
|
||||
|
||||
DrawableText::~DrawableText()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableText::setText (const String& newText)
|
||||
{
|
||||
if (text != newText)
|
||||
{
|
||||
text = newText;
|
||||
refreshBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::setColour (Colour newColour)
|
||||
{
|
||||
if (colour != newColour)
|
||||
{
|
||||
colour = newColour;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::setFont (const Font& newFont, bool applySizeAndScale)
|
||||
{
|
||||
if (font != newFont)
|
||||
{
|
||||
font = newFont;
|
||||
|
||||
if (applySizeAndScale)
|
||||
{
|
||||
fontHeight = font.getHeight();
|
||||
fontHScale = font.getHorizontalScale();
|
||||
}
|
||||
|
||||
refreshBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::setJustification (Justification newJustification)
|
||||
{
|
||||
justification = newJustification;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void DrawableText::setBoundingBox (const RelativeParallelogram& newBounds)
|
||||
{
|
||||
if (bounds != newBounds)
|
||||
{
|
||||
bounds = newBounds;
|
||||
refreshBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::setFontHeight (const RelativeCoordinate& newHeight)
|
||||
{
|
||||
if (fontHeight != newHeight)
|
||||
{
|
||||
fontHeight = newHeight;
|
||||
refreshBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::setFontHorizontalScale (const RelativeCoordinate& newScale)
|
||||
{
|
||||
if (fontHScale != newScale)
|
||||
{
|
||||
fontHScale = newScale;
|
||||
refreshBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::refreshBounds()
|
||||
{
|
||||
if (bounds.isDynamic() || fontHeight.isDynamic() || fontHScale.isDynamic())
|
||||
{
|
||||
Drawable::Positioner<DrawableText>* const p = new Drawable::Positioner<DrawableText> (*this);
|
||||
setPositioner (p);
|
||||
p->apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
setPositioner (0);
|
||||
recalculateCoordinates (0);
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawableText::registerCoordinates (RelativeCoordinatePositionerBase& pos)
|
||||
{
|
||||
bool ok = pos.addPoint (bounds.topLeft);
|
||||
ok = pos.addPoint (bounds.topRight) && ok;
|
||||
ok = pos.addPoint (bounds.bottomLeft) && ok;
|
||||
ok = pos.addCoordinate (fontHeight) && ok;
|
||||
return pos.addCoordinate (fontHScale) && ok;
|
||||
}
|
||||
|
||||
void DrawableText::recalculateCoordinates (Expression::Scope* scope)
|
||||
{
|
||||
bounds.resolveThreePoints (resolvedPoints, scope);
|
||||
|
||||
const float w = Line<float> (resolvedPoints[0], resolvedPoints[1]).getLength();
|
||||
const float h = Line<float> (resolvedPoints[0], resolvedPoints[2]).getLength();
|
||||
|
||||
const float height = jlimit (0.01f, jmax (0.01f, h), (float) fontHeight.resolve (scope));
|
||||
const float hscale = jlimit (0.01f, jmax (0.01f, w), (float) fontHScale.resolve (scope));
|
||||
|
||||
scaledFont = font;
|
||||
scaledFont.setHeight (height);
|
||||
scaledFont.setHorizontalScale (hscale);
|
||||
|
||||
setBoundsToEnclose (getDrawableBounds());
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableText::paint (Graphics& g)
|
||||
{
|
||||
transformContextToCorrectOrigin (g);
|
||||
|
||||
const float w = Line<float> (resolvedPoints[0], resolvedPoints[1]).getLength();
|
||||
const float h = Line<float> (resolvedPoints[0], resolvedPoints[2]).getLength();
|
||||
|
||||
g.addTransform (AffineTransform::fromTargetPoints (0, 0, resolvedPoints[0].x, resolvedPoints[0].y,
|
||||
w, 0, resolvedPoints[1].x, resolvedPoints[1].y,
|
||||
0, h, resolvedPoints[2].x, resolvedPoints[2].y));
|
||||
g.setFont (scaledFont);
|
||||
g.setColour (colour);
|
||||
|
||||
g.drawFittedText (text, Rectangle<float> (w, h).getSmallestIntegerContainer(), justification, 0x100000);
|
||||
}
|
||||
|
||||
Rectangle<float> DrawableText::getDrawableBounds() const
|
||||
{
|
||||
return RelativeParallelogram::getBoundingBox (resolvedPoints);
|
||||
}
|
||||
|
||||
Drawable* DrawableText::createCopy() const
|
||||
{
|
||||
return new DrawableText (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Identifier DrawableText::valueTreeType ("Text");
|
||||
|
||||
const Identifier DrawableText::ValueTreeWrapper::text ("text");
|
||||
const Identifier DrawableText::ValueTreeWrapper::colour ("colour");
|
||||
const Identifier DrawableText::ValueTreeWrapper::font ("font");
|
||||
const Identifier DrawableText::ValueTreeWrapper::justification ("justification");
|
||||
const Identifier DrawableText::ValueTreeWrapper::topLeft ("topLeft");
|
||||
const Identifier DrawableText::ValueTreeWrapper::topRight ("topRight");
|
||||
const Identifier DrawableText::ValueTreeWrapper::bottomLeft ("bottomLeft");
|
||||
const Identifier DrawableText::ValueTreeWrapper::fontHeight ("fontHeight");
|
||||
const Identifier DrawableText::ValueTreeWrapper::fontHScale ("fontHScale");
|
||||
|
||||
//==============================================================================
|
||||
DrawableText::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
|
||||
: ValueTreeWrapperBase (state_)
|
||||
{
|
||||
jassert (state.hasType (valueTreeType));
|
||||
}
|
||||
|
||||
String DrawableText::ValueTreeWrapper::getText() const
|
||||
{
|
||||
return state [text].toString();
|
||||
}
|
||||
|
||||
void DrawableText::ValueTreeWrapper::setText (const String& newText, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (text, newText, undoManager);
|
||||
}
|
||||
|
||||
Value DrawableText::ValueTreeWrapper::getTextValue (UndoManager* undoManager)
|
||||
{
|
||||
return state.getPropertyAsValue (text, undoManager);
|
||||
}
|
||||
|
||||
Colour DrawableText::ValueTreeWrapper::getColour() const
|
||||
{
|
||||
return Colour::fromString (state [colour].toString());
|
||||
}
|
||||
|
||||
void DrawableText::ValueTreeWrapper::setColour (Colour newColour, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (colour, newColour.toString(), undoManager);
|
||||
}
|
||||
|
||||
Justification DrawableText::ValueTreeWrapper::getJustification() const
|
||||
{
|
||||
return Justification ((int) state [justification]);
|
||||
}
|
||||
|
||||
void DrawableText::ValueTreeWrapper::setJustification (Justification newJustification, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (justification, newJustification.getFlags(), undoManager);
|
||||
}
|
||||
|
||||
Font DrawableText::ValueTreeWrapper::getFont() const
|
||||
{
|
||||
return Font::fromString (state [font]);
|
||||
}
|
||||
|
||||
void DrawableText::ValueTreeWrapper::setFont (const Font& newFont, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (font, newFont.toString(), undoManager);
|
||||
}
|
||||
|
||||
Value DrawableText::ValueTreeWrapper::getFontValue (UndoManager* undoManager)
|
||||
{
|
||||
return state.getPropertyAsValue (font, undoManager);
|
||||
}
|
||||
|
||||
RelativeParallelogram DrawableText::ValueTreeWrapper::getBoundingBox() const
|
||||
{
|
||||
return RelativeParallelogram (state [topLeft].toString(), state [topRight].toString(), state [bottomLeft].toString());
|
||||
}
|
||||
|
||||
void DrawableText::ValueTreeWrapper::setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager);
|
||||
state.setProperty (topRight, newBounds.topRight.toString(), undoManager);
|
||||
state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager);
|
||||
}
|
||||
|
||||
RelativeCoordinate DrawableText::ValueTreeWrapper::getFontHeight() const
|
||||
{
|
||||
return state [fontHeight].toString();
|
||||
}
|
||||
|
||||
void DrawableText::ValueTreeWrapper::setFontHeight (const RelativeCoordinate& coord, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (fontHeight, coord.toString(), undoManager);
|
||||
}
|
||||
|
||||
RelativeCoordinate DrawableText::ValueTreeWrapper::getFontHorizontalScale() const
|
||||
{
|
||||
return state [fontHScale].toString();
|
||||
}
|
||||
|
||||
void DrawableText::ValueTreeWrapper::setFontHorizontalScale (const RelativeCoordinate& coord, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (fontHScale, coord.toString(), undoManager);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableText::refreshFromValueTree (const ValueTree& tree, ComponentBuilder&)
|
||||
{
|
||||
ValueTreeWrapper v (tree);
|
||||
setComponentID (v.getID());
|
||||
|
||||
const RelativeParallelogram newBounds (v.getBoundingBox());
|
||||
const RelativeCoordinate newFontHeight (v.getFontHeight());
|
||||
const RelativeCoordinate newFontHScale (v.getFontHorizontalScale());
|
||||
const Colour newColour (v.getColour());
|
||||
const Justification newJustification (v.getJustification());
|
||||
const String newText (v.getText());
|
||||
const Font newFont (v.getFont());
|
||||
|
||||
if (text != newText || font != newFont || justification != newJustification
|
||||
|| colour != newColour || bounds != newBounds
|
||||
|| newFontHeight != fontHeight || newFontHScale != fontHScale)
|
||||
{
|
||||
setBoundingBox (newBounds);
|
||||
setFontHeight (newFontHeight);
|
||||
setFontHorizontalScale (newFontHScale);
|
||||
setColour (newColour);
|
||||
setFont (newFont, false);
|
||||
setJustification (newJustification);
|
||||
setText (newText);
|
||||
}
|
||||
}
|
||||
|
||||
ValueTree DrawableText::createValueTree (ComponentBuilder::ImageProvider*) const
|
||||
{
|
||||
ValueTree tree (valueTreeType);
|
||||
ValueTreeWrapper v (tree);
|
||||
|
||||
v.setID (getComponentID());
|
||||
v.setText (text, nullptr);
|
||||
v.setFont (font, nullptr);
|
||||
v.setJustification (justification, nullptr);
|
||||
v.setColour (colour, nullptr);
|
||||
v.setBoundingBox (bounds, nullptr);
|
||||
v.setFontHeight (fontHeight, nullptr);
|
||||
v.setFontHorizontalScale (fontHScale, nullptr);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found 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.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_DRAWABLETEXT_H_INCLUDED
|
||||
#define JUCE_DRAWABLETEXT_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A drawable object which renders a line of text.
|
||||
|
||||
@see Drawable
|
||||
*/
|
||||
class JUCE_API DrawableText : public Drawable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a DrawableText object. */
|
||||
DrawableText();
|
||||
DrawableText (const DrawableText&);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawableText();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the text to display.*/
|
||||
void setText (const String& newText);
|
||||
|
||||
/** Returns the currently displayed text */
|
||||
const String& getText() const noexcept { return text;}
|
||||
|
||||
/** Sets the colour of the text. */
|
||||
void setColour (Colour newColour);
|
||||
|
||||
/** Returns the current text colour. */
|
||||
Colour getColour() const noexcept { return colour; }
|
||||
|
||||
/** Sets the font to use.
|
||||
Note that the font height and horizontal scale are set as RelativeCoordinates using
|
||||
setFontHeight and setFontHorizontalScale. If applySizeAndScale is true, then these height
|
||||
and scale values will be changed to match the dimensions of the font supplied;
|
||||
if it is false, then the new font object's height and scale are ignored.
|
||||
*/
|
||||
void setFont (const Font& newFont, bool applySizeAndScale);
|
||||
|
||||
/** Returns the current font. */
|
||||
const Font& getFont() const noexcept { return font; }
|
||||
|
||||
/** Changes the justification of the text within the bounding box. */
|
||||
void setJustification (Justification newJustification);
|
||||
|
||||
/** Returns the current justification. */
|
||||
Justification getJustification() const noexcept { return justification; }
|
||||
|
||||
/** Returns the parallelogram that defines the text bounding box. */
|
||||
const RelativeParallelogram& getBoundingBox() const noexcept { return bounds; }
|
||||
|
||||
/** Sets the bounding box that contains the text. */
|
||||
void setBoundingBox (const RelativeParallelogram& newBounds);
|
||||
|
||||
const RelativeCoordinate& getFontHeight() const { return fontHeight; }
|
||||
void setFontHeight (const RelativeCoordinate& newHeight);
|
||||
|
||||
const RelativeCoordinate& getFontHorizontalScale() const { return fontHScale; }
|
||||
void setFontHorizontalScale (const RelativeCoordinate& newScale);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
Drawable* createCopy() const override;
|
||||
/** @internal */
|
||||
void refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder);
|
||||
/** @internal */
|
||||
ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const override;
|
||||
/** @internal */
|
||||
static const Identifier valueTreeType;
|
||||
/** @internal */
|
||||
Rectangle<float> getDrawableBounds() const override;
|
||||
|
||||
//==============================================================================
|
||||
/** Internally-used class for wrapping a DrawableText's state into a ValueTree. */
|
||||
class ValueTreeWrapper : public Drawable::ValueTreeWrapperBase
|
||||
{
|
||||
public:
|
||||
ValueTreeWrapper (const ValueTree& state);
|
||||
|
||||
String getText() const;
|
||||
void setText (const String& newText, UndoManager* undoManager);
|
||||
Value getTextValue (UndoManager* undoManager);
|
||||
|
||||
Colour getColour() const;
|
||||
void setColour (Colour newColour, UndoManager* undoManager);
|
||||
|
||||
Justification getJustification() const;
|
||||
void setJustification (Justification newJustification, UndoManager* undoManager);
|
||||
|
||||
Font getFont() const;
|
||||
void setFont (const Font& newFont, UndoManager* undoManager);
|
||||
Value getFontValue (UndoManager* undoManager);
|
||||
|
||||
RelativeParallelogram getBoundingBox() const;
|
||||
void setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager);
|
||||
|
||||
RelativeCoordinate getFontHeight() const;
|
||||
void setFontHeight (const RelativeCoordinate& newHeight, UndoManager* undoManager);
|
||||
|
||||
RelativeCoordinate getFontHorizontalScale() const;
|
||||
void setFontHorizontalScale (const RelativeCoordinate& newScale, UndoManager* undoManager);
|
||||
|
||||
static const Identifier text, colour, font, justification, topLeft, topRight, bottomLeft, fontHeight, fontHScale;
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
RelativeParallelogram bounds;
|
||||
RelativeCoordinate fontHeight, fontHScale;
|
||||
Point<float> resolvedPoints[3];
|
||||
Font font, scaledFont;
|
||||
String text;
|
||||
Colour colour;
|
||||
Justification justification;
|
||||
|
||||
friend class Drawable::Positioner<DrawableText>;
|
||||
bool registerCoordinates (RelativeCoordinatePositionerBase&);
|
||||
void recalculateCoordinates (Expression::Scope*);
|
||||
void refreshBounds();
|
||||
|
||||
DrawableText& operator= (const DrawableText&);
|
||||
JUCE_LEAK_DETECTOR (DrawableText)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_DRAWABLETEXT_H_INCLUDED
|
||||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue