| @@ -185,6 +185,10 @@ public: | |||||
| DrawablePath::ValueTreeWrapper wrapper (item.getState()); | DrawablePath::ValueTreeWrapper wrapper (item.getState()); | ||||
| props.add (new DrawablePathFillPropComp (item, "Fill", wrapper.getMainFillState())); | props.add (new DrawablePathFillPropComp (item, "Fill", wrapper.getMainFillState())); | ||||
| props.add (StrokeThicknessValueSource::create (wrapper, item.getDocument().getUndoManager())); | |||||
| props.add (StrokeJoinStyleValueSource::create (wrapper, item.getDocument().getUndoManager())); | |||||
| props.add (StrokeCapStyleValueSource::create (wrapper, item.getDocument().getUndoManager())); | |||||
| props.add (new DrawablePathFillPropComp (item, "Stroke", wrapper.getStrokeFillState())); | props.add (new DrawablePathFillPropComp (item, "Stroke", wrapper.getStrokeFillState())); | ||||
| } | } | ||||
| @@ -327,7 +331,7 @@ public: | |||||
| if (stroke.isGradient()) | if (stroke.isGradient()) | ||||
| { | { | ||||
| points.add (new GradientControlPoint (itemId + "/gs1", item.getState(), true, true)); | points.add (new GradientControlPoint (itemId + "/gs1", item.getState(), true, true)); | ||||
| points.add (new GradientControlPoint (itemId + "/gs1", item.getState(), false, true)); | |||||
| points.add (new GradientControlPoint (itemId + "/gs2", item.getState(), false, true)); | |||||
| } | } | ||||
| } | } | ||||
| @@ -412,6 +416,113 @@ public: | |||||
| getGradientControlPoints (wrapper, item, points, itemId); | getGradientControlPoints (wrapper, item, points, itemId); | ||||
| } | } | ||||
| //============================================================================== | |||||
| class StrokeValueSourceBase : public Value::ValueSource, | |||||
| public ValueTree::Listener | |||||
| { | |||||
| public: | |||||
| StrokeValueSourceBase (const DrawablePath::ValueTreeWrapper& wrapper_, UndoManager* undoManager_) | |||||
| : wrapper (wrapper_), undoManager (undoManager_) | |||||
| { | |||||
| wrapper.getState().addListener (this); | |||||
| } | |||||
| ~StrokeValueSourceBase() {} | |||||
| void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { sendChangeMessage (true); } | |||||
| void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) {} | |||||
| void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) {} | |||||
| protected: | |||||
| DrawablePath::ValueTreeWrapper wrapper; | |||||
| UndoManager* undoManager; | |||||
| }; | |||||
| //============================================================================== | |||||
| class StrokeThicknessValueSource : public StrokeValueSourceBase | |||||
| { | |||||
| public: | |||||
| StrokeThicknessValueSource (const DrawablePath::ValueTreeWrapper& wrapper_, UndoManager* undoManager_) | |||||
| : StrokeValueSourceBase (wrapper_, undoManager_) | |||||
| {} | |||||
| const var getValue() const | |||||
| { | |||||
| return wrapper.getStrokeType().getStrokeThickness(); | |||||
| } | |||||
| void setValue (const var& newValue) | |||||
| { | |||||
| PathStrokeType s (wrapper.getStrokeType()); | |||||
| s.setStrokeThickness (newValue); | |||||
| wrapper.setStrokeType (s, undoManager); | |||||
| } | |||||
| static PropertyComponent* create (const DrawablePath::ValueTreeWrapper& wrapper, UndoManager* undoManager) | |||||
| { | |||||
| return new SliderPropertyComponent (Value (new StrokeThicknessValueSource (wrapper, undoManager)), | |||||
| "Stroke Thickness", 0, 50.0, 0.1); | |||||
| } | |||||
| }; | |||||
| //============================================================================== | |||||
| class StrokeJoinStyleValueSource : public StrokeValueSourceBase | |||||
| { | |||||
| public: | |||||
| StrokeJoinStyleValueSource (const DrawablePath::ValueTreeWrapper& wrapper_, UndoManager* undoManager_) | |||||
| : StrokeValueSourceBase (wrapper_, undoManager_) | |||||
| {} | |||||
| const var getValue() const | |||||
| { | |||||
| return (int) wrapper.getStrokeType().getJointStyle(); | |||||
| } | |||||
| void setValue (const var& newValue) | |||||
| { | |||||
| PathStrokeType s (wrapper.getStrokeType()); | |||||
| s.setJointStyle ((PathStrokeType::JointStyle) (int) newValue); | |||||
| wrapper.setStrokeType (s, undoManager); | |||||
| } | |||||
| static PropertyComponent* create (const DrawablePath::ValueTreeWrapper& wrapper, UndoManager* undoManager) | |||||
| { | |||||
| const char* types[] = { "Miter", "Curved", "Bevel", 0 }; | |||||
| const int mappings[] = { PathStrokeType::mitered, PathStrokeType::curved, PathStrokeType::beveled }; | |||||
| return new ChoicePropertyComponent (Value (new StrokeJoinStyleValueSource (wrapper, undoManager)), | |||||
| "Joint Style", StringArray (types), Array<var> (mappings, numElementsInArray (mappings))); | |||||
| } | |||||
| }; | |||||
| //============================================================================== | |||||
| class StrokeCapStyleValueSource : public StrokeValueSourceBase | |||||
| { | |||||
| public: | |||||
| StrokeCapStyleValueSource (const DrawablePath::ValueTreeWrapper& wrapper_, UndoManager* undoManager_) | |||||
| : StrokeValueSourceBase (wrapper_, undoManager_) | |||||
| {} | |||||
| const var getValue() const | |||||
| { | |||||
| return (int) wrapper.getStrokeType().getEndStyle(); | |||||
| } | |||||
| void setValue (const var& newValue) | |||||
| { | |||||
| PathStrokeType s (wrapper.getStrokeType()); | |||||
| s.setEndStyle ((PathStrokeType::EndCapStyle) (int) newValue); | |||||
| wrapper.setStrokeType (s, undoManager); | |||||
| } | |||||
| static PropertyComponent* create (const DrawablePath::ValueTreeWrapper& wrapper, UndoManager* undoManager) | |||||
| { | |||||
| const char* types[] = { "Butt", "Square", "Round", 0 }; | |||||
| const int mappings[] = { PathStrokeType::butt, PathStrokeType::square, PathStrokeType::rounded }; | |||||
| return new ChoicePropertyComponent (Value (new StrokeCapStyleValueSource (wrapper, undoManager)), | |||||
| "Cap Style", StringArray (types), Array<var> (mappings, numElementsInArray (mappings))); | |||||
| } | |||||
| }; | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -713,6 +824,119 @@ public: | |||||
| }; | }; | ||||
| }; | }; | ||||
| //============================================================================== | |||||
| class DrawableTextHandler : public DrawableTypeHandler | |||||
| { | |||||
| public: | |||||
| DrawableTextHandler() : DrawableTypeHandler ("Text", DrawableText::valueTreeType) {} | |||||
| ~DrawableTextHandler() {} | |||||
| static const ValueTree createNewInstance (DrawableDocument& document, const Point<float>& approxPosition) | |||||
| { | |||||
| DrawableText dt; | |||||
| dt.setText ("Text"); | |||||
| dt.setBounds (RelativePoint (approxPosition), | |||||
| RelativePoint (approxPosition + Point<float> (100.0f, 0.0f)), | |||||
| RelativePoint (approxPosition + Point<float> (0.0f, 100.0f)), | |||||
| RelativePoint (approxPosition + Point<float> (25.0f, 25.0f))); | |||||
| dt.setFont (Font (25.0f), true); | |||||
| return dt.createValueTree (&document); | |||||
| } | |||||
| void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) | |||||
| { | |||||
| DrawableText::ValueTreeWrapper wrapper (item.getState()); | |||||
| //props.add (new ResetButtonPropertyComponent (item, wrapper)); | |||||
| } | |||||
| void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) | |||||
| { | |||||
| } | |||||
| //============================================================================== | |||||
| class TextControlPoint : public ControlPoint | |||||
| { | |||||
| public: | |||||
| TextControlPoint (const String& id_, const ValueTree& item_, const int cpNum_) | |||||
| : ControlPoint (id_), item (item_), cpNum (cpNum_) | |||||
| {} | |||||
| ~TextControlPoint() {} | |||||
| const RelativePoint getPosition() | |||||
| { | |||||
| DrawableText::ValueTreeWrapper wrapper (item); | |||||
| switch (cpNum) | |||||
| { | |||||
| case 0: return wrapper.getBoundingBoxTopLeft(); | |||||
| case 1: return wrapper.getBoundingBoxTopRight(); | |||||
| case 2: return wrapper.getBoundingBoxBottomLeft(); | |||||
| case 3: return wrapper.getFontSizeAndScaleAnchor(); | |||||
| default: jassertfalse; break; | |||||
| } | |||||
| return RelativePoint(); | |||||
| } | |||||
| void setPosition (const RelativePoint& newPoint, UndoManager* undoManager) | |||||
| { | |||||
| DrawableText::ValueTreeWrapper wrapper (item); | |||||
| switch (cpNum) | |||||
| { | |||||
| case 0: wrapper.setBoundingBoxTopLeft (newPoint, undoManager); break; | |||||
| case 1: wrapper.setBoundingBoxTopRight (newPoint, undoManager); break; | |||||
| case 2: wrapper.setBoundingBoxBottomLeft (newPoint, undoManager); break; | |||||
| case 3: wrapper.setFontSizeAndScaleAnchor (newPoint, undoManager); break; | |||||
| default: jassertfalse; break; | |||||
| } | |||||
| } | |||||
| const Value getPositionValue (UndoManager* undoManager) | |||||
| { | |||||
| DrawableText::ValueTreeWrapper wrapper (item); | |||||
| switch (cpNum) | |||||
| { | |||||
| case 0: return item.getPropertyAsValue (DrawableText::ValueTreeWrapper::topLeft, undoManager); | |||||
| case 1: return item.getPropertyAsValue (DrawableText::ValueTreeWrapper::topRight, undoManager); | |||||
| case 2: return item.getPropertyAsValue (DrawableText::ValueTreeWrapper::bottomLeft, undoManager); | |||||
| case 3: return item.getPropertyAsValue (DrawableText::ValueTreeWrapper::fontSizeAnchor, undoManager); | |||||
| default: jassertfalse; break; | |||||
| } | |||||
| return Value(); | |||||
| } | |||||
| bool hasLine() { return false; } | |||||
| RelativePoint getEndOfLine() { return RelativePoint(); } | |||||
| void createProperties (DrawableDocument& document, Array <PropertyComponent*>& props) | |||||
| { | |||||
| DrawableTypeInstance instance (document, item); | |||||
| props.add (new ControlPointPropertyComp (instance, this, "X", true, document.getUndoManager())); | |||||
| props.add (new ControlPointPropertyComp (instance, this, "Y", false, document.getUndoManager())); | |||||
| } | |||||
| private: | |||||
| ValueTree item; | |||||
| int cpNum; | |||||
| }; | |||||
| void getAllControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points) | |||||
| { | |||||
| const String itemIDRoot (item.getID() + "/"); | |||||
| for (int i = 0; i < 4; ++i) | |||||
| points.add (new TextControlPoint (itemIDRoot + String(i), item.getState(), i)); | |||||
| } | |||||
| void getVisibleControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points, const EditorCanvasBase::SelectedItems&) | |||||
| { | |||||
| return getAllControlPoints (item, points); | |||||
| } | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| DrawableTypeManager::DrawableTypeManager() | DrawableTypeManager::DrawableTypeManager() | ||||
| @@ -720,6 +944,7 @@ DrawableTypeManager::DrawableTypeManager() | |||||
| handlers.add (new DrawablePathHandler()); | handlers.add (new DrawablePathHandler()); | ||||
| handlers.add (new DrawableImageHandler()); | handlers.add (new DrawableImageHandler()); | ||||
| handlers.add (new DrawableCompositeHandler()); | handlers.add (new DrawableCompositeHandler()); | ||||
| handlers.add (new DrawableTextHandler()); | |||||
| } | } | ||||
| DrawableTypeManager::~DrawableTypeManager() | DrawableTypeManager::~DrawableTypeManager() | ||||
| @@ -738,7 +963,7 @@ DrawableTypeHandler* DrawableTypeManager::getHandlerFor (const Identifier& type) | |||||
| const StringArray DrawableTypeManager::getNewItemList() | const StringArray DrawableTypeManager::getNewItemList() | ||||
| { | { | ||||
| const char* types[] = { "New Triangle", "New Rectangle", "New Ellipse", "New Image", 0 }; | |||||
| const char* types[] = { "New Triangle", "New Rectangle", "New Ellipse", "New Image", "New Text Object", 0 }; | |||||
| return StringArray (types); | return StringArray (types); | ||||
| } | } | ||||
| @@ -750,6 +975,7 @@ const ValueTree DrawableTypeManager::createNewItem (const int index, DrawableDoc | |||||
| case 1: return DrawablePathHandler::createNewRectangle (document, approxPosition); | case 1: return DrawablePathHandler::createNewRectangle (document, approxPosition); | ||||
| case 2: return DrawablePathHandler::createNewEllipse (document, approxPosition); | case 2: return DrawablePathHandler::createNewEllipse (document, approxPosition); | ||||
| case 3: return DrawableImageHandler::createNewInstance (document, approxPosition); | case 3: return DrawableImageHandler::createNewInstance (document, approxPosition); | ||||
| case 4: return DrawableTextHandler::createNewInstance (document, approxPosition); | |||||
| default: jassertfalse; break; | default: jassertfalse; break; | ||||
| } | } | ||||
| @@ -83,7 +83,7 @@ MainWindow::MainWindow() | |||||
| // don't want the window to take focus when the title-bar is clicked.. | // don't want the window to take focus when the title-bar is clicked.. | ||||
| setWantsKeyboardFocus (false); | setWantsKeyboardFocus (false); | ||||
| getPeer()->setCurrentRenderingEngine (0); | |||||
| //getPeer()->setCurrentRenderingEngine (0); | |||||
| } | } | ||||
| MainWindow::~MainWindow() | MainWindow::~MainWindow() | ||||
| @@ -84706,25 +84706,12 @@ const Rectangle<float> DrawableImage::getBounds() const | |||||
| if (image.isNull()) | if (image.isNull()) | ||||
| return Rectangle<float>(); | return Rectangle<float>(); | ||||
| Point<float> resolved[3]; | |||||
| for (int i = 0; i < 3; ++i) | |||||
| resolved[i] = controlPoints[i].resolve (parent); | |||||
| const Point<float> bottomRight (resolved[1] + (resolved[2] - resolved[0])); | |||||
| float minX = bottomRight.getX(); | |||||
| float maxX = minX; | |||||
| float minY = bottomRight.getY(); | |||||
| float maxY = minY; | |||||
| Point<float> corners[4]; | |||||
| for (int i = 0; i < 3; ++i) | for (int i = 0; i < 3; ++i) | ||||
| { | |||||
| minX = jmin (minX, resolved[i].getX()); | |||||
| maxX = jmax (maxX, resolved[i].getX()); | |||||
| minY = jmin (minY, resolved[i].getY()); | |||||
| maxY = jmax (maxY, resolved[i].getY()); | |||||
| } | |||||
| corners[i] = controlPoints[i].resolve (parent); | |||||
| return Rectangle<float> (minX, minY, maxX - minX, maxY - minY); | |||||
| corners[3] = corners[1] + (corners[2] - corners[0]); | |||||
| return Rectangle<float>::findAreaContainingPoints (corners, 4); | |||||
| } | } | ||||
| bool DrawableImage::hitTest (float x, float y) const | bool DrawableImage::hitTest (float x, float y) const | ||||
| @@ -85264,6 +85251,7 @@ const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree | |||||
| { | { | ||||
| damageRect = getBounds(); | damageRect = getBounds(); | ||||
| path.swapWithPath (newPath); | path.swapWithPath (newPath); | ||||
| strokeNeedsUpdating = true; | |||||
| strokeType = newStroke; | strokeType = newStroke; | ||||
| needsRedraw = true; | needsRedraw = true; | ||||
| } | } | ||||
| @@ -85307,50 +85295,136 @@ END_JUCE_NAMESPACE | |||||
| BEGIN_JUCE_NAMESPACE | BEGIN_JUCE_NAMESPACE | ||||
| DrawableText::DrawableText() | DrawableText::DrawableText() | ||||
| : colour (Colours::white) | |||||
| : colour (Colours::black), | |||||
| justification (Justification::centredLeft) | |||||
| { | { | ||||
| setFont (Font (15.0f), true); | |||||
| } | } | ||||
| DrawableText::DrawableText (const DrawableText& other) | DrawableText::DrawableText (const DrawableText& other) | ||||
| : text (other.text), | : text (other.text), | ||||
| colour (other.colour) | |||||
| font (other.font), | |||||
| colour (other.colour), | |||||
| justification (other.justification) | |||||
| { | { | ||||
| for (int i = 0; i < numElementsInArray (controlPoints); ++i) | |||||
| controlPoints[i] = other.controlPoints[i]; | |||||
| } | } | ||||
| DrawableText::~DrawableText() | DrawableText::~DrawableText() | ||||
| { | { | ||||
| } | } | ||||
| void DrawableText::setText (const GlyphArrangement& newText) | |||||
| void DrawableText::setText (const String& newText) | |||||
| { | { | ||||
| text = newText; | text = newText; | ||||
| } | } | ||||
| void DrawableText::setText (const String& newText, const Font& fontToUse) | |||||
| void DrawableText::setColour (const Colour& newColour) | |||||
| { | { | ||||
| text.clear(); | |||||
| text.addLineOfText (fontToUse, newText, 0.0f, 0.0f); | |||||
| colour = newColour; | |||||
| } | } | ||||
| void DrawableText::setColour (const Colour& newColour) | |||||
| void DrawableText::setFont (const Font& newFont, bool applySizeAndScale) | |||||
| { | { | ||||
| colour = newColour; | |||||
| font = newFont; | |||||
| if (applySizeAndScale) | |||||
| { | |||||
| const Line<float> left (Point<float>(), controlPoints[2].resolve (getParent())); | |||||
| const Line<float> top (Point<float>(), controlPoints[1].resolve (getParent())); | |||||
| controlPoints[3] = RelativePoint (controlPoints[0].resolve (getParent()) | |||||
| + left.getPointAlongLine (font.getHeight()) | |||||
| + top.getPointAlongLine (font.getHorizontalScale() * font.getHeight())); | |||||
| } | |||||
| } | |||||
| void DrawableText::setJustification (const Justification& newJustification) | |||||
| { | |||||
| justification = newJustification; | |||||
| } | |||||
| void DrawableText::setBounds (const RelativePoint& boundingBoxTopLeft, | |||||
| const RelativePoint& boundingBoxTopRight, | |||||
| const RelativePoint& boundingBoxBottomLeft, | |||||
| const RelativePoint& fontSizeAndScaleAnchor) | |||||
| { | |||||
| controlPoints[0] = boundingBoxTopLeft; | |||||
| controlPoints[1] = boundingBoxTopRight; | |||||
| controlPoints[2] = boundingBoxBottomLeft; | |||||
| controlPoints[3] = fontSizeAndScaleAnchor; | |||||
| } | |||||
| static const Point<float> findNormalisedCoordWithinParallelogram (const Point<float>& origin, | |||||
| Point<float> topRight, | |||||
| Point<float> bottomLeft, | |||||
| Point<float> target) | |||||
| { | |||||
| topRight -= origin; | |||||
| bottomLeft -= origin; | |||||
| target -= origin; | |||||
| return Point<float> (Line<float> (Point<float>(), topRight).getIntersection (Line<float> (target, target - bottomLeft)).getDistanceFromOrigin(), | |||||
| Line<float> (Point<float>(), bottomLeft).getIntersection (Line<float> (target, target - topRight)).getDistanceFromOrigin()); | |||||
| } | } | ||||
| void DrawableText::render (const Drawable::RenderingContext& context) const | void DrawableText::render (const Drawable::RenderingContext& context) const | ||||
| { | { | ||||
| Point<float> points[4]; | |||||
| for (int i = 0; i < 4; ++i) | |||||
| points[i] = controlPoints[i].resolve (getParent()); | |||||
| const float w = Line<float> (points[0], points[1]).getLength(); | |||||
| const float h = Line<float> (points[0], points[2]).getLength(); | |||||
| const Point<float> fontCoords (findNormalisedCoordWithinParallelogram (points[0], points[1], points[2], points[3])); | |||||
| const float fontHeight = jlimit (1.0f, h, fontCoords.getY()); | |||||
| const float fontWidth = jlimit (0.01f, w, fontCoords.getX()); | |||||
| Font f (font); | |||||
| f.setHeight (fontHeight); | |||||
| f.setHorizontalScale (fontWidth / fontHeight); | |||||
| context.g.setColour (colour.withMultipliedAlpha (context.opacity)); | context.g.setColour (colour.withMultipliedAlpha (context.opacity)); | ||||
| text.draw (context.g, context.transform); | |||||
| GlyphArrangement ga; | |||||
| ga.addFittedText (f, text, 0, 0, w, h, justification, 0x100000); | |||||
| ga.draw (context.g, | |||||
| AffineTransform::fromTargetPoints (0, 0, points[0].getX(), points[0].getY(), | |||||
| w, 0, points[1].getX(), points[1].getY(), | |||||
| 0, h, points[2].getX(), points[2].getY()) | |||||
| .followedBy (context.transform)); | |||||
| } | |||||
| void DrawableText::resolveCorners (Point<float>* const corners) const | |||||
| { | |||||
| for (int i = 0; i < 3; ++i) | |||||
| corners[i] = controlPoints[i].resolve (parent); | |||||
| corners[3] = corners[1] + (corners[2] - corners[0]); | |||||
| } | } | ||||
| const Rectangle<float> DrawableText::getBounds() const | const Rectangle<float> DrawableText::getBounds() const | ||||
| { | { | ||||
| return text.getBoundingBox (0, -1, false); | |||||
| Point<float> corners[4]; | |||||
| resolveCorners (corners); | |||||
| return Rectangle<float>::findAreaContainingPoints (corners, 4); | |||||
| } | } | ||||
| bool DrawableText::hitTest (float x, float y) const | bool DrawableText::hitTest (float x, float y) const | ||||
| { | { | ||||
| return text.findGlyphIndexAt (x, y) >= 0; | |||||
| Point<float> corners[4]; | |||||
| resolveCorners (corners); | |||||
| Path p; | |||||
| p.startNewSubPath (corners[0].getX(), corners[0].getY()); | |||||
| p.lineTo (corners[1].getX(), corners[1].getY()); | |||||
| p.lineTo (corners[3].getX(), corners[3].getY()); | |||||
| p.lineTo (corners[2].getX(), corners[2].getY()); | |||||
| p.closeSubPath(); | |||||
| return p.contains (x, y); | |||||
| } | } | ||||
| Drawable* DrawableText::createCopy() const | Drawable* DrawableText::createCopy() const | ||||
| @@ -85365,6 +85439,13 @@ void DrawableText::invalidatePoints() | |||||
| const Identifier DrawableText::valueTreeType ("Text"); | const Identifier DrawableText::valueTreeType ("Text"); | ||||
| const Identifier DrawableText::ValueTreeWrapper::text ("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::fontSizeAnchor ("fontSizeAnchor"); | |||||
| DrawableText::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) | DrawableText::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) | ||||
| : ValueTreeWrapperBase (state_) | : ValueTreeWrapperBase (state_) | ||||
| @@ -85372,12 +85453,113 @@ DrawableText::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) | |||||
| jassert (state.hasType (valueTreeType)); | jassert (state.hasType (valueTreeType)); | ||||
| } | } | ||||
| const String DrawableText::ValueTreeWrapper::getText() const | |||||
| { | |||||
| return state [text].toString(); | |||||
| } | |||||
| void DrawableText::ValueTreeWrapper::setText (const String& newText, UndoManager* undoManager) | |||||
| { | |||||
| state.setProperty (text, newText, undoManager); | |||||
| } | |||||
| const Colour DrawableText::ValueTreeWrapper::getColour() const | |||||
| { | |||||
| return Colour::fromString (state [colour].toString()); | |||||
| } | |||||
| void DrawableText::ValueTreeWrapper::setColour (const Colour& newColour, UndoManager* undoManager) | |||||
| { | |||||
| state.setProperty (colour, newColour.toString(), undoManager); | |||||
| } | |||||
| const Justification DrawableText::ValueTreeWrapper::getJustification() const | |||||
| { | |||||
| return Justification ((int) state [justification]); | |||||
| } | |||||
| void DrawableText::ValueTreeWrapper::setJustification (const Justification& newJustification, UndoManager* undoManager) | |||||
| { | |||||
| state.setProperty (justification, newJustification.getFlags(), undoManager); | |||||
| } | |||||
| const 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); | |||||
| } | |||||
| const RelativePoint DrawableText::ValueTreeWrapper::getBoundingBoxTopLeft() const | |||||
| { | |||||
| return state [topLeft].toString(); | |||||
| } | |||||
| void DrawableText::ValueTreeWrapper::setBoundingBoxTopLeft (const RelativePoint& p, UndoManager* undoManager) | |||||
| { | |||||
| state.setProperty (topLeft, p.toString(), undoManager); | |||||
| } | |||||
| const RelativePoint DrawableText::ValueTreeWrapper::getBoundingBoxTopRight() const | |||||
| { | |||||
| return state [topRight].toString(); | |||||
| } | |||||
| void DrawableText::ValueTreeWrapper::setBoundingBoxTopRight (const RelativePoint& p, UndoManager* undoManager) | |||||
| { | |||||
| state.setProperty (topRight, p.toString(), undoManager); | |||||
| } | |||||
| const RelativePoint DrawableText::ValueTreeWrapper::getBoundingBoxBottomLeft() const | |||||
| { | |||||
| return state [bottomLeft].toString(); | |||||
| } | |||||
| void DrawableText::ValueTreeWrapper::setBoundingBoxBottomLeft (const RelativePoint& p, UndoManager* undoManager) | |||||
| { | |||||
| state.setProperty (bottomLeft, p.toString(), undoManager); | |||||
| } | |||||
| const RelativePoint DrawableText::ValueTreeWrapper::getFontSizeAndScaleAnchor() const | |||||
| { | |||||
| return state [fontSizeAnchor].toString(); | |||||
| } | |||||
| void DrawableText::ValueTreeWrapper::setFontSizeAndScaleAnchor (const RelativePoint& p, UndoManager* undoManager) | |||||
| { | |||||
| state.setProperty (fontSizeAnchor, p.toString(), undoManager); | |||||
| } | |||||
| const Rectangle<float> DrawableText::refreshFromValueTree (const ValueTree& tree, ImageProvider*) | const Rectangle<float> DrawableText::refreshFromValueTree (const ValueTree& tree, ImageProvider*) | ||||
| { | { | ||||
| ValueTreeWrapper v (tree); | ValueTreeWrapper v (tree); | ||||
| setName (v.getID()); | setName (v.getID()); | ||||
| jassertfalse; // xxx not finished! | |||||
| const RelativePoint p1 (v.getBoundingBoxTopLeft()), p2 (v.getBoundingBoxTopRight()), | |||||
| p3 (v.getBoundingBoxBottomLeft()), p4 (v.getFontSizeAndScaleAnchor()); | |||||
| 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 | |||||
| || p1 != controlPoints[0] || p2 != controlPoints[1] || p3 != controlPoints[2] || p4 != controlPoints[3]) | |||||
| { | |||||
| const Rectangle<float> damage (getBounds()); | |||||
| setBounds (p1, p2, p3, p4); | |||||
| setColour (newColour); | |||||
| setFont (newFont, false); | |||||
| setJustification (newJustification); | |||||
| setText (newText); | |||||
| return damage.getUnion (getBounds()); | |||||
| } | |||||
| return Rectangle<float>(); | return Rectangle<float>(); | ||||
| } | } | ||||
| @@ -85388,8 +85570,14 @@ const ValueTree DrawableText::createValueTree (ImageProvider*) const | |||||
| ValueTreeWrapper v (tree); | ValueTreeWrapper v (tree); | ||||
| v.setID (getName(), 0); | v.setID (getName(), 0); | ||||
| jassertfalse; // xxx not finished! | |||||
| v.setText (text, 0); | |||||
| v.setFont (font, 0); | |||||
| v.setJustification (justification, 0); | |||||
| v.setColour (colour, 0); | |||||
| v.setBoundingBoxTopLeft (controlPoints[0], 0); | |||||
| v.setBoundingBoxTopRight (controlPoints[1], 0); | |||||
| v.setBoundingBoxBottomLeft (controlPoints[2], 0); | |||||
| v.setFontSizeAndScaleAnchor (controlPoints[3], 0); | |||||
| return tree; | return tree; | ||||
| } | } | ||||
| @@ -88911,6 +89099,15 @@ const AffineTransform AffineTransform::fromTargetPoints (const float x00, const | |||||
| y10 - y00, y01 - y00, y00); | y10 - y00, y01 - y00, y00); | ||||
| } | } | ||||
| const AffineTransform AffineTransform::fromTargetPoints (const float sx1, const float sy1, const float tx1, const float ty1, | |||||
| const float sx2, const float sy2, const float tx2, const float ty2, | |||||
| const float sx3, const float sy3, const float tx3, const float ty3) throw() | |||||
| { | |||||
| return fromTargetPoints (sx1, sy1, sx2, sy2, sx3, sy3) | |||||
| .inverted() | |||||
| .followedBy (fromTargetPoints (tx1, ty1, tx2, ty2, tx3, ty3)); | |||||
| } | |||||
| bool AffineTransform::isOnlyTranslation() const throw() | bool AffineTransform::isOnlyTranslation() const throw() | ||||
| { | { | ||||
| return (mat01 == 0) | return (mat01 == 0) | ||||
| @@ -263809,7 +264006,8 @@ public: | |||||
| : context (context_), | : context (context_), | ||||
| flipHeight (flipHeight_), | flipHeight (flipHeight_), | ||||
| state (new SavedState()), | state (new SavedState()), | ||||
| numGradientLookupEntries (0) | |||||
| numGradientLookupEntries (0), | |||||
| lastClipRectIsValid (false) | |||||
| { | { | ||||
| CGContextRetain (context); | CGContextRetain (context); | ||||
| CGContextSaveGState(context); | CGContextSaveGState(context); | ||||
| @@ -263837,11 +264035,21 @@ public: | |||||
| void setOrigin (int x, int y) | void setOrigin (int x, int y) | ||||
| { | { | ||||
| CGContextTranslateCTM (context, x, -y); | CGContextTranslateCTM (context, x, -y); | ||||
| if (lastClipRectIsValid) | |||||
| lastClipRect.translate (-x, -y); | |||||
| } | } | ||||
| bool clipToRectangle (const Rectangle<int>& r) | bool clipToRectangle (const Rectangle<int>& r) | ||||
| { | { | ||||
| CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); | CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); | ||||
| if (lastClipRectIsValid) | |||||
| { | |||||
| lastClipRect = lastClipRect.getIntersection (r); | |||||
| return ! r.isEmpty(); | |||||
| } | |||||
| return ! isClipEmpty(); | return ! isClipEmpty(); | ||||
| } | } | ||||
| @@ -263850,6 +264058,8 @@ public: | |||||
| if (clipRegion.isEmpty()) | if (clipRegion.isEmpty()) | ||||
| { | { | ||||
| CGContextClipToRect (context, CGRectMake (0, 0, 0, 0)); | CGContextClipToRect (context, CGRectMake (0, 0, 0, 0)); | ||||
| lastClipRectIsValid = true; | |||||
| lastClipRect = Rectangle<int>(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| else | else | ||||
| @@ -263864,6 +264074,7 @@ public: | |||||
| } | } | ||||
| CGContextClipToRects (context, rects, numRects); | CGContextClipToRects (context, rects, numRects); | ||||
| lastClipRectIsValid = false; | |||||
| return ! isClipEmpty(); | return ! isClipEmpty(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -263873,12 +264084,14 @@ public: | |||||
| RectangleList remaining (getClipBounds()); | RectangleList remaining (getClipBounds()); | ||||
| remaining.subtract (r); | remaining.subtract (r); | ||||
| clipToRectangleList (remaining); | clipToRectangleList (remaining); | ||||
| lastClipRectIsValid = false; | |||||
| } | } | ||||
| void clipToPath (const Path& path, const AffineTransform& transform) | void clipToPath (const Path& path, const AffineTransform& transform) | ||||
| { | { | ||||
| createPath (path, transform); | createPath (path, transform); | ||||
| CGContextClip (context); | CGContextClip (context); | ||||
| lastClipRectIsValid = false; | |||||
| } | } | ||||
| void clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform) | void clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform) | ||||
| @@ -263903,6 +264116,7 @@ public: | |||||
| flip(); | flip(); | ||||
| CGImageRelease (image); | CGImageRelease (image); | ||||
| lastClipRectIsValid = false; | |||||
| } | } | ||||
| } | } | ||||
| @@ -263913,17 +264127,23 @@ public: | |||||
| const Rectangle<int> getClipBounds() const | const Rectangle<int> getClipBounds() const | ||||
| { | { | ||||
| CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context)); | |||||
| if (! lastClipRectIsValid) | |||||
| { | |||||
| CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context)); | |||||
| lastClipRectIsValid = true; | |||||
| lastClipRect.setBounds (roundToInt (bounds.origin.x), | |||||
| roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)), | |||||
| roundToInt (bounds.size.width), | |||||
| roundToInt (bounds.size.height)); | |||||
| } | |||||
| return Rectangle<int> (roundToInt (bounds.origin.x), | |||||
| roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)), | |||||
| roundToInt (bounds.size.width), | |||||
| roundToInt (bounds.size.height)); | |||||
| return lastClipRect; | |||||
| } | } | ||||
| bool isClipEmpty() const | bool isClipEmpty() const | ||||
| { | { | ||||
| return CGRectIsEmpty (CGContextGetClipBoundingBox (context)); | |||||
| return getClipBounds().isEmpty(); | |||||
| } | } | ||||
| void saveState() | void saveState() | ||||
| @@ -263942,6 +264162,7 @@ public: | |||||
| { | { | ||||
| state = top; | state = top; | ||||
| stateStack.removeLast (1, false); | stateStack.removeLast (1, false); | ||||
| lastClipRectIsValid = false; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -264223,6 +264444,8 @@ private: | |||||
| const CGFloat flipHeight; | const CGFloat flipHeight; | ||||
| CGColorSpaceRef rgbColourSpace, greyColourSpace; | CGColorSpaceRef rgbColourSpace, greyColourSpace; | ||||
| CGFunctionCallbacks gradientCallbacks; | CGFunctionCallbacks gradientCallbacks; | ||||
| mutable Rectangle<int> lastClipRect; | |||||
| mutable bool lastClipRectIsValid; | |||||
| struct SavedState | struct SavedState | ||||
| { | { | ||||
| @@ -268422,7 +268645,8 @@ public: | |||||
| : context (context_), | : context (context_), | ||||
| flipHeight (flipHeight_), | flipHeight (flipHeight_), | ||||
| state (new SavedState()), | state (new SavedState()), | ||||
| numGradientLookupEntries (0) | |||||
| numGradientLookupEntries (0), | |||||
| lastClipRectIsValid (false) | |||||
| { | { | ||||
| CGContextRetain (context); | CGContextRetain (context); | ||||
| CGContextSaveGState(context); | CGContextSaveGState(context); | ||||
| @@ -268450,11 +268674,21 @@ public: | |||||
| void setOrigin (int x, int y) | void setOrigin (int x, int y) | ||||
| { | { | ||||
| CGContextTranslateCTM (context, x, -y); | CGContextTranslateCTM (context, x, -y); | ||||
| if (lastClipRectIsValid) | |||||
| lastClipRect.translate (-x, -y); | |||||
| } | } | ||||
| bool clipToRectangle (const Rectangle<int>& r) | bool clipToRectangle (const Rectangle<int>& r) | ||||
| { | { | ||||
| CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); | CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); | ||||
| if (lastClipRectIsValid) | |||||
| { | |||||
| lastClipRect = lastClipRect.getIntersection (r); | |||||
| return ! r.isEmpty(); | |||||
| } | |||||
| return ! isClipEmpty(); | return ! isClipEmpty(); | ||||
| } | } | ||||
| @@ -268463,6 +268697,8 @@ public: | |||||
| if (clipRegion.isEmpty()) | if (clipRegion.isEmpty()) | ||||
| { | { | ||||
| CGContextClipToRect (context, CGRectMake (0, 0, 0, 0)); | CGContextClipToRect (context, CGRectMake (0, 0, 0, 0)); | ||||
| lastClipRectIsValid = true; | |||||
| lastClipRect = Rectangle<int>(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| else | else | ||||
| @@ -268477,6 +268713,7 @@ public: | |||||
| } | } | ||||
| CGContextClipToRects (context, rects, numRects); | CGContextClipToRects (context, rects, numRects); | ||||
| lastClipRectIsValid = false; | |||||
| return ! isClipEmpty(); | return ! isClipEmpty(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -268486,12 +268723,14 @@ public: | |||||
| RectangleList remaining (getClipBounds()); | RectangleList remaining (getClipBounds()); | ||||
| remaining.subtract (r); | remaining.subtract (r); | ||||
| clipToRectangleList (remaining); | clipToRectangleList (remaining); | ||||
| lastClipRectIsValid = false; | |||||
| } | } | ||||
| void clipToPath (const Path& path, const AffineTransform& transform) | void clipToPath (const Path& path, const AffineTransform& transform) | ||||
| { | { | ||||
| createPath (path, transform); | createPath (path, transform); | ||||
| CGContextClip (context); | CGContextClip (context); | ||||
| lastClipRectIsValid = false; | |||||
| } | } | ||||
| void clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform) | void clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform) | ||||
| @@ -268516,6 +268755,7 @@ public: | |||||
| flip(); | flip(); | ||||
| CGImageRelease (image); | CGImageRelease (image); | ||||
| lastClipRectIsValid = false; | |||||
| } | } | ||||
| } | } | ||||
| @@ -268526,17 +268766,23 @@ public: | |||||
| const Rectangle<int> getClipBounds() const | const Rectangle<int> getClipBounds() const | ||||
| { | { | ||||
| CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context)); | |||||
| if (! lastClipRectIsValid) | |||||
| { | |||||
| CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context)); | |||||
| lastClipRectIsValid = true; | |||||
| lastClipRect.setBounds (roundToInt (bounds.origin.x), | |||||
| roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)), | |||||
| roundToInt (bounds.size.width), | |||||
| roundToInt (bounds.size.height)); | |||||
| } | |||||
| return Rectangle<int> (roundToInt (bounds.origin.x), | |||||
| roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)), | |||||
| roundToInt (bounds.size.width), | |||||
| roundToInt (bounds.size.height)); | |||||
| return lastClipRect; | |||||
| } | } | ||||
| bool isClipEmpty() const | bool isClipEmpty() const | ||||
| { | { | ||||
| return CGRectIsEmpty (CGContextGetClipBoundingBox (context)); | |||||
| return getClipBounds().isEmpty(); | |||||
| } | } | ||||
| void saveState() | void saveState() | ||||
| @@ -268555,6 +268801,7 @@ public: | |||||
| { | { | ||||
| state = top; | state = top; | ||||
| stateStack.removeLast (1, false); | stateStack.removeLast (1, false); | ||||
| lastClipRectIsValid = false; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -268836,6 +269083,8 @@ private: | |||||
| const CGFloat flipHeight; | const CGFloat flipHeight; | ||||
| CGColorSpaceRef rgbColourSpace, greyColourSpace; | CGColorSpaceRef rgbColourSpace, greyColourSpace; | ||||
| CGFunctionCallbacks gradientCallbacks; | CGFunctionCallbacks gradientCallbacks; | ||||
| mutable Rectangle<int> lastClipRect; | |||||
| mutable bool lastClipRectIsValid; | |||||
| struct SavedState | struct SavedState | ||||
| { | { | ||||
| @@ -18946,6 +18946,12 @@ public: | |||||
| float x10, float y10, | float x10, float y10, | ||||
| float x01, float y01) throw(); | float x01, float y01) throw(); | ||||
| /** Returns the transform that will map three specified points onto three target points. | |||||
| */ | |||||
| static const AffineTransform fromTargetPoints (float sourceX1, float sourceY1, float targetX1, float targetY1, | |||||
| float sourceX2, float sourceY2, float targetX2, float targetY2, | |||||
| float sourceX3, float sourceY3, float targetX3, float targetY3) throw(); | |||||
| /** Returns the result of concatenating another transformation after this one. */ | /** Returns the result of concatenating another transformation after this one. */ | ||||
| const AffineTransform followedBy (const AffineTransform& other) const throw(); | const AffineTransform followedBy (const AffineTransform& other) const throw(); | ||||
| @@ -19981,6 +19987,18 @@ public: | |||||
| return findIntersection (start, end, line.start, line.end, intersection); | return findIntersection (start, end, line.start, line.end, intersection); | ||||
| } | } | ||||
| /** Finds the intersection between two lines. | |||||
| @param line the line to intersect with | |||||
| @returns the point at which the lines intersect, even if this lies beyond the end of the lines | |||||
| */ | |||||
| const Point<ValueType> getIntersection (const Line& line) const throw() | |||||
| { | |||||
| Point<ValueType> p; | |||||
| findIntersection (start, end, line.start, line.end, p); | |||||
| return p; | |||||
| } | |||||
| /** Returns the location of the point which is a given distance along this line. | /** Returns the location of the point which is a given distance along this line. | ||||
| @param distanceFromStart the distance to move along the line from its | @param distanceFromStart the distance to move along the line from its | ||||
| @@ -20660,6 +20678,28 @@ public: | |||||
| return Rectangle<int> (x1, y1, x2 - x1, y2 - y1); | return Rectangle<int> (x1, y1, x2 - x1, y2 - y1); | ||||
| } | } | ||||
| /** Returns the smallest Rectangle that can contain a set of points. */ | |||||
| static const Rectangle findAreaContainingPoints (const Point<ValueType>* const points, const int numPoints) throw() | |||||
| { | |||||
| if (numPoints == 0) | |||||
| return Rectangle(); | |||||
| ValueType minX (points[0].getX()); | |||||
| ValueType maxX (minX); | |||||
| ValueType minY (points[0].getY()); | |||||
| ValueType maxY (minY); | |||||
| for (int i = 1; i < numPoints; ++i) | |||||
| { | |||||
| minX = jmin (minX, points[i].getX()); | |||||
| maxX = jmax (maxX, points[i].getX()); | |||||
| minY = jmin (minY, points[i].getY()); | |||||
| maxY = jmax (maxY, points[i].getY()); | |||||
| } | |||||
| return Rectangle (minX, minY, maxX - minX, maxY - minY); | |||||
| } | |||||
| /** Casts this rectangle to a Rectangle<float>. | /** Casts this rectangle to a Rectangle<float>. | ||||
| Obviously this is mainly useful for rectangles that use integer types. | Obviously this is mainly useful for rectangles that use integer types. | ||||
| @see getSmallestIntegerContainer | @see getSmallestIntegerContainer | ||||
| @@ -22147,12 +22187,21 @@ public: | |||||
| /** Returns the stroke thickness. */ | /** Returns the stroke thickness. */ | ||||
| float getStrokeThickness() const throw() { return thickness; } | float getStrokeThickness() const throw() { return thickness; } | ||||
| /** Sets the stroke thickness. */ | |||||
| void setStrokeThickness (float newThickness) throw() { thickness = newThickness; } | |||||
| /** Returns the joint style. */ | /** Returns the joint style. */ | ||||
| JointStyle getJointStyle() const throw() { return jointStyle; } | JointStyle getJointStyle() const throw() { return jointStyle; } | ||||
| /** Sets the joint style. */ | |||||
| void setJointStyle (JointStyle newStyle) throw() { jointStyle = newStyle; } | |||||
| /** Returns the end-cap style. */ | /** Returns the end-cap style. */ | ||||
| EndCapStyle getEndStyle() const throw() { return endStyle; } | EndCapStyle getEndStyle() const throw() { return endStyle; } | ||||
| /** Sets the end-cap style. */ | |||||
| void setEndStyle (EndCapStyle newStyle) throw() { endStyle = newStyle; } | |||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| /** Compares the stroke thickness, joint and end styles of two stroke types. */ | /** Compares the stroke thickness, joint and end styles of two stroke types. */ | ||||
| @@ -31096,9 +31145,16 @@ public: | |||||
| */ | */ | ||||
| AudioSource* getCurrentSource() const throw() { return source; } | AudioSource* getCurrentSource() const throw() { return source; } | ||||
| /** Sets a gain to apply to the audio data. */ | |||||
| /** Sets a gain to apply to the audio data. | |||||
| @see getGain | |||||
| */ | |||||
| void setGain (const float newGain) throw(); | void setGain (const float newGain) throw(); | ||||
| /** Returns the current gain. | |||||
| @see setGain | |||||
| */ | |||||
| float getGain() const throw() { return gain; } | |||||
| /** Implementation of the AudioIODeviceCallback method. */ | /** Implementation of the AudioIODeviceCallback method. */ | ||||
| void audioDeviceIOCallback (const float** inputChannelData, | void audioDeviceIOCallback (const float** inputChannelData, | ||||
| int totalNumInputChannels, | int totalNumInputChannels, | ||||
| @@ -58788,19 +58844,8 @@ public: | |||||
| /** Destructor. */ | /** Destructor. */ | ||||
| virtual ~DrawableText(); | virtual ~DrawableText(); | ||||
| /** Sets the block of text to render */ | |||||
| void setText (const GlyphArrangement& newText); | |||||
| /** Sets a single line of text to render. | |||||
| This is a convenient method of adding a single line - for | |||||
| more complex text, use the setText() that takes a | |||||
| GlyphArrangement instead. | |||||
| */ | |||||
| void setText (const String& newText, const Font& fontToUse); | |||||
| /** Returns the text arrangement that was set with setText(). */ | |||||
| const GlyphArrangement& getText() const throw() { return text; } | |||||
| /** Sets the text to display.*/ | |||||
| void setText (const String& newText); | |||||
| /** Sets the colour of the text. */ | /** Sets the colour of the text. */ | ||||
| void setColour (const Colour& newColour); | void setColour (const Colour& newColour); | ||||
| @@ -58808,6 +58853,37 @@ public: | |||||
| /** Returns the current text colour. */ | /** Returns the current text colour. */ | ||||
| const Colour& getColour() const throw() { return colour; } | const Colour& getColour() const throw() { return colour; } | ||||
| /** Sets the font to use. | |||||
| Note that the font height and horizontal scale are actually based upon the position | |||||
| of the fontSizeAndScaleAnchor parameter to setBounds(). If applySizeAndScale is true, then | |||||
| the height and scale control point will be moved to match the dimensions of the font supplied; | |||||
| if it is false, then the new font's height and scale are ignored. | |||||
| */ | |||||
| void setFont (const Font& newFont, bool applySizeAndScale); | |||||
| /** Changes the justification of the text within the bounding box. */ | |||||
| void setJustification (const Justification& newJustification); | |||||
| /** Sets the bounding box and the control point that controls the font size. | |||||
| The three bounding box points define the parallelogram within which the text will be | |||||
| placed. The fontSizeAndScaleAnchor specifies a position within that parallelogram, whose | |||||
| Y position (relative to the parallelogram's origin and possibly distorted shape) specifies | |||||
| the font's height, and its X defines the font's horizontal scale. | |||||
| */ | |||||
| void setBounds (const RelativePoint& boundingBoxTopLeft, | |||||
| const RelativePoint& boundingBoxTopRight, | |||||
| const RelativePoint& boundingBoxBottomLeft, | |||||
| const RelativePoint& fontSizeAndScaleAnchor); | |||||
| /** Returns the origin of the text bounding box. */ | |||||
| const RelativePoint& getBoundingBoxTopLeft() const throw() { return controlPoints[0]; } | |||||
| /** Returns the top-right of the text bounding box. */ | |||||
| const RelativePoint& getBoundingBoxTopRight() const throw() { return controlPoints[1]; } | |||||
| /** Returns the bottom-left of the text bounding box. */ | |||||
| const RelativePoint& getBoundingBoxBottomLeft() const throw() { return controlPoints[2]; } | |||||
| /** Returns the point within the text bounding box which defines the size and scale of the font. */ | |||||
| const RelativePoint& getFontSizeAndScaleAnchor() const throw() { return controlPoints[3]; } | |||||
| /** @internal */ | /** @internal */ | ||||
| void render (const Drawable::RenderingContext& context) const; | void render (const Drawable::RenderingContext& context) const; | ||||
| /** @internal */ | /** @internal */ | ||||
| @@ -58833,17 +58909,43 @@ public: | |||||
| public: | public: | ||||
| ValueTreeWrapper (const ValueTree& state); | ValueTreeWrapper (const ValueTree& state); | ||||
| //xxx todo | |||||
| const String getText() const; | |||||
| void setText (const String& newText, UndoManager* undoManager); | |||||
| private: | |||||
| static const Identifier text; | |||||
| const Colour getColour() const; | |||||
| void setColour (const Colour& newColour, UndoManager* undoManager); | |||||
| const Justification getJustification() const; | |||||
| void setJustification (const Justification& newJustification, UndoManager* undoManager); | |||||
| const Font getFont() const; | |||||
| void setFont (const Font& newFont, UndoManager* undoManager); | |||||
| const RelativePoint getBoundingBoxTopLeft() const; | |||||
| void setBoundingBoxTopLeft (const RelativePoint& p, UndoManager* undoManager); | |||||
| const RelativePoint getBoundingBoxTopRight() const; | |||||
| void setBoundingBoxTopRight (const RelativePoint& p, UndoManager* undoManager); | |||||
| const RelativePoint getBoundingBoxBottomLeft() const; | |||||
| void setBoundingBoxBottomLeft (const RelativePoint& p, UndoManager* undoManager); | |||||
| const RelativePoint getFontSizeAndScaleAnchor() const; | |||||
| void setFontSizeAndScaleAnchor (const RelativePoint& p, UndoManager* undoManager); | |||||
| static const Identifier text, colour, font, justification, topLeft, topRight, bottomLeft, fontSizeAnchor; | |||||
| }; | }; | ||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| private: | private: | ||||
| GlyphArrangement text; | |||||
| RelativePoint controlPoints[4]; | |||||
| Font font; | |||||
| String text; | |||||
| Colour colour; | Colour colour; | ||||
| Justification justification; | |||||
| void resolveCorners (Point<float>* corners) const; | |||||
| DrawableText& operator= (const DrawableText&); | DrawableText& operator= (const DrawableText&); | ||||
| }; | }; | ||||
| @@ -74,9 +74,16 @@ public: | |||||
| */ | */ | ||||
| AudioSource* getCurrentSource() const throw() { return source; } | AudioSource* getCurrentSource() const throw() { return source; } | ||||
| /** Sets a gain to apply to the audio data. */ | |||||
| /** Sets a gain to apply to the audio data. | |||||
| @see getGain | |||||
| */ | |||||
| void setGain (const float newGain) throw(); | void setGain (const float newGain) throw(); | ||||
| /** Returns the current gain. | |||||
| @see setGain | |||||
| */ | |||||
| float getGain() const throw() { return gain; } | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Implementation of the AudioIODeviceCallback method. */ | /** Implementation of the AudioIODeviceCallback method. */ | ||||
| void audioDeviceIOCallback (const float** inputChannelData, | void audioDeviceIOCallback (const float** inputChannelData, | ||||
| @@ -130,25 +130,12 @@ const Rectangle<float> DrawableImage::getBounds() const | |||||
| if (image.isNull()) | if (image.isNull()) | ||||
| return Rectangle<float>(); | return Rectangle<float>(); | ||||
| Point<float> resolved[3]; | |||||
| Point<float> corners[4]; | |||||
| for (int i = 0; i < 3; ++i) | for (int i = 0; i < 3; ++i) | ||||
| resolved[i] = controlPoints[i].resolve (parent); | |||||
| const Point<float> bottomRight (resolved[1] + (resolved[2] - resolved[0])); | |||||
| float minX = bottomRight.getX(); | |||||
| float maxX = minX; | |||||
| float minY = bottomRight.getY(); | |||||
| float maxY = minY; | |||||
| for (int i = 0; i < 3; ++i) | |||||
| { | |||||
| minX = jmin (minX, resolved[i].getX()); | |||||
| maxX = jmax (maxX, resolved[i].getX()); | |||||
| minY = jmin (minY, resolved[i].getY()); | |||||
| maxY = jmax (maxY, resolved[i].getY()); | |||||
| } | |||||
| corners[i] = controlPoints[i].resolve (parent); | |||||
| return Rectangle<float> (minX, minY, maxX - minX, maxY - minY); | |||||
| corners[3] = corners[1] + (corners[2] - corners[0]); | |||||
| return Rectangle<float>::findAreaContainingPoints (corners, 4); | |||||
| } | } | ||||
| bool DrawableImage::hitTest (float x, float y) const | bool DrawableImage::hitTest (float x, float y) const | ||||
| @@ -382,6 +382,7 @@ const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree | |||||
| { | { | ||||
| damageRect = getBounds(); | damageRect = getBounds(); | ||||
| path.swapWithPath (newPath); | path.swapWithPath (newPath); | ||||
| strokeNeedsUpdating = true; | |||||
| strokeType = newStroke; | strokeType = newStroke; | ||||
| needsRedraw = true; | needsRedraw = true; | ||||
| } | } | ||||
| @@ -28,18 +28,25 @@ | |||||
| BEGIN_JUCE_NAMESPACE | BEGIN_JUCE_NAMESPACE | ||||
| #include "juce_DrawableText.h" | #include "juce_DrawableText.h" | ||||
| #include "juce_DrawableComposite.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| DrawableText::DrawableText() | DrawableText::DrawableText() | ||||
| : colour (Colours::white) | |||||
| : colour (Colours::black), | |||||
| justification (Justification::centredLeft) | |||||
| { | { | ||||
| setFont (Font (15.0f), true); | |||||
| } | } | ||||
| DrawableText::DrawableText (const DrawableText& other) | DrawableText::DrawableText (const DrawableText& other) | ||||
| : text (other.text), | : text (other.text), | ||||
| colour (other.colour) | |||||
| font (other.font), | |||||
| colour (other.colour), | |||||
| justification (other.justification) | |||||
| { | { | ||||
| for (int i = 0; i < numElementsInArray (controlPoints); ++i) | |||||
| controlPoints[i] = other.controlPoints[i]; | |||||
| } | } | ||||
| DrawableText::~DrawableText() | DrawableText::~DrawableText() | ||||
| @@ -47,37 +54,117 @@ DrawableText::~DrawableText() | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void DrawableText::setText (const GlyphArrangement& newText) | |||||
| void DrawableText::setText (const String& newText) | |||||
| { | { | ||||
| text = newText; | text = newText; | ||||
| } | } | ||||
| void DrawableText::setText (const String& newText, const Font& fontToUse) | |||||
| void DrawableText::setColour (const Colour& newColour) | |||||
| { | { | ||||
| text.clear(); | |||||
| text.addLineOfText (fontToUse, newText, 0.0f, 0.0f); | |||||
| colour = newColour; | |||||
| } | } | ||||
| void DrawableText::setColour (const Colour& newColour) | |||||
| void DrawableText::setFont (const Font& newFont, bool applySizeAndScale) | |||||
| { | { | ||||
| colour = newColour; | |||||
| font = newFont; | |||||
| if (applySizeAndScale) | |||||
| { | |||||
| const Line<float> left (Point<float>(), controlPoints[2].resolve (getParent())); | |||||
| const Line<float> top (Point<float>(), controlPoints[1].resolve (getParent())); | |||||
| controlPoints[3] = RelativePoint (controlPoints[0].resolve (getParent()) | |||||
| + left.getPointAlongLine (font.getHeight()) | |||||
| + top.getPointAlongLine (font.getHorizontalScale() * font.getHeight())); | |||||
| } | |||||
| } | |||||
| void DrawableText::setJustification (const Justification& newJustification) | |||||
| { | |||||
| justification = newJustification; | |||||
| } | |||||
| void DrawableText::setBounds (const RelativePoint& boundingBoxTopLeft, | |||||
| const RelativePoint& boundingBoxTopRight, | |||||
| const RelativePoint& boundingBoxBottomLeft, | |||||
| const RelativePoint& fontSizeAndScaleAnchor) | |||||
| { | |||||
| controlPoints[0] = boundingBoxTopLeft; | |||||
| controlPoints[1] = boundingBoxTopRight; | |||||
| controlPoints[2] = boundingBoxBottomLeft; | |||||
| controlPoints[3] = fontSizeAndScaleAnchor; | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| static const Point<float> findNormalisedCoordWithinParallelogram (const Point<float>& origin, | |||||
| Point<float> topRight, | |||||
| Point<float> bottomLeft, | |||||
| Point<float> target) | |||||
| { | |||||
| topRight -= origin; | |||||
| bottomLeft -= origin; | |||||
| target -= origin; | |||||
| return Point<float> (Line<float> (Point<float>(), topRight).getIntersection (Line<float> (target, target - bottomLeft)).getDistanceFromOrigin(), | |||||
| Line<float> (Point<float>(), bottomLeft).getIntersection (Line<float> (target, target - topRight)).getDistanceFromOrigin()); | |||||
| } | |||||
| void DrawableText::render (const Drawable::RenderingContext& context) const | void DrawableText::render (const Drawable::RenderingContext& context) const | ||||
| { | { | ||||
| Point<float> points[4]; | |||||
| for (int i = 0; i < 4; ++i) | |||||
| points[i] = controlPoints[i].resolve (getParent()); | |||||
| const float w = Line<float> (points[0], points[1]).getLength(); | |||||
| const float h = Line<float> (points[0], points[2]).getLength(); | |||||
| const Point<float> fontCoords (findNormalisedCoordWithinParallelogram (points[0], points[1], points[2], points[3])); | |||||
| const float fontHeight = jlimit (1.0f, h, fontCoords.getY()); | |||||
| const float fontWidth = jlimit (0.01f, w, fontCoords.getX()); | |||||
| Font f (font); | |||||
| f.setHeight (fontHeight); | |||||
| f.setHorizontalScale (fontWidth / fontHeight); | |||||
| context.g.setColour (colour.withMultipliedAlpha (context.opacity)); | context.g.setColour (colour.withMultipliedAlpha (context.opacity)); | ||||
| text.draw (context.g, context.transform); | |||||
| GlyphArrangement ga; | |||||
| ga.addFittedText (f, text, 0, 0, w, h, justification, 0x100000); | |||||
| ga.draw (context.g, | |||||
| AffineTransform::fromTargetPoints (0, 0, points[0].getX(), points[0].getY(), | |||||
| w, 0, points[1].getX(), points[1].getY(), | |||||
| 0, h, points[2].getX(), points[2].getY()) | |||||
| .followedBy (context.transform)); | |||||
| } | |||||
| void DrawableText::resolveCorners (Point<float>* const corners) const | |||||
| { | |||||
| for (int i = 0; i < 3; ++i) | |||||
| corners[i] = controlPoints[i].resolve (parent); | |||||
| corners[3] = corners[1] + (corners[2] - corners[0]); | |||||
| } | } | ||||
| const Rectangle<float> DrawableText::getBounds() const | const Rectangle<float> DrawableText::getBounds() const | ||||
| { | { | ||||
| return text.getBoundingBox (0, -1, false); | |||||
| Point<float> corners[4]; | |||||
| resolveCorners (corners); | |||||
| return Rectangle<float>::findAreaContainingPoints (corners, 4); | |||||
| } | } | ||||
| bool DrawableText::hitTest (float x, float y) const | bool DrawableText::hitTest (float x, float y) const | ||||
| { | { | ||||
| return text.findGlyphIndexAt (x, y) >= 0; | |||||
| Point<float> corners[4]; | |||||
| resolveCorners (corners); | |||||
| Path p; | |||||
| p.startNewSubPath (corners[0].getX(), corners[0].getY()); | |||||
| p.lineTo (corners[1].getX(), corners[1].getY()); | |||||
| p.lineTo (corners[3].getX(), corners[3].getY()); | |||||
| p.lineTo (corners[2].getX(), corners[2].getY()); | |||||
| p.closeSubPath(); | |||||
| return p.contains (x, y); | |||||
| } | } | ||||
| Drawable* DrawableText::createCopy() const | Drawable* DrawableText::createCopy() const | ||||
| @@ -93,19 +180,128 @@ void DrawableText::invalidatePoints() | |||||
| const Identifier DrawableText::valueTreeType ("Text"); | const Identifier DrawableText::valueTreeType ("Text"); | ||||
| const Identifier DrawableText::ValueTreeWrapper::text ("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::fontSizeAnchor ("fontSizeAnchor"); | |||||
| //============================================================================== | |||||
| DrawableText::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) | DrawableText::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) | ||||
| : ValueTreeWrapperBase (state_) | : ValueTreeWrapperBase (state_) | ||||
| { | { | ||||
| jassert (state.hasType (valueTreeType)); | jassert (state.hasType (valueTreeType)); | ||||
| } | } | ||||
| const String DrawableText::ValueTreeWrapper::getText() const | |||||
| { | |||||
| return state [text].toString(); | |||||
| } | |||||
| void DrawableText::ValueTreeWrapper::setText (const String& newText, UndoManager* undoManager) | |||||
| { | |||||
| state.setProperty (text, newText, undoManager); | |||||
| } | |||||
| const Colour DrawableText::ValueTreeWrapper::getColour() const | |||||
| { | |||||
| return Colour::fromString (state [colour].toString()); | |||||
| } | |||||
| void DrawableText::ValueTreeWrapper::setColour (const Colour& newColour, UndoManager* undoManager) | |||||
| { | |||||
| state.setProperty (colour, newColour.toString(), undoManager); | |||||
| } | |||||
| const Justification DrawableText::ValueTreeWrapper::getJustification() const | |||||
| { | |||||
| return Justification ((int) state [justification]); | |||||
| } | |||||
| void DrawableText::ValueTreeWrapper::setJustification (const Justification& newJustification, UndoManager* undoManager) | |||||
| { | |||||
| state.setProperty (justification, newJustification.getFlags(), undoManager); | |||||
| } | |||||
| const 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); | |||||
| } | |||||
| const RelativePoint DrawableText::ValueTreeWrapper::getBoundingBoxTopLeft() const | |||||
| { | |||||
| return state [topLeft].toString(); | |||||
| } | |||||
| void DrawableText::ValueTreeWrapper::setBoundingBoxTopLeft (const RelativePoint& p, UndoManager* undoManager) | |||||
| { | |||||
| state.setProperty (topLeft, p.toString(), undoManager); | |||||
| } | |||||
| const RelativePoint DrawableText::ValueTreeWrapper::getBoundingBoxTopRight() const | |||||
| { | |||||
| return state [topRight].toString(); | |||||
| } | |||||
| void DrawableText::ValueTreeWrapper::setBoundingBoxTopRight (const RelativePoint& p, UndoManager* undoManager) | |||||
| { | |||||
| state.setProperty (topRight, p.toString(), undoManager); | |||||
| } | |||||
| const RelativePoint DrawableText::ValueTreeWrapper::getBoundingBoxBottomLeft() const | |||||
| { | |||||
| return state [bottomLeft].toString(); | |||||
| } | |||||
| void DrawableText::ValueTreeWrapper::setBoundingBoxBottomLeft (const RelativePoint& p, UndoManager* undoManager) | |||||
| { | |||||
| state.setProperty (bottomLeft, p.toString(), undoManager); | |||||
| } | |||||
| const RelativePoint DrawableText::ValueTreeWrapper::getFontSizeAndScaleAnchor() const | |||||
| { | |||||
| return state [fontSizeAnchor].toString(); | |||||
| } | |||||
| void DrawableText::ValueTreeWrapper::setFontSizeAndScaleAnchor (const RelativePoint& p, UndoManager* undoManager) | |||||
| { | |||||
| state.setProperty (fontSizeAnchor, p.toString(), undoManager); | |||||
| } | |||||
| const Rectangle<float> DrawableText::refreshFromValueTree (const ValueTree& tree, ImageProvider*) | const Rectangle<float> DrawableText::refreshFromValueTree (const ValueTree& tree, ImageProvider*) | ||||
| { | { | ||||
| ValueTreeWrapper v (tree); | ValueTreeWrapper v (tree); | ||||
| setName (v.getID()); | setName (v.getID()); | ||||
| jassertfalse; // xxx not finished! | |||||
| const RelativePoint p1 (v.getBoundingBoxTopLeft()), p2 (v.getBoundingBoxTopRight()), | |||||
| p3 (v.getBoundingBoxBottomLeft()), p4 (v.getFontSizeAndScaleAnchor()); | |||||
| 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 | |||||
| || p1 != controlPoints[0] || p2 != controlPoints[1] || p3 != controlPoints[2] || p4 != controlPoints[3]) | |||||
| { | |||||
| const Rectangle<float> damage (getBounds()); | |||||
| setBounds (p1, p2, p3, p4); | |||||
| setColour (newColour); | |||||
| setFont (newFont, false); | |||||
| setJustification (newJustification); | |||||
| setText (newText); | |||||
| return damage.getUnion (getBounds()); | |||||
| } | |||||
| return Rectangle<float>(); | return Rectangle<float>(); | ||||
| } | } | ||||
| @@ -116,8 +312,14 @@ const ValueTree DrawableText::createValueTree (ImageProvider*) const | |||||
| ValueTreeWrapper v (tree); | ValueTreeWrapper v (tree); | ||||
| v.setID (getName(), 0); | v.setID (getName(), 0); | ||||
| jassertfalse; // xxx not finished! | |||||
| v.setText (text, 0); | |||||
| v.setFont (font, 0); | |||||
| v.setJustification (justification, 0); | |||||
| v.setColour (colour, 0); | |||||
| v.setBoundingBoxTopLeft (controlPoints[0], 0); | |||||
| v.setBoundingBoxTopRight (controlPoints[1], 0); | |||||
| v.setBoundingBoxBottomLeft (controlPoints[2], 0); | |||||
| v.setFontSizeAndScaleAnchor (controlPoints[3], 0); | |||||
| return tree; | return tree; | ||||
| } | } | ||||
| @@ -48,19 +48,8 @@ public: | |||||
| virtual ~DrawableText(); | virtual ~DrawableText(); | ||||
| //============================================================================== | //============================================================================== | ||||
| /** Sets the block of text to render */ | |||||
| void setText (const GlyphArrangement& newText); | |||||
| /** Sets a single line of text to render. | |||||
| This is a convenient method of adding a single line - for | |||||
| more complex text, use the setText() that takes a | |||||
| GlyphArrangement instead. | |||||
| */ | |||||
| void setText (const String& newText, const Font& fontToUse); | |||||
| /** Returns the text arrangement that was set with setText(). */ | |||||
| const GlyphArrangement& getText() const throw() { return text; } | |||||
| /** Sets the text to display.*/ | |||||
| void setText (const String& newText); | |||||
| /** Sets the colour of the text. */ | /** Sets the colour of the text. */ | ||||
| void setColour (const Colour& newColour); | void setColour (const Colour& newColour); | ||||
| @@ -68,6 +57,36 @@ public: | |||||
| /** Returns the current text colour. */ | /** Returns the current text colour. */ | ||||
| const Colour& getColour() const throw() { return colour; } | const Colour& getColour() const throw() { return colour; } | ||||
| /** Sets the font to use. | |||||
| Note that the font height and horizontal scale are actually based upon the position | |||||
| of the fontSizeAndScaleAnchor parameter to setBounds(). If applySizeAndScale is true, then | |||||
| the height and scale control point will be moved to match the dimensions of the font supplied; | |||||
| if it is false, then the new font's height and scale are ignored. | |||||
| */ | |||||
| void setFont (const Font& newFont, bool applySizeAndScale); | |||||
| /** Changes the justification of the text within the bounding box. */ | |||||
| void setJustification (const Justification& newJustification); | |||||
| /** Sets the bounding box and the control point that controls the font size. | |||||
| The three bounding box points define the parallelogram within which the text will be | |||||
| placed. The fontSizeAndScaleAnchor specifies a position within that parallelogram, whose | |||||
| Y position (relative to the parallelogram's origin and possibly distorted shape) specifies | |||||
| the font's height, and its X defines the font's horizontal scale. | |||||
| */ | |||||
| void setBounds (const RelativePoint& boundingBoxTopLeft, | |||||
| const RelativePoint& boundingBoxTopRight, | |||||
| const RelativePoint& boundingBoxBottomLeft, | |||||
| const RelativePoint& fontSizeAndScaleAnchor); | |||||
| /** Returns the origin of the text bounding box. */ | |||||
| const RelativePoint& getBoundingBoxTopLeft() const throw() { return controlPoints[0]; } | |||||
| /** Returns the top-right of the text bounding box. */ | |||||
| const RelativePoint& getBoundingBoxTopRight() const throw() { return controlPoints[1]; } | |||||
| /** Returns the bottom-left of the text bounding box. */ | |||||
| const RelativePoint& getBoundingBoxBottomLeft() const throw() { return controlPoints[2]; } | |||||
| /** Returns the point within the text bounding box which defines the size and scale of the font. */ | |||||
| const RelativePoint& getFontSizeAndScaleAnchor() const throw() { return controlPoints[3]; } | |||||
| //============================================================================== | //============================================================================== | ||||
| /** @internal */ | /** @internal */ | ||||
| @@ -96,18 +115,44 @@ public: | |||||
| public: | public: | ||||
| ValueTreeWrapper (const ValueTree& state); | ValueTreeWrapper (const ValueTree& state); | ||||
| //xxx todo | |||||
| const String getText() const; | |||||
| void setText (const String& newText, UndoManager* undoManager); | |||||
| private: | |||||
| static const Identifier text; | |||||
| const Colour getColour() const; | |||||
| void setColour (const Colour& newColour, UndoManager* undoManager); | |||||
| const Justification getJustification() const; | |||||
| void setJustification (const Justification& newJustification, UndoManager* undoManager); | |||||
| const Font getFont() const; | |||||
| void setFont (const Font& newFont, UndoManager* undoManager); | |||||
| const RelativePoint getBoundingBoxTopLeft() const; | |||||
| void setBoundingBoxTopLeft (const RelativePoint& p, UndoManager* undoManager); | |||||
| const RelativePoint getBoundingBoxTopRight() const; | |||||
| void setBoundingBoxTopRight (const RelativePoint& p, UndoManager* undoManager); | |||||
| const RelativePoint getBoundingBoxBottomLeft() const; | |||||
| void setBoundingBoxBottomLeft (const RelativePoint& p, UndoManager* undoManager); | |||||
| const RelativePoint getFontSizeAndScaleAnchor() const; | |||||
| void setFontSizeAndScaleAnchor (const RelativePoint& p, UndoManager* undoManager); | |||||
| static const Identifier text, colour, font, justification, topLeft, topRight, bottomLeft, fontSizeAnchor; | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| private: | private: | ||||
| GlyphArrangement text; | |||||
| RelativePoint controlPoints[4]; | |||||
| Font font; | |||||
| String text; | |||||
| Colour colour; | Colour colour; | ||||
| Justification justification; | |||||
| void resolveCorners (Point<float>* corners) const; | |||||
| DrawableText& operator= (const DrawableText&); | DrawableText& operator= (const DrawableText&); | ||||
| }; | }; | ||||
| @@ -241,6 +241,15 @@ const AffineTransform AffineTransform::fromTargetPoints (const float x00, const | |||||
| y10 - y00, y01 - y00, y00); | y10 - y00, y01 - y00, y00); | ||||
| } | } | ||||
| const AffineTransform AffineTransform::fromTargetPoints (const float sx1, const float sy1, const float tx1, const float ty1, | |||||
| const float sx2, const float sy2, const float tx2, const float ty2, | |||||
| const float sx3, const float sy3, const float tx3, const float ty3) throw() | |||||
| { | |||||
| return fromTargetPoints (sx1, sy1, sx2, sy2, sx3, sy3) | |||||
| .inverted() | |||||
| .followedBy (fromTargetPoints (tx1, ty1, tx2, ty2, tx3, ty3)); | |||||
| } | |||||
| bool AffineTransform::isOnlyTranslation() const throw() | bool AffineTransform::isOnlyTranslation() const throw() | ||||
| { | { | ||||
| return (mat01 == 0) | return (mat01 == 0) | ||||
| @@ -156,6 +156,12 @@ public: | |||||
| float x10, float y10, | float x10, float y10, | ||||
| float x01, float y01) throw(); | float x01, float y01) throw(); | ||||
| /** Returns the transform that will map three specified points onto three target points. | |||||
| */ | |||||
| static const AffineTransform fromTargetPoints (float sourceX1, float sourceY1, float targetX1, float targetY1, | |||||
| float sourceX2, float sourceY2, float targetX2, float targetY2, | |||||
| float sourceX3, float sourceY3, float targetX3, float targetY3) throw(); | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Returns the result of concatenating another transformation after this one. */ | /** Returns the result of concatenating another transformation after this one. */ | ||||
| const AffineTransform followedBy (const AffineTransform& other) const throw(); | const AffineTransform followedBy (const AffineTransform& other) const throw(); | ||||
| @@ -164,6 +164,18 @@ public: | |||||
| return findIntersection (start, end, line.start, line.end, intersection); | return findIntersection (start, end, line.start, line.end, intersection); | ||||
| } | } | ||||
| /** Finds the intersection between two lines. | |||||
| @param line the line to intersect with | |||||
| @returns the point at which the lines intersect, even if this lies beyond the end of the lines | |||||
| */ | |||||
| const Point<ValueType> getIntersection (const Line& line) const throw() | |||||
| { | |||||
| Point<ValueType> p; | |||||
| findIntersection (start, end, line.start, line.end, p); | |||||
| return p; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Returns the location of the point which is a given distance along this line. | /** Returns the location of the point which is a given distance along this line. | ||||
| @@ -141,12 +141,20 @@ public: | |||||
| /** Returns the stroke thickness. */ | /** Returns the stroke thickness. */ | ||||
| float getStrokeThickness() const throw() { return thickness; } | float getStrokeThickness() const throw() { return thickness; } | ||||
| /** Sets the stroke thickness. */ | |||||
| void setStrokeThickness (float newThickness) throw() { thickness = newThickness; } | |||||
| /** Returns the joint style. */ | /** Returns the joint style. */ | ||||
| JointStyle getJointStyle() const throw() { return jointStyle; } | JointStyle getJointStyle() const throw() { return jointStyle; } | ||||
| /** Sets the joint style. */ | |||||
| void setJointStyle (JointStyle newStyle) throw() { jointStyle = newStyle; } | |||||
| /** Returns the end-cap style. */ | /** Returns the end-cap style. */ | ||||
| EndCapStyle getEndStyle() const throw() { return endStyle; } | EndCapStyle getEndStyle() const throw() { return endStyle; } | ||||
| /** Sets the end-cap style. */ | |||||
| void setEndStyle (EndCapStyle newStyle) throw() { endStyle = newStyle; } | |||||
| //============================================================================== | //============================================================================== | ||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| @@ -509,6 +509,28 @@ public: | |||||
| return Rectangle<int> (x1, y1, x2 - x1, y2 - y1); | return Rectangle<int> (x1, y1, x2 - x1, y2 - y1); | ||||
| } | } | ||||
| /** Returns the smallest Rectangle that can contain a set of points. */ | |||||
| static const Rectangle findAreaContainingPoints (const Point<ValueType>* const points, const int numPoints) throw() | |||||
| { | |||||
| if (numPoints == 0) | |||||
| return Rectangle(); | |||||
| ValueType minX (points[0].getX()); | |||||
| ValueType maxX (minX); | |||||
| ValueType minY (points[0].getY()); | |||||
| ValueType maxY (minY); | |||||
| for (int i = 1; i < numPoints; ++i) | |||||
| { | |||||
| minX = jmin (minX, points[i].getX()); | |||||
| maxX = jmax (maxX, points[i].getX()); | |||||
| minY = jmin (minY, points[i].getY()); | |||||
| maxY = jmax (maxY, points[i].getY()); | |||||
| } | |||||
| return Rectangle (minX, minY, maxX - minX, maxY - minY); | |||||
| } | |||||
| /** Casts this rectangle to a Rectangle<float>. | /** Casts this rectangle to a Rectangle<float>. | ||||
| Obviously this is mainly useful for rectangles that use integer types. | Obviously this is mainly useful for rectangles that use integer types. | ||||
| @see getSmallestIntegerContainer | @see getSmallestIntegerContainer | ||||
| @@ -145,7 +145,8 @@ public: | |||||
| : context (context_), | : context (context_), | ||||
| flipHeight (flipHeight_), | flipHeight (flipHeight_), | ||||
| state (new SavedState()), | state (new SavedState()), | ||||
| numGradientLookupEntries (0) | |||||
| numGradientLookupEntries (0), | |||||
| lastClipRectIsValid (false) | |||||
| { | { | ||||
| CGContextRetain (context); | CGContextRetain (context); | ||||
| CGContextSaveGState(context); | CGContextSaveGState(context); | ||||
| @@ -174,11 +175,21 @@ public: | |||||
| void setOrigin (int x, int y) | void setOrigin (int x, int y) | ||||
| { | { | ||||
| CGContextTranslateCTM (context, x, -y); | CGContextTranslateCTM (context, x, -y); | ||||
| if (lastClipRectIsValid) | |||||
| lastClipRect.translate (-x, -y); | |||||
| } | } | ||||
| bool clipToRectangle (const Rectangle<int>& r) | bool clipToRectangle (const Rectangle<int>& r) | ||||
| { | { | ||||
| CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); | CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); | ||||
| if (lastClipRectIsValid) | |||||
| { | |||||
| lastClipRect = lastClipRect.getIntersection (r); | |||||
| return ! r.isEmpty(); | |||||
| } | |||||
| return ! isClipEmpty(); | return ! isClipEmpty(); | ||||
| } | } | ||||
| @@ -187,6 +198,8 @@ public: | |||||
| if (clipRegion.isEmpty()) | if (clipRegion.isEmpty()) | ||||
| { | { | ||||
| CGContextClipToRect (context, CGRectMake (0, 0, 0, 0)); | CGContextClipToRect (context, CGRectMake (0, 0, 0, 0)); | ||||
| lastClipRectIsValid = true; | |||||
| lastClipRect = Rectangle<int>(); | |||||
| return false; | return false; | ||||
| } | } | ||||
| else | else | ||||
| @@ -201,6 +214,7 @@ public: | |||||
| } | } | ||||
| CGContextClipToRects (context, rects, numRects); | CGContextClipToRects (context, rects, numRects); | ||||
| lastClipRectIsValid = false; | |||||
| return ! isClipEmpty(); | return ! isClipEmpty(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -210,12 +224,14 @@ public: | |||||
| RectangleList remaining (getClipBounds()); | RectangleList remaining (getClipBounds()); | ||||
| remaining.subtract (r); | remaining.subtract (r); | ||||
| clipToRectangleList (remaining); | clipToRectangleList (remaining); | ||||
| lastClipRectIsValid = false; | |||||
| } | } | ||||
| void clipToPath (const Path& path, const AffineTransform& transform) | void clipToPath (const Path& path, const AffineTransform& transform) | ||||
| { | { | ||||
| createPath (path, transform); | createPath (path, transform); | ||||
| CGContextClip (context); | CGContextClip (context); | ||||
| lastClipRectIsValid = false; | |||||
| } | } | ||||
| void clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform) | void clipToImageAlpha (const Image& sourceImage, const Rectangle<int>& srcClip, const AffineTransform& transform) | ||||
| @@ -240,6 +256,7 @@ public: | |||||
| flip(); | flip(); | ||||
| CGImageRelease (image); | CGImageRelease (image); | ||||
| lastClipRectIsValid = false; | |||||
| } | } | ||||
| } | } | ||||
| @@ -250,17 +267,23 @@ public: | |||||
| const Rectangle<int> getClipBounds() const | const Rectangle<int> getClipBounds() const | ||||
| { | { | ||||
| CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context)); | |||||
| if (! lastClipRectIsValid) | |||||
| { | |||||
| CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context)); | |||||
| lastClipRectIsValid = true; | |||||
| lastClipRect.setBounds (roundToInt (bounds.origin.x), | |||||
| roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)), | |||||
| roundToInt (bounds.size.width), | |||||
| roundToInt (bounds.size.height)); | |||||
| } | |||||
| return Rectangle<int> (roundToInt (bounds.origin.x), | |||||
| roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)), | |||||
| roundToInt (bounds.size.width), | |||||
| roundToInt (bounds.size.height)); | |||||
| return lastClipRect; | |||||
| } | } | ||||
| bool isClipEmpty() const | bool isClipEmpty() const | ||||
| { | { | ||||
| return CGRectIsEmpty (CGContextGetClipBoundingBox (context)); | |||||
| return getClipBounds().isEmpty(); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -280,6 +303,7 @@ public: | |||||
| { | { | ||||
| state = top; | state = top; | ||||
| stateStack.removeLast (1, false); | stateStack.removeLast (1, false); | ||||
| lastClipRectIsValid = false; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -564,6 +588,8 @@ private: | |||||
| const CGFloat flipHeight; | const CGFloat flipHeight; | ||||
| CGColorSpaceRef rgbColourSpace, greyColourSpace; | CGColorSpaceRef rgbColourSpace, greyColourSpace; | ||||
| CGFunctionCallbacks gradientCallbacks; | CGFunctionCallbacks gradientCallbacks; | ||||
| mutable Rectangle<int> lastClipRect; | |||||
| mutable bool lastClipRectIsValid; | |||||
| struct SavedState | struct SavedState | ||||
| { | { | ||||