mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Removal of RelativeCoordinate classes from the Drawables
This commit is contained in:
parent
ce8b2d865a
commit
3300e71e17
22 changed files with 162 additions and 2089 deletions
|
|
@ -485,9 +485,7 @@ public:
|
|||
if (svgDrawable != nullptr)
|
||||
{
|
||||
// to make our icon the right size, we'll set its bounding box to the size and position that we want.
|
||||
svgDrawable->setBoundingBox (RelativeParallelogram (Point<float> (-100, -100),
|
||||
Point<float> (100, -100),
|
||||
Point<float> (-100, 100)));
|
||||
svgDrawable->setBoundingBox ({ -100.0f, -100.0f, 200.0f, 200.0f });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,17 +43,17 @@ public:
|
|||
|
||||
if (drawable != nullptr)
|
||||
{
|
||||
Rectangle<float> contentBounds (drawable->getDrawableBounds());
|
||||
auto contentBounds = drawable->getDrawableBounds();
|
||||
|
||||
if (DrawableComposite* dc = dynamic_cast<DrawableComposite*> (drawable.get()))
|
||||
if (auto* dc = dynamic_cast<DrawableComposite*> (drawable.get()))
|
||||
{
|
||||
Rectangle<float> r (dc->getContentArea().resolve (nullptr));
|
||||
auto r = dc->getContentArea();
|
||||
|
||||
if (! r.isEmpty())
|
||||
contentBounds = r;
|
||||
}
|
||||
|
||||
Rectangle<float> area = RectanglePlacement (RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize)
|
||||
auto area = RectanglePlacement (RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize)
|
||||
.appliedTo (contentBounds, Rectangle<float> (4.0f, 22.0f, getWidth() - 8.0f, getHeight() - 26.0f));
|
||||
|
||||
Path p;
|
||||
|
|
|
|||
|
|
@ -2210,8 +2210,7 @@ public:
|
|||
|
||||
/** Attempts to set the component's position to the given rectangle.
|
||||
Unlike simply calling Component::setBounds(), this may involve the positioner
|
||||
being smart enough to adjust itself to fit the new bounds, e.g. a RelativeRectangle's
|
||||
positioner may try to reverse the expressions used to make them fit these new coordinates.
|
||||
being smart enough to adjust itself to fit the new bounds.
|
||||
*/
|
||||
virtual void applyNewBounds (const Rectangle<int>& newBounds) = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -209,74 +209,4 @@ Drawable* Drawable::createFromImageFile (const File& file)
|
|||
return fin.openedOk() ? createFromImageDataStream (fin) : nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <class DrawableClass>
|
||||
struct DrawableTypeHandler : public ComponentBuilder::TypeHandler
|
||||
{
|
||||
DrawableTypeHandler() : ComponentBuilder::TypeHandler (DrawableClass::valueTreeType)
|
||||
{
|
||||
}
|
||||
|
||||
Component* addNewComponentFromState (const ValueTree& state, Component* parent)
|
||||
{
|
||||
auto* d = new DrawableClass();
|
||||
|
||||
if (parent != nullptr)
|
||||
parent->addAndMakeVisible (d);
|
||||
|
||||
updateComponentFromState (d, state);
|
||||
return d;
|
||||
}
|
||||
|
||||
void updateComponentFromState (Component* component, const ValueTree& state)
|
||||
{
|
||||
if (auto* d = dynamic_cast<DrawableClass*> (component))
|
||||
d->refreshFromValueTree (state, *this->getBuilder());
|
||||
else
|
||||
jassertfalse;
|
||||
}
|
||||
};
|
||||
|
||||
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());
|
||||
auto* d = dynamic_cast<Drawable*> (static_cast<Component*> (comp));
|
||||
|
||||
if (d != nullptr)
|
||||
comp.release();
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Drawable::ValueTreeWrapperBase::ValueTreeWrapperBase (const ValueTree& s) : state (s)
|
||||
{
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@ namespace juce
|
|||
|
||||
@see DrawableComposite, DrawableImage, DrawablePath, DrawableText
|
||||
*/
|
||||
class JUCE_API Drawable : public Component,
|
||||
public MarkerList::MarkerListHolder
|
||||
class JUCE_API Drawable : public Component
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
|
|
@ -177,22 +176,6 @@ public:
|
|||
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.
|
||||
|
|
@ -204,30 +187,6 @@ public:
|
|||
*/
|
||||
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);
|
||||
|
||||
MarkerList* getMarkers (bool) override { return nullptr; }
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
friend class DrawableComposite;
|
||||
|
|
@ -245,42 +204,9 @@ protected:
|
|||
Point<int> originRelativeToComponent;
|
||||
ScopedPointer<Drawable> drawableClipPath;
|
||||
|
||||
#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() override { return owner.registerCoordinates (*this); }
|
||||
|
||||
void applyToComponentBounds() override
|
||||
{
|
||||
ComponentScope scope (getComponent());
|
||||
owner.recalculateCoordinates (&scope);
|
||||
}
|
||||
|
||||
void applyNewBounds (const Rectangle<int>&) override
|
||||
{
|
||||
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 (const Drawable&);
|
||||
Drawable& operator= (const Drawable&);
|
||||
JUCE_LEAK_DETECTOR (Drawable)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,16 +28,15 @@ namespace juce
|
|||
{
|
||||
|
||||
DrawableComposite::DrawableComposite()
|
||||
: bounds (Point<float>(), Point<float> (100.0f, 0.0f), Point<float> (0.0f, 100.0f))
|
||||
: bounds ({ 0.0f, 0.0f, 100.0f, 100.0f })
|
||||
{
|
||||
setContentArea (RelativeRectangle (Rectangle<float> (0.0f, 0.0f, 100.0f, 100.0f)));
|
||||
setContentArea ({ 0.0f, 0.0f, 100.0f, 100.0f });
|
||||
}
|
||||
|
||||
DrawableComposite::DrawableComposite (const DrawableComposite& other)
|
||||
: Drawable (other),
|
||||
bounds (other.bounds),
|
||||
markersX (other.markersX),
|
||||
markersY (other.markersY)
|
||||
contentArea (other.contentArea)
|
||||
{
|
||||
for (auto* c : other.getChildren())
|
||||
if (auto* d = dynamic_cast<const Drawable*> (c))
|
||||
|
|
@ -67,87 +66,44 @@ Rectangle<float> DrawableComposite::getDrawableBounds() const
|
|||
return r;
|
||||
}
|
||||
|
||||
MarkerList* DrawableComposite::getMarkers (bool xAxis)
|
||||
void DrawableComposite::setContentArea (Rectangle<float> newArea)
|
||||
{
|
||||
return xAxis ? &markersX : &markersY;
|
||||
contentArea = newArea;
|
||||
}
|
||||
|
||||
RelativeRectangle DrawableComposite::getContentArea() const
|
||||
void DrawableComposite::setBoundingBox (Rectangle<float> newBounds)
|
||||
{
|
||||
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);
|
||||
setBoundingBox (Parallelogram<float> (newBounds));
|
||||
}
|
||||
|
||||
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)
|
||||
void DrawableComposite::setBoundingBox (Parallelogram<float> newBounds)
|
||||
{
|
||||
if (bounds != newBounds)
|
||||
{
|
||||
bounds = newBounds;
|
||||
|
||||
if (bounds.isDynamic())
|
||||
{
|
||||
auto p = new Drawable::Positioner<DrawableComposite> (*this);
|
||||
setPositioner (p);
|
||||
p->apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
setPositioner (nullptr);
|
||||
recalculateCoordinates (nullptr);
|
||||
}
|
||||
auto t = AffineTransform::fromTargetPoints (contentArea.getTopLeft(), bounds.topLeft,
|
||||
contentArea.getTopRight(), bounds.topRight,
|
||||
contentArea.getBottomLeft(), bounds.bottomLeft);
|
||||
|
||||
if (t.isSingularity())
|
||||
t = {};
|
||||
|
||||
setTransform (t);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableComposite::resetBoundingBoxToContentArea()
|
||||
{
|
||||
const RelativeRectangle content (getContentArea());
|
||||
|
||||
setBoundingBox (RelativeParallelogram (RelativePoint (content.left, content.top),
|
||||
RelativePoint (content.right, content.top),
|
||||
RelativePoint (content.left, content.bottom)));
|
||||
setBoundingBox (contentArea);
|
||||
}
|
||||
|
||||
void DrawableComposite::resetContentAreaAndBoundingBoxToFitChildren()
|
||||
{
|
||||
setContentArea (RelativeRectangle (getDrawableBounds()));
|
||||
setContentArea (getDrawableBounds());
|
||||
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);
|
||||
|
||||
auto content = getContentArea().resolve (scope);
|
||||
|
||||
auto 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();
|
||||
|
||||
setTransform (t);
|
||||
}
|
||||
|
||||
void DrawableComposite::parentHierarchyChanged()
|
||||
{
|
||||
if (auto* parent = getParent())
|
||||
|
|
@ -194,131 +150,6 @@ void DrawableComposite::updateBoundsToFitChildren()
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
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 (auto* c : getChildren())
|
||||
{
|
||||
auto* d = dynamic_cast<const Drawable*> (c);
|
||||
jassert (d != nullptr); // You can't save a mix of Drawables and normal components!
|
||||
|
||||
childList.appendChild (d->createValueTree (imageProvider), nullptr);
|
||||
}
|
||||
|
||||
v.getMarkerListCreating (true, nullptr).readFrom (markersX, nullptr);
|
||||
v.getMarkerListCreating (false, nullptr).readFrom (markersY, nullptr);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
Path DrawableComposite::getOutlineAsPath() const
|
||||
{
|
||||
Path p;
|
||||
|
|
|
|||
|
|
@ -55,12 +55,17 @@ public:
|
|||
/** Sets the parallelogram that defines the target position of the content rectangle when the drawable is rendered.
|
||||
@see setContentArea
|
||||
*/
|
||||
void setBoundingBox (const RelativeParallelogram& newBoundingBox);
|
||||
void setBoundingBox (Parallelogram<float> newBoundingBox);
|
||||
|
||||
/** Sets the rectangle that defines the target position of the content rectangle when the drawable is rendered.
|
||||
@see setContentArea
|
||||
*/
|
||||
void setBoundingBox (Rectangle<float> 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; }
|
||||
Parallelogram<float> 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.
|
||||
|
|
@ -68,44 +73,24 @@ public:
|
|||
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;
|
||||
Rectangle<float> getContentArea() const noexcept { return contentArea; }
|
||||
|
||||
/** 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);
|
||||
void setContentArea (Rectangle<float> newArea);
|
||||
|
||||
/** Resets the content area and the bounding transform to fit around the area occupied
|
||||
by the child components (ignoring any markers).
|
||||
by the child components.
|
||||
*/
|
||||
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 override;
|
||||
/** @internal */
|
||||
void refreshFromValueTree (const ValueTree&, ComponentBuilder&);
|
||||
/** @internal */
|
||||
ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const override;
|
||||
/** @internal */
|
||||
static const Identifier valueTreeType;
|
||||
/** @internal */
|
||||
Rectangle<float> getDrawableBounds() const override;
|
||||
/** @internal */
|
||||
void childBoundsChanged (Component*) override;
|
||||
|
|
@ -114,46 +99,14 @@ public:
|
|||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
MarkerList* getMarkers (bool xAxis) override;
|
||||
/** @internal */
|
||||
Path getOutlineAsPath() const 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;
|
||||
Parallelogram<float> bounds;
|
||||
Rectangle<float> contentArea;
|
||||
bool updateBoundsReentrant = false;
|
||||
|
||||
friend class Drawable::Positioner<DrawableComposite>;
|
||||
bool registerCoordinates (RelativeCoordinatePositionerBase&);
|
||||
void recalculateCoordinates (Expression::Scope*);
|
||||
|
||||
void updateBoundsToFitChildren();
|
||||
|
||||
DrawableComposite& operator= (const DrawableComposite&);
|
||||
|
|
|
|||
|
|
@ -27,12 +27,8 @@
|
|||
namespace juce
|
||||
{
|
||||
|
||||
DrawableImage::DrawableImage()
|
||||
: opacity (1.0f),
|
||||
overlayColour (0x00000000)
|
||||
DrawableImage::DrawableImage() : bounds ({ 0.0f, 0.0f, 1.0f, 1.0f })
|
||||
{
|
||||
bounds.topRight = RelativePoint (Point<float> (1.0f, 0.0f));
|
||||
bounds.bottomLeft = RelativePoint (Point<float> (0.0f, 1.0f));
|
||||
}
|
||||
|
||||
DrawableImage::DrawableImage (const DrawableImage& other)
|
||||
|
|
@ -49,19 +45,22 @@ DrawableImage::~DrawableImage()
|
|||
{
|
||||
}
|
||||
|
||||
Drawable* DrawableImage::createCopy() const
|
||||
{
|
||||
return new DrawableImage (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableImage::setImage (const Image& imageToUse)
|
||||
{
|
||||
if (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);
|
||||
|
||||
setBounds (image.getBounds());
|
||||
setBoundingBox (image.getBounds().toFloat());
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableImage::setOpacity (const float newOpacity)
|
||||
{
|
||||
|
|
@ -73,54 +72,33 @@ void DrawableImage::setOverlayColour (Colour newOverlayColour)
|
|||
overlayColour = newOverlayColour;
|
||||
}
|
||||
|
||||
void DrawableImage::setBoundingBox (const RelativeParallelogram& newBounds)
|
||||
void DrawableImage::setBoundingBox (Rectangle<float> newBounds)
|
||||
{
|
||||
setBoundingBox (Parallelogram<float> (newBounds));
|
||||
}
|
||||
|
||||
void DrawableImage::setBoundingBox (Parallelogram<float> 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);
|
||||
auto tr = bounds.topLeft + (bounds.topRight - bounds.topLeft) / (float) image.getWidth();
|
||||
auto bl = bounds.topLeft + (bounds.bottomLeft - bounds.topLeft) / (float) image.getHeight();
|
||||
|
||||
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,
|
||||
auto t = AffineTransform::fromTargetPoints (bounds.topLeft.x, bounds.topLeft.y,
|
||||
tr.x, tr.y,
|
||||
bl.x, bl.y));
|
||||
bl.x, bl.y);
|
||||
|
||||
if (t.isSingularity())
|
||||
t = AffineTransform();
|
||||
t = {};
|
||||
|
||||
setTransform (t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableImage::paint (Graphics& g)
|
||||
|
|
@ -151,149 +129,6 @@ 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;
|
||||
}
|
||||
|
||||
Path DrawableImage::getOutlineAsPath() const
|
||||
{
|
||||
return {}; // not applicable for images
|
||||
|
|
|
|||
|
|
@ -71,13 +71,16 @@ public:
|
|||
Colour getOverlayColour() const noexcept { return overlayColour; }
|
||||
|
||||
/** Sets the bounding box within which the image should be displayed. */
|
||||
void setBoundingBox (const RelativeParallelogram& newBounds);
|
||||
void setBoundingBox (Parallelogram<float> newBounds);
|
||||
|
||||
/** Sets the bounding box within which the image should be displayed. */
|
||||
void setBoundingBox (Rectangle<float> 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; }
|
||||
Parallelogram<float> getBoundingBox() const noexcept { return bounds; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
|
|
@ -89,49 +92,14 @@ public:
|
|||
/** @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;
|
||||
/** @internal */
|
||||
Path getOutlineAsPath() const override;
|
||||
|
||||
//==============================================================================
|
||||
/** 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*);
|
||||
float opacity = 1.0f;
|
||||
Colour overlayColour { 0 };
|
||||
Parallelogram<float> bounds;
|
||||
|
||||
DrawableImage& operator= (const DrawableImage&);
|
||||
JUCE_LEAK_DETECTOR (DrawableImage)
|
||||
|
|
|
|||
|
|
@ -27,551 +27,32 @@
|
|||
namespace juce
|
||||
{
|
||||
|
||||
DrawablePath::DrawablePath()
|
||||
{
|
||||
}
|
||||
DrawablePath::DrawablePath() {}
|
||||
DrawablePath::~DrawablePath() {}
|
||||
|
||||
DrawablePath::DrawablePath (const DrawablePath& other)
|
||||
: DrawableShape (other)
|
||||
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
|
||||
void DrawablePath::setPath (Path&& newPath)
|
||||
{
|
||||
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);
|
||||
path = static_cast<Path&&> (newPath);
|
||||
pathChanged();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class DrawablePath::RelativePositioner : public RelativeCoordinatePositionerBase
|
||||
{
|
||||
public:
|
||||
RelativePositioner (DrawablePath& comp)
|
||||
: RelativeCoordinatePositionerBase (comp),
|
||||
owner (comp)
|
||||
{
|
||||
}
|
||||
|
||||
bool registerCoordinates() override
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
jassert (owner.relativePath != nullptr);
|
||||
const RelativePointPath& relPath = *owner.relativePath;
|
||||
|
||||
for (int i = 0; i < relPath.elements.size(); ++i)
|
||||
{
|
||||
RelativePointPath::ElementBase* const e = relPath.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() override
|
||||
{
|
||||
jassert (owner.relativePath != nullptr);
|
||||
|
||||
ComponentScope scope (getComponent());
|
||||
owner.applyRelativePath (*owner.relativePath, &scope);
|
||||
}
|
||||
|
||||
void applyNewBounds (const Rectangle<int>&) override
|
||||
{
|
||||
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.reset();
|
||||
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.appendChild (p.elements.getUnchecked(i)->createTree(), 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;
|
||||
}
|
||||
const Path& DrawablePath::getPath() const { return path; }
|
||||
const Path& DrawablePath::getStrokePath() const { return strokePath; }
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -52,11 +52,10 @@ public:
|
|||
*/
|
||||
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.
|
||||
/** Changes the path that will be drawn.
|
||||
@see setFill, setStrokeType
|
||||
*/
|
||||
void setPath (const RelativePointPath& newPath);
|
||||
void setPath (Path&& newPath);
|
||||
|
||||
/** Returns the current path. */
|
||||
const Path& getPath() const;
|
||||
|
|
@ -67,77 +66,9 @@ public:
|
|||
//==============================================================================
|
||||
/** @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& relativePath, UndoManager* undoManager);
|
||||
void writeTo (RelativePointPath& relativePath) 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)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,9 +27,8 @@
|
|||
namespace juce
|
||||
{
|
||||
|
||||
DrawableRectangle::DrawableRectangle()
|
||||
{
|
||||
}
|
||||
DrawableRectangle::DrawableRectangle() {}
|
||||
DrawableRectangle::~DrawableRectangle() {}
|
||||
|
||||
DrawableRectangle::DrawableRectangle (const DrawableRectangle& other)
|
||||
: DrawableShape (other),
|
||||
|
|
@ -39,17 +38,13 @@ DrawableRectangle::DrawableRectangle (const DrawableRectangle& other)
|
|||
rebuildPath();
|
||||
}
|
||||
|
||||
DrawableRectangle::~DrawableRectangle()
|
||||
{
|
||||
}
|
||||
|
||||
Drawable* DrawableRectangle::createCopy() const
|
||||
{
|
||||
return new DrawableRectangle (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableRectangle::setRectangle (const RelativeParallelogram& newBounds)
|
||||
void DrawableRectangle::setRectangle (Parallelogram<float> newBounds)
|
||||
{
|
||||
if (bounds != newBounds)
|
||||
{
|
||||
|
|
@ -58,7 +53,7 @@ void DrawableRectangle::setRectangle (const RelativeParallelogram& newBounds)
|
|||
}
|
||||
}
|
||||
|
||||
void DrawableRectangle::setCornerSize (const RelativePoint& newSize)
|
||||
void DrawableRectangle::setCornerSize (Point<float> newSize)
|
||||
{
|
||||
if (cornerSize != newSize)
|
||||
{
|
||||
|
|
@ -69,48 +64,19 @@ void DrawableRectangle::setCornerSize (const RelativePoint& newSize)
|
|||
|
||||
void DrawableRectangle::rebuildPath()
|
||||
{
|
||||
if (bounds.isDynamic() || cornerSize.isDynamic())
|
||||
{
|
||||
auto 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);
|
||||
|
||||
auto cornerSizeX = (float) cornerSize.x.resolve (scope);
|
||||
auto cornerSizeY = (float) cornerSize.y.resolve (scope);
|
||||
|
||||
auto w = Line<float> (points[0], points[1]).getLength();
|
||||
auto h = Line<float> (points[0], points[2]).getLength();
|
||||
auto w = bounds.getWidth();
|
||||
auto h = bounds.getHeight();
|
||||
|
||||
Path newPath;
|
||||
|
||||
if (cornerSizeX > 0 && cornerSizeY > 0)
|
||||
newPath.addRoundedRectangle (0, 0, w, h, cornerSizeX, cornerSizeY);
|
||||
if (cornerSize.x > 0 && cornerSize.y > 0)
|
||||
newPath.addRoundedRectangle (0, 0, w, h, cornerSize.x, cornerSize.y);
|
||||
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));
|
||||
newPath.applyTransform (AffineTransform::fromTargetPoints (Point<float>(), bounds.topLeft,
|
||||
Point<float> (w, 0), bounds.topRight,
|
||||
Point<float> (0, h), bounds.bottomLeft));
|
||||
|
||||
if (path != newPath)
|
||||
{
|
||||
|
|
@ -119,72 +85,4 @@ void DrawableRectangle::recalculateCoordinates (Expression::Scope* scope)
|
|||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -47,54 +47,26 @@ public:
|
|||
|
||||
//==============================================================================
|
||||
/** Sets the rectangle's bounds. */
|
||||
void setRectangle (const RelativeParallelogram& newBounds);
|
||||
void setRectangle (Parallelogram<float> newBounds);
|
||||
|
||||
/** Returns the rectangle's bounds. */
|
||||
const RelativeParallelogram& getRectangle() const noexcept { return bounds; }
|
||||
Parallelogram<float> getRectangle() const noexcept { return bounds; }
|
||||
|
||||
/** Returns the corner size to be used. */
|
||||
const RelativePoint& getCornerSize() const noexcept { return cornerSize; }
|
||||
Point<float> getCornerSize() const noexcept { return cornerSize; }
|
||||
|
||||
/** Sets a new corner size for the rectangle */
|
||||
void setCornerSize (const RelativePoint& newSize);
|
||||
void setCornerSize (Point<float> 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;
|
||||
Parallelogram<float> bounds;
|
||||
Point<float> cornerSize;
|
||||
|
||||
void rebuildPath();
|
||||
bool registerCoordinates (RelativeCoordinatePositionerBase&);
|
||||
void recalculateCoordinates (Expression::Scope*);
|
||||
|
||||
DrawableRectangle& operator= (const DrawableRectangle&);
|
||||
JUCE_LEAK_DETECTOR (DrawableRectangle)
|
||||
|
|
|
|||
|
|
@ -48,85 +48,22 @@ 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() override
|
||||
{
|
||||
bool ok = addPoint (fill.gradientPoint1);
|
||||
ok = addPoint (fill.gradientPoint2) && ok;
|
||||
return addPoint (fill.gradientPoint3) && ok;
|
||||
}
|
||||
|
||||
void applyToComponentBounds() override
|
||||
{
|
||||
ComponentScope scope (owner);
|
||||
if (isMainFill ? owner.mainFill.recalculateCoords (&scope)
|
||||
: owner.strokeFill.recalculateCoords (&scope))
|
||||
owner.repaint();
|
||||
}
|
||||
|
||||
void applyNewBounds (const Rectangle<int>&) override
|
||||
{
|
||||
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)
|
||||
if (mainFill != newFill)
|
||||
{
|
||||
setStrokeFill (RelativeFillType (newFill));
|
||||
}
|
||||
|
||||
void DrawableShape::setFillInternal (RelativeFillType& fill, const RelativeFillType& newFill,
|
||||
ScopedPointer<RelativeCoordinatePositionerBase>& pos)
|
||||
{
|
||||
if (fill != newFill)
|
||||
{
|
||||
fill = newFill;
|
||||
pos.reset();
|
||||
|
||||
if (fill.isDynamic())
|
||||
{
|
||||
pos = new RelativePositioner (*this, fill, true);
|
||||
pos->apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
fill.recalculateCoords (nullptr);
|
||||
}
|
||||
|
||||
mainFill = newFill;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableShape::setFill (const RelativeFillType& newFill)
|
||||
void DrawableShape::setStrokeFill (const FillType& newFill)
|
||||
{
|
||||
setFillInternal (mainFill, newFill, mainFillPositioner);
|
||||
if (strokeFill != newFill)
|
||||
{
|
||||
strokeFill = newFill;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void DrawableShape::setStrokeFill (const RelativeFillType& newFill)
|
||||
{
|
||||
setFillInternal (strokeFill, newFill, strokeFillPositioner);
|
||||
}
|
||||
|
||||
void DrawableShape::setStrokeType (const PathStrokeType& newStrokeType)
|
||||
|
|
@ -154,20 +91,7 @@ void DrawableShape::setStrokeThickness (const float newThickness)
|
|||
|
||||
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);
|
||||
return strokeType.getStrokeThickness() > 0.0f && ! strokeFill.isInvisible();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -176,12 +100,12 @@ void DrawableShape::paint (Graphics& g)
|
|||
transformContextToCorrectOrigin (g);
|
||||
applyDrawableClipPath (g);
|
||||
|
||||
g.setFillType (mainFill.fill);
|
||||
g.setFillType (mainFill);
|
||||
g.fillPath (path);
|
||||
|
||||
if (isStrokeVisible())
|
||||
{
|
||||
g.setFillType (strokeFill.fill);
|
||||
g.setFillType (strokeFill);
|
||||
g.fillPath (strokePath);
|
||||
}
|
||||
}
|
||||
|
|
@ -222,262 +146,17 @@ bool DrawableShape::hitTest (int x, int y)
|
|||
if (! allowsClicksOnThisComponent)
|
||||
return false;
|
||||
|
||||
const float globalX = (float) (x - originRelativeToComponent.x);
|
||||
const float globalY = (float) (y - originRelativeToComponent.y);
|
||||
auto globalX = (float) (x - originRelativeToComponent.x);
|
||||
auto globalY = (float) (y - originRelativeToComponent.y);
|
||||
|
||||
return path.contains (globalX, globalY)
|
||||
|| (isStrokeVisible() && strokePath.contains (globalX, globalY));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
DrawableShape::RelativeFillType::RelativeFillType()
|
||||
static bool replaceColourInFill (FillType& fill, Colour original, Colour replacement)
|
||||
{
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
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())
|
||||
if (fill.colour == original && fill.isColour())
|
||||
{
|
||||
fill = FillType (replacement);
|
||||
return true;
|
||||
|
|
@ -495,7 +174,7 @@ bool DrawableShape::replaceColour (Colour original, Colour replacement)
|
|||
|
||||
Path DrawableShape::getOutlineAsPath() const
|
||||
{
|
||||
Path outline (isStrokeVisible() ? strokePath : path);
|
||||
auto outline = isStrokeVisible() ? strokePath : path;
|
||||
outline.applyTransform (getTransform());
|
||||
return outline;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,31 +45,6 @@ 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
|
||||
|
|
@ -80,34 +55,20 @@ public:
|
|||
*/
|
||||
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; }
|
||||
const FillType& 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; }
|
||||
const FillType& 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.
|
||||
|
|
@ -130,24 +91,6 @@ public:
|
|||
const Array<float>& getDashLengths() const noexcept { return dashLengths; }
|
||||
|
||||
//==============================================================================
|
||||
/** @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 */
|
||||
|
|
@ -167,10 +110,6 @@ protected:
|
|||
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;
|
||||
|
|
@ -178,12 +117,7 @@ protected:
|
|||
Path path, strokePath;
|
||||
|
||||
private:
|
||||
class RelativePositioner;
|
||||
RelativeFillType mainFill, strokeFill;
|
||||
ScopedPointer<RelativeCoordinatePositionerBase> mainFillPositioner, strokeFillPositioner;
|
||||
|
||||
void setFillInternal (RelativeFillType& fill, const RelativeFillType& newFill,
|
||||
ScopedPointer<RelativeCoordinatePositionerBase>& positioner);
|
||||
FillType mainFill, strokeFill;
|
||||
|
||||
DrawableShape& operator= (const DrawableShape&);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,9 +31,7 @@ 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)));
|
||||
setBoundingBox (Parallelogram<float> ({ 0.0f, 0.0f, 50.0f, 20.0f }));
|
||||
setFont (Font (15.0f), true);
|
||||
}
|
||||
|
||||
|
|
@ -54,6 +52,11 @@ DrawableText::~DrawableText()
|
|||
{
|
||||
}
|
||||
|
||||
Drawable* DrawableText::createCopy() const
|
||||
{
|
||||
return new DrawableText (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableText::setText (const String& newText)
|
||||
{
|
||||
|
|
@ -95,7 +98,7 @@ void DrawableText::setJustification (Justification newJustification)
|
|||
repaint();
|
||||
}
|
||||
|
||||
void DrawableText::setBoundingBox (const RelativeParallelogram& newBounds)
|
||||
void DrawableText::setBoundingBox (Parallelogram<float> newBounds)
|
||||
{
|
||||
if (bounds != newBounds)
|
||||
{
|
||||
|
|
@ -104,7 +107,7 @@ void DrawableText::setBoundingBox (const RelativeParallelogram& newBounds)
|
|||
}
|
||||
}
|
||||
|
||||
void DrawableText::setFontHeight (const RelativeCoordinate& newHeight)
|
||||
void DrawableText::setFontHeight (float newHeight)
|
||||
{
|
||||
if (fontHeight != newHeight)
|
||||
{
|
||||
|
|
@ -113,7 +116,7 @@ void DrawableText::setFontHeight (const RelativeCoordinate& newHeight)
|
|||
}
|
||||
}
|
||||
|
||||
void DrawableText::setFontHorizontalScale (const RelativeCoordinate& newScale)
|
||||
void DrawableText::setFontHorizontalScale (float newScale)
|
||||
{
|
||||
if (fontHScale != newScale)
|
||||
{
|
||||
|
|
@ -124,37 +127,11 @@ void DrawableText::setFontHorizontalScale (const RelativeCoordinate& newScale)
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
auto w = bounds.getWidth();
|
||||
auto h = bounds.getHeight();
|
||||
|
||||
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));
|
||||
auto height = jlimit (0.01f, jmax (0.01f, h), fontHeight);
|
||||
auto hscale = jlimit (0.01f, jmax (0.01f, w), fontHScale);
|
||||
|
||||
scaledFont = font;
|
||||
scaledFont.setHeight (height);
|
||||
|
|
@ -172,17 +149,17 @@ Rectangle<int> DrawableText::getTextArea (float w, float h) const
|
|||
|
||||
AffineTransform DrawableText::getTextTransform (float w, float h) const
|
||||
{
|
||||
return 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);
|
||||
return AffineTransform::fromTargetPoints (Point<float>(), bounds.topLeft,
|
||||
Point<float> (w, 0), bounds.topRight,
|
||||
Point<float> (0, h), bounds.bottomLeft);
|
||||
}
|
||||
|
||||
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();
|
||||
auto w = bounds.getWidth();
|
||||
auto h = bounds.getHeight();
|
||||
|
||||
g.addTransform (getTextTransform (w, h));
|
||||
g.setFont (scaledFont);
|
||||
|
|
@ -193,166 +170,14 @@ void DrawableText::paint (Graphics& g)
|
|||
|
||||
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;
|
||||
return bounds.getBoundingBox();
|
||||
}
|
||||
|
||||
Path DrawableText::getOutlineAsPath() const
|
||||
{
|
||||
auto w = Line<float> (resolvedPoints[0], resolvedPoints[1]).getLength();
|
||||
auto h = Line<float> (resolvedPoints[0], resolvedPoints[2]).getLength();
|
||||
const auto area = getTextArea (w, h).toFloat();
|
||||
auto w = bounds.getWidth();
|
||||
auto h = bounds.getHeight();
|
||||
auto area = getTextArea (w, h).toFloat();
|
||||
|
||||
GlyphArrangement arr;
|
||||
arr.addFittedText (scaledFont, text,
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ public:
|
|||
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
|
||||
Note that the font height and horizontal scale are set 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.
|
||||
*/
|
||||
|
|
@ -75,16 +75,16 @@ public:
|
|||
Justification getJustification() const noexcept { return justification; }
|
||||
|
||||
/** Returns the parallelogram that defines the text bounding box. */
|
||||
const RelativeParallelogram& getBoundingBox() const noexcept { return bounds; }
|
||||
Parallelogram<float> getBoundingBox() const noexcept { return bounds; }
|
||||
|
||||
/** Sets the bounding box that contains the text. */
|
||||
void setBoundingBox (const RelativeParallelogram& newBounds);
|
||||
void setBoundingBox (Parallelogram<float> newBounds);
|
||||
|
||||
const RelativeCoordinate& getFontHeight() const { return fontHeight; }
|
||||
void setFontHeight (const RelativeCoordinate& newHeight);
|
||||
float getFontHeight() const noexcept { return fontHeight; }
|
||||
void setFontHeight (float newHeight);
|
||||
|
||||
const RelativeCoordinate& getFontHorizontalScale() const { return fontHScale; }
|
||||
void setFontHorizontalScale (const RelativeCoordinate& newScale);
|
||||
float getFontHorizontalScale() const noexcept { return fontHScale; }
|
||||
void setFontHorizontalScale (float newScale);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
|
|
@ -92,62 +92,19 @@ public:
|
|||
/** @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;
|
||||
/** @internal */
|
||||
Path getOutlineAsPath() 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];
|
||||
Parallelogram<float> bounds;
|
||||
float fontHeight, fontHScale;
|
||||
Font font, scaledFont;
|
||||
String text;
|
||||
Colour colour;
|
||||
Justification justification;
|
||||
|
||||
friend class Drawable::Positioner<DrawableText>;
|
||||
bool registerCoordinates (RelativeCoordinatePositionerBase&);
|
||||
void recalculateCoordinates (Expression::Scope*);
|
||||
void refreshBounds();
|
||||
Rectangle<int> getTextArea (float width, float height) const;
|
||||
AffineTransform getTextTransform (float width, float height) const;
|
||||
|
|
|
|||
|
|
@ -196,10 +196,7 @@ public:
|
|||
|
||||
newState.parseSubElements (xml, *drawable);
|
||||
|
||||
drawable->setContentArea (RelativeRectangle (RelativeCoordinate (viewboxXY.x),
|
||||
RelativeCoordinate (viewboxXY.x + newState.viewBoxW),
|
||||
RelativeCoordinate (viewboxXY.y),
|
||||
RelativeCoordinate (viewboxXY.y + newState.viewBoxH)));
|
||||
drawable->setContentArea ({ viewboxXY.x, viewboxXY.y, newState.viewBoxW, newState.viewBoxH });
|
||||
drawable->resetBoundingBoxToContentArea();
|
||||
|
||||
return drawable;
|
||||
|
|
|
|||
|
|
@ -182,7 +182,6 @@ ComponentBuilder::TypeHandler* ComponentBuilder::getHandler (const int index) co
|
|||
|
||||
void ComponentBuilder::registerStandardComponentTypes()
|
||||
{
|
||||
Drawable::registerDrawableTypeHandlers (*this);
|
||||
}
|
||||
|
||||
void ComponentBuilder::setImageProvider (ImageProvider* newImageProvider) noexcept
|
||||
|
|
|
|||
|
|
@ -95,9 +95,9 @@ AffineTransform RelativeParallelogram::resetToPerpendicular (Expression::Scope*
|
|||
topRight.moveToAbsolute (newTopRight, scope);
|
||||
bottomLeft.moveToAbsolute (newBottomLeft, scope);
|
||||
|
||||
return AffineTransform::fromTargetPoints (corners[0].x, corners[0].y, corners[0].x, corners[0].y,
|
||||
corners[1].x, corners[1].y, newTopRight.x, newTopRight.y,
|
||||
corners[2].x, corners[2].y, newBottomLeft.x, newBottomLeft.y);
|
||||
return AffineTransform::fromTargetPoints (corners[0], corners[0],
|
||||
corners[1], newTopRight,
|
||||
corners[2], newBottomLeft);
|
||||
}
|
||||
|
||||
bool RelativeParallelogram::isDynamic() const
|
||||
|
|
|
|||
|
|
@ -147,13 +147,6 @@ RelativePointPath::StartSubPath::StartSubPath (const RelativePoint& pos)
|
|||
{
|
||||
}
|
||||
|
||||
ValueTree RelativePointPath::StartSubPath::createTree() const
|
||||
{
|
||||
ValueTree v (DrawablePath::ValueTreeWrapper::Element::startSubPathElement);
|
||||
v.setProperty (DrawablePath::ValueTreeWrapper::point1, startPos.toString(), nullptr);
|
||||
return v;
|
||||
}
|
||||
|
||||
void RelativePointPath::StartSubPath::addToPath (Path& path, Expression::Scope* scope) const
|
||||
{
|
||||
path.startNewSubPath (startPos.resolve (scope));
|
||||
|
|
@ -176,11 +169,6 @@ RelativePointPath::CloseSubPath::CloseSubPath()
|
|||
{
|
||||
}
|
||||
|
||||
ValueTree RelativePointPath::CloseSubPath::createTree() const
|
||||
{
|
||||
return ValueTree (DrawablePath::ValueTreeWrapper::Element::closeSubPathElement);
|
||||
}
|
||||
|
||||
void RelativePointPath::CloseSubPath::addToPath (Path& path, Expression::Scope*) const
|
||||
{
|
||||
path.closeSubPath();
|
||||
|
|
@ -203,13 +191,6 @@ RelativePointPath::LineTo::LineTo (const RelativePoint& endPoint_)
|
|||
{
|
||||
}
|
||||
|
||||
ValueTree RelativePointPath::LineTo::createTree() const
|
||||
{
|
||||
ValueTree v (DrawablePath::ValueTreeWrapper::Element::lineToElement);
|
||||
v.setProperty (DrawablePath::ValueTreeWrapper::point1, endPoint.toString(), nullptr);
|
||||
return v;
|
||||
}
|
||||
|
||||
void RelativePointPath::LineTo::addToPath (Path& path, Expression::Scope* scope) const
|
||||
{
|
||||
path.lineTo (endPoint.resolve (scope));
|
||||
|
|
@ -234,14 +215,6 @@ RelativePointPath::QuadraticTo::QuadraticTo (const RelativePoint& controlPoint,
|
|||
controlPoints[1] = endPoint;
|
||||
}
|
||||
|
||||
ValueTree RelativePointPath::QuadraticTo::createTree() const
|
||||
{
|
||||
ValueTree v (DrawablePath::ValueTreeWrapper::Element::quadraticToElement);
|
||||
v.setProperty (DrawablePath::ValueTreeWrapper::point1, controlPoints[0].toString(), nullptr);
|
||||
v.setProperty (DrawablePath::ValueTreeWrapper::point2, controlPoints[1].toString(), nullptr);
|
||||
return v;
|
||||
}
|
||||
|
||||
void RelativePointPath::QuadraticTo::addToPath (Path& path, Expression::Scope* scope) const
|
||||
{
|
||||
path.quadraticTo (controlPoints[0].resolve (scope),
|
||||
|
|
@ -269,15 +242,6 @@ RelativePointPath::CubicTo::CubicTo (const RelativePoint& controlPoint1, const R
|
|||
controlPoints[2] = endPoint;
|
||||
}
|
||||
|
||||
ValueTree RelativePointPath::CubicTo::createTree() const
|
||||
{
|
||||
ValueTree v (DrawablePath::ValueTreeWrapper::Element::cubicToElement);
|
||||
v.setProperty (DrawablePath::ValueTreeWrapper::point1, controlPoints[0].toString(), nullptr);
|
||||
v.setProperty (DrawablePath::ValueTreeWrapper::point2, controlPoints[1].toString(), nullptr);
|
||||
v.setProperty (DrawablePath::ValueTreeWrapper::point3, controlPoints[2].toString(), nullptr);
|
||||
return v;
|
||||
}
|
||||
|
||||
void RelativePointPath::CubicTo::addToPath (Path& path, Expression::Scope* scope) const
|
||||
{
|
||||
path.cubicTo (controlPoints[0].resolve (scope),
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@ public:
|
|||
public:
|
||||
ElementBase (ElementType type);
|
||||
virtual ~ElementBase() {}
|
||||
virtual ValueTree createTree() const = 0;
|
||||
virtual void addToPath (Path& path, Expression::Scope*) const = 0;
|
||||
virtual RelativePoint* getControlPoints (int& numPoints) = 0;
|
||||
virtual ElementBase* clone() const = 0;
|
||||
|
|
@ -97,7 +96,6 @@ public:
|
|||
{
|
||||
public:
|
||||
StartSubPath (const RelativePoint& pos);
|
||||
ValueTree createTree() const;
|
||||
void addToPath (Path& path, Expression::Scope*) const;
|
||||
RelativePoint* getControlPoints (int& numPoints);
|
||||
ElementBase* clone() const;
|
||||
|
|
@ -113,7 +111,6 @@ public:
|
|||
{
|
||||
public:
|
||||
CloseSubPath();
|
||||
ValueTree createTree() const;
|
||||
void addToPath (Path& path, Expression::Scope*) const;
|
||||
RelativePoint* getControlPoints (int& numPoints);
|
||||
ElementBase* clone() const;
|
||||
|
|
@ -127,7 +124,6 @@ public:
|
|||
{
|
||||
public:
|
||||
LineTo (const RelativePoint& endPoint);
|
||||
ValueTree createTree() const;
|
||||
void addToPath (Path& path, Expression::Scope*) const;
|
||||
RelativePoint* getControlPoints (int& numPoints);
|
||||
ElementBase* clone() const;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue