| @@ -162,27 +162,22 @@ public: | |||
| drawable = newDrawable; | |||
| drawable.addListener (this); | |||
| drawableObject = Drawable::createFromValueTree (drawable, 0); // xxx image provider missing | |||
| addAndMakeVisible (drawableObject); | |||
| resized(); | |||
| repaint(); | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| if (drawableObject != 0) | |||
| drawableObject->drawAt (g, 0, 0, 1.0f); | |||
| } | |||
| void resized() | |||
| { | |||
| DrawableComposite* dc = dynamic_cast <DrawableComposite*> (static_cast <Drawable*> (drawableObject)); | |||
| /* DrawableComposite* dc = dynamic_cast <DrawableComposite*> (static_cast <Drawable*> (drawableObject)); | |||
| if (dc != 0) | |||
| { | |||
| const RelativeCoordinate origin, right (getWidth()), bottom (getHeight()); | |||
| dc->setContentArea (RelativeRectangle (origin, right, origin, bottom)); | |||
| dc->resetBoundingBoxToContentArea(); | |||
| } | |||
| //dc->resetBoundingBoxToContentArea(); | |||
| }*/ | |||
| } | |||
| void valueTreePropertyChanged (ValueTree&, const Identifier&) { updateGraphics(); } | |||
| @@ -196,10 +191,7 @@ private: | |||
| void updateGraphics() | |||
| { | |||
| if (drawableObject != 0) | |||
| { | |||
| const Rectangle<float> dirtyArea (drawableObject->refreshFromValueTree (drawable, 0)); | |||
| repaint (dirtyArea.getSmallestIntegerContainer()); | |||
| } | |||
| drawableObject->refreshFromValueTree (drawable, 0); | |||
| } | |||
| }; | |||
| @@ -540,7 +540,7 @@ void PaintRoutine::dropImageAt (const File& f, int x, int y) | |||
| if (d != 0) | |||
| { | |||
| Rectangle<float> bounds (d->getBounds()); | |||
| Rectangle<float> bounds (d->getDrawableBounds()); | |||
| delete d; | |||
| PaintElement* newElement | |||
| @@ -310,7 +310,7 @@ public: | |||
| const Rectangle<int> parentArea (((PaintRoutineEditor*) getParentComponent())->getComponentArea()); | |||
| Rectangle<int> r (getCurrentBounds (parentArea)); | |||
| Rectangle<float> bounds (image->getBounds()); | |||
| Rectangle<float> bounds (image->getDrawableBounds()); | |||
| r.setSize ((int) (bounds.getWidth() + 0.999f), (int) (bounds.getHeight() + 0.999f)); | |||
| @@ -33,7 +33,7 @@ | |||
| */ | |||
| #define JUCE_MAJOR_VERSION 1 | |||
| #define JUCE_MINOR_VERSION 52 | |||
| #define JUCE_BUILDNUMBER 95 | |||
| #define JUCE_BUILDNUMBER 96 | |||
| /** Current Juce version number. | |||
| @@ -36,6 +36,7 @@ DrawableButton::DrawableButton (const String& name, | |||
| const DrawableButton::ButtonStyle buttonStyle) | |||
| : Button (name), | |||
| style (buttonStyle), | |||
| currentImage (0), | |||
| edgeIndent (3) | |||
| { | |||
| if (buttonStyle == ImageOnButtonBackground) | |||
| @@ -52,14 +53,9 @@ DrawableButton::DrawableButton (const String& name, | |||
| DrawableButton::~DrawableButton() | |||
| { | |||
| deleteImages(); | |||
| } | |||
| //============================================================================== | |||
| void DrawableButton::deleteImages() | |||
| { | |||
| } | |||
| void DrawableButton::setImages (const Drawable* normal, | |||
| const Drawable* over, | |||
| const Drawable* down, | |||
| @@ -69,36 +65,18 @@ void DrawableButton::setImages (const Drawable* normal, | |||
| const Drawable* downOn, | |||
| const Drawable* disabledOn) | |||
| { | |||
| deleteImages(); | |||
| jassert (normal != 0); // you really need to give it at least a normal image.. | |||
| if (normal != 0) | |||
| normalImage = normal->createCopy(); | |||
| if (over != 0) | |||
| overImage = over->createCopy(); | |||
| if (down != 0) | |||
| downImage = down->createCopy(); | |||
| if (disabled != 0) | |||
| disabledImage = disabled->createCopy(); | |||
| if (normalOn != 0) | |||
| normalImageOn = normalOn->createCopy(); | |||
| if (overOn != 0) | |||
| overImageOn = overOn->createCopy(); | |||
| if (downOn != 0) | |||
| downImageOn = downOn->createCopy(); | |||
| if (normal != 0) normalImage = normal->createCopy(); | |||
| if (over != 0) overImage = over->createCopy(); | |||
| if (down != 0) downImage = down->createCopy(); | |||
| if (disabled != 0) disabledImage = disabled->createCopy(); | |||
| if (normalOn != 0) normalImageOn = normalOn->createCopy(); | |||
| if (overOn != 0) overImageOn = overOn->createCopy(); | |||
| if (downOn != 0) downImageOn = downOn->createCopy(); | |||
| if (disabledOn != 0) disabledImageOn = disabledOn->createCopy(); | |||
| if (disabledOn != 0) | |||
| disabledImageOn = disabledOn->createCopy(); | |||
| repaint(); | |||
| buttonStateChanged(); | |||
| } | |||
| //============================================================================== | |||
| @@ -107,7 +85,7 @@ void DrawableButton::setButtonStyle (const DrawableButton::ButtonStyle newStyle) | |||
| if (style != newStyle) | |||
| { | |||
| style = newStyle; | |||
| repaint(); | |||
| buttonStateChanged(); | |||
| } | |||
| } | |||
| @@ -134,21 +112,88 @@ void DrawableButton::setEdgeIndent (const int numPixelsIndent) | |||
| { | |||
| edgeIndent = numPixelsIndent; | |||
| repaint(); | |||
| resized(); | |||
| } | |||
| void DrawableButton::resized() | |||
| { | |||
| Button::resized(); | |||
| if (style == ImageRaw) | |||
| { | |||
| currentImage->setOriginWithOriginalSize (Point<float>()); | |||
| } | |||
| else if (currentImage != 0) | |||
| { | |||
| Rectangle<int> imageSpace; | |||
| if (style == ImageOnButtonBackground) | |||
| { | |||
| imageSpace = getLocalBounds().reduced (getWidth() / 4, getHeight() / 4); | |||
| } | |||
| else | |||
| { | |||
| const int textH = (style == ImageAboveTextLabel) | |||
| ? jmin (16, proportionOfHeight (0.25f)) | |||
| : 0; | |||
| const int indentX = jmin (edgeIndent, proportionOfWidth (0.3f)); | |||
| const int indentY = jmin (edgeIndent, proportionOfHeight (0.3f)); | |||
| imageSpace.setBounds (indentX, indentY, | |||
| getWidth() - indentX * 2, | |||
| getHeight() - indentY * 2 - textH); | |||
| } | |||
| currentImage->setTransformToFit (imageSpace.toFloat(), RectanglePlacement::centred); | |||
| } | |||
| } | |||
| void DrawableButton::buttonStateChanged() | |||
| { | |||
| repaint(); | |||
| Drawable* imageToDraw = 0; | |||
| float opacity = 1.0f; | |||
| if (isEnabled()) | |||
| { | |||
| imageToDraw = getCurrentImage(); | |||
| } | |||
| else | |||
| { | |||
| imageToDraw = getToggleState() ? disabledImageOn | |||
| : disabledImage; | |||
| if (imageToDraw == 0) | |||
| { | |||
| opacity = 0.4f; | |||
| imageToDraw = getNormalImage(); | |||
| } | |||
| } | |||
| if (imageToDraw != currentImage) | |||
| { | |||
| removeChildComponent (currentImage); | |||
| currentImage = imageToDraw; | |||
| if (currentImage != 0) | |||
| { | |||
| addAndMakeVisible (currentImage); | |||
| DrawableButton::resized(); | |||
| } | |||
| } | |||
| if (currentImage != 0) | |||
| currentImage->setAlpha (opacity); | |||
| } | |||
| void DrawableButton::paintButton (Graphics& g, | |||
| bool isMouseOverButton, | |||
| bool isButtonDown) | |||
| { | |||
| Rectangle<int> imageSpace; | |||
| if (style == ImageOnButtonBackground) | |||
| { | |||
| const int insetX = getWidth() / 4; | |||
| const int insetY = getHeight() / 4; | |||
| imageSpace.setBounds (insetX, insetY, getWidth() - insetX * 2, getHeight() - insetY * 2); | |||
| getLookAndFeel().drawButtonBackground (g, *this, | |||
| getBackgroundColour(), | |||
| isMouseOverButton, | |||
| @@ -162,13 +207,6 @@ void DrawableButton::paintButton (Graphics& g, | |||
| ? jmin (16, proportionOfHeight (0.25f)) | |||
| : 0; | |||
| const int indentX = jmin (edgeIndent, proportionOfWidth (0.3f)); | |||
| const int indentY = jmin (edgeIndent, proportionOfHeight (0.3f)); | |||
| imageSpace.setBounds (indentX, indentY, | |||
| getWidth() - indentX * 2, | |||
| getHeight() - indentY * 2 - textH); | |||
| if (textH > 0) | |||
| { | |||
| g.setFont ((float) textH); | |||
| @@ -182,39 +220,10 @@ void DrawableButton::paintButton (Graphics& g, | |||
| Justification::centred, 1); | |||
| } | |||
| } | |||
| g.setImageResamplingQuality (Graphics::mediumResamplingQuality); | |||
| g.setOpacity (1.0f); | |||
| const Drawable* imageToDraw = 0; | |||
| if (isEnabled()) | |||
| { | |||
| imageToDraw = getCurrentImage(); | |||
| } | |||
| else | |||
| { | |||
| imageToDraw = getToggleState() ? disabledImageOn | |||
| : disabledImage; | |||
| if (imageToDraw == 0) | |||
| { | |||
| g.setOpacity (0.4f); | |||
| imageToDraw = getNormalImage(); | |||
| } | |||
| } | |||
| if (imageToDraw != 0) | |||
| { | |||
| if (style == ImageRaw) | |||
| imageToDraw->draw (g, 1.0f); | |||
| else | |||
| imageToDraw->drawWithin (g, imageSpace.toFloat(), RectanglePlacement::centred, 1.0f); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| const Drawable* DrawableButton::getCurrentImage() const throw() | |||
| Drawable* DrawableButton::getCurrentImage() const throw() | |||
| { | |||
| if (isDown()) | |||
| return getDownImage(); | |||
| @@ -225,15 +234,15 @@ const Drawable* DrawableButton::getCurrentImage() const throw() | |||
| return getNormalImage(); | |||
| } | |||
| const Drawable* DrawableButton::getNormalImage() const throw() | |||
| Drawable* DrawableButton::getNormalImage() const throw() | |||
| { | |||
| return (getToggleState() && normalImageOn != 0) ? normalImageOn | |||
| : normalImage; | |||
| } | |||
| const Drawable* DrawableButton::getOverImage() const throw() | |||
| Drawable* DrawableButton::getOverImage() const throw() | |||
| { | |||
| const Drawable* d = normalImage; | |||
| Drawable* d = normalImage; | |||
| if (getToggleState()) | |||
| { | |||
| @@ -253,9 +262,9 @@ const Drawable* DrawableButton::getOverImage() const throw() | |||
| return d; | |||
| } | |||
| const Drawable* DrawableButton::getDownImage() const throw() | |||
| Drawable* DrawableButton::getDownImage() const throw() | |||
| { | |||
| const Drawable* d = normalImage; | |||
| Drawable* d = normalImage; | |||
| if (getToggleState()) | |||
| { | |||
| @@ -148,10 +148,10 @@ public: | |||
| //============================================================================== | |||
| /** Returns the image that the button is currently displaying. */ | |||
| const Drawable* getCurrentImage() const throw(); | |||
| const Drawable* getNormalImage() const throw(); | |||
| const Drawable* getOverImage() const throw(); | |||
| const Drawable* getDownImage() const throw(); | |||
| Drawable* getCurrentImage() const throw(); | |||
| Drawable* getNormalImage() const throw(); | |||
| Drawable* getOverImage() const throw(); | |||
| Drawable* getDownImage() const throw(); | |||
| //============================================================================== | |||
| /** A set of colour IDs to use to change the colour of various aspects of the link. | |||
| @@ -174,16 +174,20 @@ protected: | |||
| void paintButton (Graphics& g, | |||
| bool isMouseOverButton, | |||
| bool isButtonDown); | |||
| /** @internal */ | |||
| void buttonStateChanged(); | |||
| /** @internal */ | |||
| void resized(); | |||
| private: | |||
| //============================================================================== | |||
| ButtonStyle style; | |||
| ScopedPointer <Drawable> normalImage, overImage, downImage, disabledImage; | |||
| ScopedPointer <Drawable> normalImageOn, overImageOn, downImageOn, disabledImageOn; | |||
| Drawable* currentImage; | |||
| Colour backgroundOff, backgroundOn; | |||
| int edgeIndent; | |||
| void deleteImages(); | |||
| DrawableButton (const DrawableButton&); | |||
| DrawableButton& operator= (const DrawableButton&); | |||
| }; | |||
| @@ -34,13 +34,12 @@ BEGIN_JUCE_NAMESPACE | |||
| //============================================================================== | |||
| ToolbarButton::ToolbarButton (const int itemId_, | |||
| const String& buttonText, | |||
| Drawable* const normalImage_, | |||
| Drawable* const toggledOnImage_) | |||
| ToolbarButton::ToolbarButton (const int itemId_, const String& buttonText, | |||
| Drawable* const normalImage_, Drawable* const toggledOnImage_) | |||
| : ToolbarItemComponent (itemId_, buttonText, true), | |||
| normalImage (normalImage_), | |||
| toggledOnImage (toggledOnImage_) | |||
| toggledOnImage (toggledOnImage_), | |||
| currentImage (0) | |||
| { | |||
| jassert (normalImage_ != 0); | |||
| } | |||
| @@ -50,47 +49,61 @@ ToolbarButton::~ToolbarButton() | |||
| } | |||
| //============================================================================== | |||
| bool ToolbarButton::getToolbarItemSizes (int toolbarDepth, | |||
| bool /*isToolbarVertical*/, | |||
| int& preferredSize, | |||
| int& minSize, int& maxSize) | |||
| bool ToolbarButton::getToolbarItemSizes (int toolbarDepth, bool /*isToolbarVertical*/, int& preferredSize, int& minSize, int& maxSize) | |||
| { | |||
| preferredSize = minSize = maxSize = toolbarDepth; | |||
| return true; | |||
| } | |||
| void ToolbarButton::paintButtonArea (Graphics& g, | |||
| int width, int height, | |||
| bool /*isMouseOver*/, | |||
| bool /*isMouseDown*/) | |||
| void ToolbarButton::paintButtonArea (Graphics&, int /*width*/, int /*height*/, bool /*isMouseOver*/, bool /*isMouseDown*/) | |||
| { | |||
| } | |||
| void ToolbarButton::contentAreaChanged (const Rectangle<int>&) | |||
| { | |||
| buttonStateChanged(); | |||
| } | |||
| void ToolbarButton::updateDrawable() | |||
| { | |||
| if (currentImage != 0) | |||
| { | |||
| currentImage->setTransformToFit (getContentArea().toFloat(), RectanglePlacement::centred); | |||
| currentImage->setAlpha (isEnabled() ? 1.0f : 0.5f); | |||
| } | |||
| } | |||
| void ToolbarButton::resized() | |||
| { | |||
| ToolbarItemComponent::resized(); | |||
| updateDrawable(); | |||
| } | |||
| void ToolbarButton::enablementChanged() | |||
| { | |||
| ToolbarItemComponent::enablementChanged(); | |||
| updateDrawable(); | |||
| } | |||
| void ToolbarButton::buttonStateChanged() | |||
| { | |||
| Drawable* d = normalImage; | |||
| if (getToggleState() && toggledOnImage != 0) | |||
| d = toggledOnImage; | |||
| const Rectangle<float> area (0.0f, 0.0f, (float) width, (float) height); | |||
| if (! isEnabled()) | |||
| if (d != currentImage) | |||
| { | |||
| Image im (Image::ARGB, width, height, true); | |||
| removeChildComponent (currentImage); | |||
| currentImage = d; | |||
| if (d != 0) | |||
| { | |||
| Graphics g2 (im); | |||
| d->drawWithin (g2, area, RectanglePlacement::centred, 1.0f); | |||
| enablementChanged(); | |||
| addAndMakeVisible (d); | |||
| updateDrawable(); | |||
| } | |||
| im.desaturate(); | |||
| g.drawImageAt (im, 0, 0); | |||
| } | |||
| else | |||
| { | |||
| d->drawWithin (g, area, RectanglePlacement::centred, 1.0f); | |||
| } | |||
| } | |||
| void ToolbarButton::contentAreaChanged (const Rectangle<int>&) | |||
| { | |||
| } | |||
| @@ -77,11 +77,20 @@ public: | |||
| void paintButtonArea (Graphics& g, int width, int height, bool isMouseOver, bool isMouseDown); | |||
| /** @internal */ | |||
| void contentAreaChanged (const Rectangle<int>& newBounds); | |||
| /** @internal */ | |||
| void buttonStateChanged(); | |||
| /** @internal */ | |||
| void resized(); | |||
| /** @internal */ | |||
| void enablementChanged(); | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| ScopedPointer <Drawable> normalImage, toggledOnImage; | |||
| ScopedPointer<Drawable> normalImage, toggledOnImage; | |||
| Drawable* currentImage; | |||
| void updateDrawable(); | |||
| ToolbarButton (const ToolbarButton&); | |||
| ToolbarButton& operator= (const ToolbarButton&); | |||
| @@ -44,7 +44,6 @@ ToolbarItemFactory::~ToolbarItemFactory() | |||
| { | |||
| } | |||
| //============================================================================== | |||
| class ItemDragAndDropOverlayComponent : public Component | |||
| { | |||
| @@ -57,10 +56,6 @@ public: | |||
| setMouseCursor (MouseCursor::DraggingHandCursor); | |||
| } | |||
| ~ItemDragAndDropOverlayComponent() | |||
| { | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| ToolbarItemComponent* const tc = dynamic_cast <ToolbarItemComponent*> (getParentComponent()); | |||
| @@ -40,42 +40,90 @@ BEGIN_JUCE_NAMESPACE | |||
| //============================================================================== | |||
| Drawable::RenderingContext::RenderingContext (Graphics& g_, | |||
| const AffineTransform& transform_, | |||
| const float opacity_) throw() | |||
| : g (g_), | |||
| transform (transform_), | |||
| opacity (opacity_) | |||
| Drawable::Drawable() | |||
| { | |||
| setInterceptsMouseClicks (false, false); | |||
| setPaintingIsUnclipped (true); | |||
| } | |||
| //============================================================================== | |||
| Drawable::Drawable() | |||
| : parent (0) | |||
| Drawable::~Drawable() | |||
| { | |||
| } | |||
| Drawable::~Drawable() | |||
| //============================================================================== | |||
| void Drawable::draw (Graphics& g, float opacity, const AffineTransform& transform) const | |||
| { | |||
| const_cast <Drawable*> (this)->nonConstDraw (g, opacity, transform); | |||
| } | |||
| void Drawable::draw (Graphics& g, const float opacity, const AffineTransform& transform) const | |||
| void Drawable::nonConstDraw (Graphics& g, float opacity, const AffineTransform& transform) | |||
| { | |||
| render (RenderingContext (g, transform, opacity)); | |||
| g.saveState(); | |||
| const float oldOpacity = getAlpha(); | |||
| setAlpha (opacity); | |||
| g.addTransform (AffineTransform::translation (-originRelativeToComponent.getX(), | |||
| -originRelativeToComponent.getY()) | |||
| .followedBy (getTransform()) | |||
| .followedBy (transform)); | |||
| paintEntireComponent (g, false); | |||
| setAlpha (oldOpacity); | |||
| g.restoreState(); | |||
| } | |||
| void Drawable::drawAt (Graphics& g, const float x, const float y, const float opacity) const | |||
| void Drawable::drawAt (Graphics& g, float x, float y, float opacity) const | |||
| { | |||
| draw (g, opacity, AffineTransform::translation (x, y)); | |||
| } | |||
| void Drawable::drawWithin (Graphics& g, | |||
| const Rectangle<float>& destArea, | |||
| const RectanglePlacement& placement, | |||
| const float opacity) const | |||
| void Drawable::drawWithin (Graphics& g, const Rectangle<float>& destArea, const RectanglePlacement& placement, float opacity) const | |||
| { | |||
| draw (g, opacity, placement.getTransformToFit (getDrawableBounds(), destArea)); | |||
| } | |||
| //============================================================================== | |||
| DrawableComposite* Drawable::getParent() const | |||
| { | |||
| return dynamic_cast <DrawableComposite*> (getParentComponent()); | |||
| } | |||
| void Drawable::transformContextToCorrectOrigin (Graphics& g) | |||
| { | |||
| g.setOrigin (originRelativeToComponent.getX(), | |||
| originRelativeToComponent.getY()); | |||
| } | |||
| void Drawable::markerHasMoved() | |||
| { | |||
| } | |||
| void Drawable::parentHierarchyChanged() | |||
| { | |||
| if (! destArea.isEmpty()) | |||
| draw (g, opacity, placement.getTransformToFit (getBounds(), destArea)); | |||
| setBoundsToEnclose (getDrawableBounds()); | |||
| } | |||
| void Drawable::setBoundsToEnclose (const Rectangle<float>& area) | |||
| { | |||
| Drawable* const parent = getParent(); | |||
| Point<int> parentOrigin; | |||
| if (parent != 0) | |||
| parentOrigin = parent->originRelativeToComponent; | |||
| const Rectangle<int> newBounds (area.getSmallestIntegerContainer() + parentOrigin); | |||
| originRelativeToComponent = parentOrigin - newBounds.getPosition(); | |||
| setBounds (newBounds); | |||
| } | |||
| //============================================================================== | |||
| void Drawable::setOriginWithOriginalSize (const Point<float>& originWithinParent) | |||
| { | |||
| setTransform (AffineTransform::translation (originWithinParent.getX(), originWithinParent.getY())); | |||
| } | |||
| void Drawable::setTransformToFit (const Rectangle<float>& area, const RectanglePlacement& placement) | |||
| { | |||
| if (! area.isEmpty()) | |||
| setTransform (placement.getTransformToFit (getDrawableBounds(), area)); | |||
| } | |||
| //============================================================================== | |||
| @@ -136,20 +184,17 @@ Drawable* Drawable::createChildFromValueTree (DrawableComposite* parent, const V | |||
| const Identifier type (tree.getType()); | |||
| Drawable* d = 0; | |||
| if (type == DrawablePath::valueTreeType) | |||
| d = new DrawablePath(); | |||
| else if (type == DrawableComposite::valueTreeType) | |||
| d = new DrawableComposite(); | |||
| else if (type == DrawableRectangle::valueTreeType) | |||
| d = new DrawableRectangle(); | |||
| else if (type == DrawableImage::valueTreeType) | |||
| d = new DrawableImage(); | |||
| else if (type == DrawableText::valueTreeType) | |||
| d = new DrawableText(); | |||
| if (type == DrawablePath::valueTreeType) d = new DrawablePath(); | |||
| else if (type == DrawableComposite::valueTreeType) d = new DrawableComposite(); | |||
| else if (type == DrawableRectangle::valueTreeType) d = new DrawableRectangle(); | |||
| else if (type == DrawableImage::valueTreeType) d = new DrawableImage(); | |||
| else if (type == DrawableText::valueTreeType) d = new DrawableText(); | |||
| if (d != 0) | |||
| { | |||
| d->parent = parent; | |||
| if (parent != 0) | |||
| parent->insertDrawable (d); | |||
| d->refreshFromValueTree (tree, imageProvider); | |||
| } | |||
| @@ -26,7 +26,7 @@ | |||
| #ifndef __JUCE_DRAWABLE_JUCEHEADER__ | |||
| #define __JUCE_DRAWABLE_JUCEHEADER__ | |||
| #include "../contexts/juce_Graphics.h" | |||
| #include "../../components/juce_Component.h" | |||
| #include "../geometry/juce_RelativeCoordinate.h" | |||
| #include "../../../text/juce_XmlElement.h" | |||
| #include "../../../containers/juce_ValueTree.h" | |||
| @@ -39,7 +39,7 @@ class DrawableComposite; | |||
| @see DrawableComposite, DrawableImage, DrawablePath, DrawableText | |||
| */ | |||
| class JUCE_API Drawable | |||
| class JUCE_API Drawable : public Component | |||
| { | |||
| protected: | |||
| //============================================================================== | |||
| @@ -62,6 +62,11 @@ public: | |||
| //============================================================================== | |||
| /** Renders this Drawable object. | |||
| Note that the preferred way to render a drawable in future is by using it | |||
| as a component and adding it to a parent, so you might want to consider that | |||
| before using this method. | |||
| @see drawWithin | |||
| */ | |||
| void draw (Graphics& g, float opacity, | |||
| @@ -75,10 +80,12 @@ public: | |||
| @code | |||
| draw (g, AffineTransform::translation (x, y)). | |||
| @endcode | |||
| Note that the preferred way to render a drawable in future is by using it | |||
| as a component and adding it to a parent, so you might want to consider that | |||
| before using this method. | |||
| */ | |||
| void drawAt (Graphics& g, | |||
| float x, float y, | |||
| float opacity) const; | |||
| void drawAt (Graphics& g, float x, float y, float opacity) const; | |||
| /** Renders the Drawable within a rectangle, scaling it to fit neatly inside without | |||
| changing its aspect-ratio. | |||
| @@ -86,6 +93,10 @@ public: | |||
| The object can placed arbitrarily within the rectangle based on a Justification type, | |||
| and can either be made as big as possible, or just reduced to fit. | |||
| Note that the preferred way to render a drawable in future is by using it | |||
| as a component and adding it to a parent, so you might want to consider that | |||
| before using this method. | |||
| @param g the graphics context to render onto | |||
| @param destArea the target rectangle to fit the drawable into | |||
| @param placement defines the alignment and rescaling to use to fit | |||
| @@ -99,51 +110,18 @@ public: | |||
| //============================================================================== | |||
| /** Holds the information needed when telling a drawable to render itself. | |||
| @see Drawable::draw | |||
| /** Resets any transformations on this drawable, and positions its origin within | |||
| its parent component. | |||
| */ | |||
| class RenderingContext | |||
| { | |||
| public: | |||
| RenderingContext (Graphics& g, const AffineTransform& transform, float opacity) throw(); | |||
| void setOriginWithOriginalSize (const Point<float>& originWithinParent); | |||
| Graphics& g; | |||
| AffineTransform transform; | |||
| float opacity; | |||
| private: | |||
| RenderingContext& operator= (const RenderingContext&); | |||
| }; | |||
| /** Renders this Drawable object. | |||
| @see draw | |||
| /** Sets a transform for this drawable that will position it within the specified | |||
| area of its parent component. | |||
| */ | |||
| virtual void render (const RenderingContext& context) const = 0; | |||
| //============================================================================== | |||
| /** Returns the smallest rectangle that can contain this Drawable object. | |||
| Co-ordinates are relative to the object's own origin. | |||
| */ | |||
| virtual const Rectangle<float> getBounds() const = 0; | |||
| /** Returns true if the given point is somewhere inside this Drawable. | |||
| Co-ordinates are relative to the object's own origin. | |||
| */ | |||
| virtual bool hitTest (float x, float y) const = 0; | |||
| //============================================================================== | |||
| /** Returns the name given to this drawable. | |||
| @see setName | |||
| */ | |||
| const String& getName() const throw() { return name; } | |||
| /** Assigns a name to this drawable. */ | |||
| void setName (const String& newName) throw() { name = newName; } | |||
| void setTransformToFit (const Rectangle<float>& areaInParent, const RectanglePlacement& placement); | |||
| /** Returns the DrawableComposite that contains this object, if there is one. */ | |||
| DrawableComposite* getParent() const throw() { return parent; } | |||
| DrawableComposite* getParent() const; | |||
| //============================================================================== | |||
| /** Tries to turn some kind of image file into a drawable. | |||
| @@ -215,7 +193,7 @@ public: | |||
| /** Tries to refresh a Drawable from the same ValueTree that was used to create it. | |||
| @returns the damage rectangle that will need repainting due to any changes that were made. | |||
| */ | |||
| virtual const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) = 0; | |||
| virtual void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) = 0; | |||
| /** Creates a ValueTree to represent this Drawable. | |||
| The VarTree that is returned can be turned back into a Drawable with | |||
| @@ -228,6 +206,12 @@ public: | |||
| /** Returns the tag ID that is used for a ValueTree that stores this type of drawable. */ | |||
| virtual const Identifier getValueTreeType() const = 0; | |||
| /** Returns the area that this drawble covers. | |||
| The result is expressed in this drawable's own coordinate space, and does not take | |||
| into account any transforms that may be applied to the component. | |||
| */ | |||
| virtual const Rectangle<float> getDrawableBounds() const = 0; | |||
| //============================================================================== | |||
| /** Internal class used to manage ValueTrees that represent Drawables. */ | |||
| class ValueTreeWrapperBase | |||
| @@ -250,15 +234,23 @@ public: | |||
| protected: | |||
| friend class DrawableComposite; | |||
| friend class DrawableShape; | |||
| /** @internal */ | |||
| DrawableComposite* parent; | |||
| static Drawable* createChildFromValueTree (DrawableComposite* parent, const ValueTree& tree, ImageProvider* imageProvider); | |||
| /** @internal */ | |||
| virtual void invalidatePoints() = 0; | |||
| void transformContextToCorrectOrigin (Graphics& g); | |||
| /** @internal */ | |||
| static Drawable* createChildFromValueTree (DrawableComposite* parent, const ValueTree& tree, ImageProvider* imageProvider); | |||
| void markerHasMoved(); | |||
| /** @internal */ | |||
| void parentHierarchyChanged(); | |||
| /** @internal */ | |||
| void setBoundsToEnclose (const Rectangle<float>& area); | |||
| Point<int> originRelativeToComponent; | |||
| private: | |||
| String name; | |||
| void nonConstDraw (Graphics& g, float opacity, const AffineTransform& transform); | |||
| Drawable (const Drawable&); | |||
| Drawable& operator= (const Drawable&); | |||
| @@ -36,7 +36,8 @@ BEGIN_JUCE_NAMESPACE | |||
| //============================================================================== | |||
| DrawableComposite::DrawableComposite() | |||
| : bounds (Point<float>(), Point<float> (100.0f, 0.0f), Point<float> (0.0f, 100.0f)) | |||
| : bounds (Point<float>(), Point<float> (100.0f, 0.0f), Point<float> (0.0f, 100.0f)), | |||
| updateBoundsReentrant (false) | |||
| { | |||
| setContentArea (RelativeRectangle (RelativeCoordinate (0.0), | |||
| RelativeCoordinate (100.0), | |||
| @@ -48,8 +49,8 @@ DrawableComposite::DrawableComposite (const DrawableComposite& other) | |||
| { | |||
| bounds = other.bounds; | |||
| for (int i = 0; i < other.drawables.size(); ++i) | |||
| drawables.add (other.drawables.getUnchecked(i)->createCopy()); | |||
| for (int i = 0; i < other.getNumDrawables(); ++i) | |||
| insertDrawable (other.getDrawable(i)->createCopy()); | |||
| markersX.addCopiesOf (other.markersX); | |||
| markersY.addCopiesOf (other.markersY); | |||
| @@ -57,18 +58,24 @@ DrawableComposite::DrawableComposite (const DrawableComposite& other) | |||
| DrawableComposite::~DrawableComposite() | |||
| { | |||
| deleteAllChildren(); | |||
| } | |||
| //============================================================================== | |||
| int DrawableComposite::getNumDrawables() const throw() | |||
| { | |||
| return getNumChildComponents(); | |||
| } | |||
| Drawable* DrawableComposite::getDrawable (int index) const | |||
| { | |||
| return dynamic_cast <Drawable*> (getChildComponent (index)); | |||
| } | |||
| void DrawableComposite::insertDrawable (Drawable* drawable, const int index) | |||
| { | |||
| if (drawable != 0) | |||
| { | |||
| jassert (! drawables.contains (drawable)); // trying to add a drawable that's already in here! | |||
| jassert (drawable->parent == 0); // A drawable can only live inside one parent at a time! | |||
| drawables.insert (index, drawable); | |||
| drawable->parent = this; | |||
| } | |||
| addAndMakeVisible (drawable, index); | |||
| } | |||
| void DrawableComposite::insertDrawable (const Drawable& drawable, const int index) | |||
| @@ -78,50 +85,60 @@ void DrawableComposite::insertDrawable (const Drawable& drawable, const int inde | |||
| void DrawableComposite::removeDrawable (const int index, const bool deleteDrawable) | |||
| { | |||
| drawables.remove (index, deleteDrawable); | |||
| Drawable* const d = getDrawable (index); | |||
| if (deleteDrawable) | |||
| delete d; | |||
| else | |||
| removeChildComponent (d); | |||
| } | |||
| Drawable* DrawableComposite::getDrawableWithName (const String& name) const throw() | |||
| { | |||
| for (int i = drawables.size(); --i >= 0;) | |||
| if (drawables.getUnchecked(i)->getName() == name) | |||
| return drawables.getUnchecked(i); | |||
| for (int i = getNumChildComponents(); --i >= 0;) | |||
| if (getChildComponent(i)->getName() == name) | |||
| return getDrawable (i); | |||
| return 0; | |||
| } | |||
| void DrawableComposite::bringToFront (const int index) | |||
| { | |||
| if (index >= 0 && index < drawables.size() - 1) | |||
| drawables.move (index, -1); | |||
| Drawable* d = getDrawable (index); | |||
| if (d != 0) | |||
| d->toFront (false); | |||
| } | |||
| void DrawableComposite::setBoundingBox (const RelativeParallelogram& newBoundingBox) | |||
| const Rectangle<float> DrawableComposite::getDrawableBounds() const | |||
| { | |||
| bounds = newBoundingBox; | |||
| } | |||
| Rectangle<float> r; | |||
| //============================================================================== | |||
| DrawableComposite::Marker::Marker (const DrawableComposite::Marker& other) | |||
| : name (other.name), position (other.position) | |||
| { | |||
| } | |||
| for (int i = getNumDrawables(); --i >= 0;) | |||
| { | |||
| Drawable* const d = getDrawable(i); | |||
| DrawableComposite::Marker::Marker (const String& name_, const RelativeCoordinate& position_) | |||
| : name (name_), position (position_) | |||
| { | |||
| if (d != 0) | |||
| { | |||
| if (d->isTransformed()) | |||
| r = r.getUnion (d->getDrawableBounds().transformed (d->getTransform())); | |||
| else | |||
| r = r.getUnion (d->getDrawableBounds()); | |||
| } | |||
| } | |||
| return r; | |||
| } | |||
| bool DrawableComposite::Marker::operator!= (const DrawableComposite::Marker& other) const throw() | |||
| void DrawableComposite::markerHasMoved() | |||
| { | |||
| return name != other.name || position != other.position; | |||
| } | |||
| for (int i = getNumDrawables(); --i >= 0;) | |||
| { | |||
| Drawable* const d = getDrawable(i); | |||
| //============================================================================== | |||
| const char* const DrawableComposite::contentLeftMarkerName = "left"; | |||
| const char* const DrawableComposite::contentRightMarkerName = "right"; | |||
| const char* const DrawableComposite::contentTopMarkerName = "top"; | |||
| const char* const DrawableComposite::contentBottomMarkerName = "bottom"; | |||
| if (d != 0) | |||
| d->markerHasMoved(); | |||
| } | |||
| } | |||
| const RelativeRectangle DrawableComposite::getContentArea() const | |||
| { | |||
| @@ -138,6 +155,13 @@ void DrawableComposite::setContentArea (const RelativeRectangle& newArea) | |||
| setMarker (contentRightMarkerName, true, newArea.right); | |||
| setMarker (contentTopMarkerName, false, newArea.top); | |||
| setMarker (contentBottomMarkerName, false, newArea.bottom); | |||
| refreshTransformFromBounds(); | |||
| } | |||
| void DrawableComposite::setBoundingBox (const RelativeParallelogram& newBoundingBox) | |||
| { | |||
| bounds = newBoundingBox; | |||
| refreshTransformFromBounds(); | |||
| } | |||
| void DrawableComposite::resetBoundingBoxToContentArea() | |||
| @@ -151,15 +175,111 @@ void DrawableComposite::resetBoundingBoxToContentArea() | |||
| void DrawableComposite::resetContentAreaAndBoundingBoxToFitChildren() | |||
| { | |||
| const Rectangle<float> bounds (getUntransformedBounds (false)); | |||
| const Rectangle<float> activeArea (getDrawableBounds()); | |||
| setContentArea (RelativeRectangle (RelativeCoordinate (bounds.getX()), | |||
| RelativeCoordinate (bounds.getRight()), | |||
| RelativeCoordinate (bounds.getY()), | |||
| RelativeCoordinate (bounds.getBottom()))); | |||
| setContentArea (RelativeRectangle (RelativeCoordinate (activeArea.getX()), | |||
| RelativeCoordinate (activeArea.getRight()), | |||
| RelativeCoordinate (activeArea.getY()), | |||
| RelativeCoordinate (activeArea.getBottom()))); | |||
| resetBoundingBoxToContentArea(); | |||
| } | |||
| void DrawableComposite::refreshTransformFromBounds() | |||
| { | |||
| Point<float> resolved[3]; | |||
| bounds.resolveThreePoints (resolved, getParent()); | |||
| const Rectangle<float> content (getContentArea().resolve (getParent())); | |||
| AffineTransform t (AffineTransform::fromTargetPoints (content.getX(), content.getY(), resolved[0].getX(), resolved[0].getY(), | |||
| content.getRight(), content.getY(), resolved[1].getX(), resolved[1].getY(), | |||
| content.getX(), content.getBottom(), resolved[2].getX(), resolved[2].getY())); | |||
| if (! t.isSingularity()) | |||
| setTransform (t); | |||
| } | |||
| void DrawableComposite::parentHierarchyChanged() | |||
| { | |||
| DrawableComposite* parent = getParent(); | |||
| if (parent != 0) | |||
| originRelativeToComponent = parent->originRelativeToComponent - getPosition(); | |||
| } | |||
| void DrawableComposite::childBoundsChanged (Component*) | |||
| { | |||
| updateBoundsToFitChildren(); | |||
| } | |||
| void DrawableComposite::childrenChanged() | |||
| { | |||
| updateBoundsToFitChildren(); | |||
| } | |||
| void DrawableComposite::updateBoundsToFitChildren() | |||
| { | |||
| if (! updateBoundsReentrant) | |||
| { | |||
| struct RentrancyCheckSetter | |||
| { | |||
| RentrancyCheckSetter (bool& b_) : b (b_) { b_ = true; } | |||
| ~RentrancyCheckSetter() { b = false; } | |||
| private: | |||
| bool& b; | |||
| }; | |||
| const RentrancyCheckSetter checkSetter (updateBoundsReentrant); | |||
| Rectangle<int> childArea; | |||
| for (int i = getNumChildComponents(); --i >= 0;) | |||
| childArea = childArea.getUnion (getChildComponent(i)->getBoundsInParent()); | |||
| const Point<int> delta (childArea.getPosition()); | |||
| childArea += getPosition(); | |||
| if (childArea != getBounds()) | |||
| { | |||
| if (! delta.isOrigin()) | |||
| { | |||
| originRelativeToComponent -= delta; | |||
| for (int i = getNumChildComponents(); --i >= 0;) | |||
| { | |||
| Component* const c = getChildComponent(i); | |||
| if (c != 0) | |||
| c->setBounds (c->getBounds() - delta); | |||
| } | |||
| } | |||
| setBounds (childArea); | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| const char* const DrawableComposite::contentLeftMarkerName = "left"; | |||
| const char* const DrawableComposite::contentRightMarkerName = "right"; | |||
| const char* const DrawableComposite::contentTopMarkerName = "top"; | |||
| const char* const DrawableComposite::contentBottomMarkerName = "bottom"; | |||
| DrawableComposite::Marker::Marker (const DrawableComposite::Marker& other) | |||
| : name (other.name), position (other.position) | |||
| { | |||
| } | |||
| DrawableComposite::Marker::Marker (const String& name_, const RelativeCoordinate& position_) | |||
| : name (name_), position (position_) | |||
| { | |||
| } | |||
| bool DrawableComposite::Marker::operator!= (const DrawableComposite::Marker& other) const throw() | |||
| { | |||
| return name != other.name || position != other.position; | |||
| } | |||
| int DrawableComposite::getNumMarkers (const bool xAxis) const throw() | |||
| { | |||
| return (xAxis ? markersX : markersY).size(); | |||
| @@ -182,7 +302,7 @@ void DrawableComposite::setMarker (const String& name, const bool xAxis, const R | |||
| if (m->position != position) | |||
| { | |||
| m->position = position; | |||
| invalidatePoints(); | |||
| markerHasMoved(); | |||
| } | |||
| return; | |||
| @@ -190,7 +310,7 @@ void DrawableComposite::setMarker (const String& name, const bool xAxis, const R | |||
| } | |||
| (xAxis ? markersX : markersY).add (new Marker (name, position)); | |||
| invalidatePoints(); | |||
| markerHasMoved(); | |||
| } | |||
| void DrawableComposite::removeMarker (const bool xAxis, const int index) | |||
| @@ -202,55 +322,6 @@ void DrawableComposite::removeMarker (const bool xAxis, const int index) | |||
| } | |||
| //============================================================================== | |||
| const AffineTransform DrawableComposite::calculateTransform() const | |||
| { | |||
| Point<float> resolved[3]; | |||
| bounds.resolveThreePoints (resolved, parent); | |||
| const Rectangle<float> content (getContentArea().resolve (parent)); | |||
| return AffineTransform::fromTargetPoints (content.getX(), content.getY(), resolved[0].getX(), resolved[0].getY(), | |||
| content.getRight(), content.getY(), resolved[1].getX(), resolved[1].getY(), | |||
| content.getX(), content.getBottom(), resolved[2].getX(), resolved[2].getY()); | |||
| } | |||
| void DrawableComposite::render (const Drawable::RenderingContext& context) const | |||
| { | |||
| if (drawables.size() > 0 && context.opacity > 0) | |||
| { | |||
| if (context.opacity >= 1.0f || drawables.size() == 1) | |||
| { | |||
| Drawable::RenderingContext contextCopy (context); | |||
| contextCopy.transform = calculateTransform().followedBy (context.transform); | |||
| for (int i = 0; i < drawables.size(); ++i) | |||
| drawables.getUnchecked(i)->render (contextCopy); | |||
| } | |||
| else | |||
| { | |||
| // To correctly render a whole composite layer with an overall transparency, | |||
| // we need to render everything opaquely into a temp buffer, then blend that | |||
| // with the target opacity... | |||
| const Rectangle<int> clipBounds (context.g.getClipBounds()); | |||
| if (! clipBounds.isEmpty()) | |||
| { | |||
| Image tempImage (Image::ARGB, clipBounds.getWidth(), clipBounds.getHeight(), true); | |||
| { | |||
| Graphics tempG (tempImage); | |||
| tempG.setOrigin (-clipBounds.getX(), -clipBounds.getY()); | |||
| Drawable::RenderingContext tempContext (tempG, context.transform, 1.0f); | |||
| render (tempContext); | |||
| } | |||
| context.g.setOpacity (context.opacity); | |||
| context.g.drawImageAt (tempImage, clipBounds.getX(), clipBounds.getY()); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| const Expression DrawableComposite::getSymbolValue (const String& symbol, const String& member) const | |||
| { | |||
| jassert (member.isEmpty()) // the only symbols available in a Drawable are markers. | |||
| @@ -273,99 +344,11 @@ const Expression DrawableComposite::getSymbolValue (const String& symbol, const | |||
| throw Expression::EvaluationError (symbol, member); | |||
| } | |||
| const Rectangle<float> DrawableComposite::getUntransformedBounds (const bool includeMarkers) const | |||
| { | |||
| Rectangle<float> bounds; | |||
| int i; | |||
| for (i = 0; i < drawables.size(); ++i) | |||
| bounds = bounds.getUnion (drawables.getUnchecked(i)->getBounds()); | |||
| if (includeMarkers) | |||
| { | |||
| if (markersX.size() > 0) | |||
| { | |||
| float minX = std::numeric_limits<float>::max(); | |||
| float maxX = std::numeric_limits<float>::min(); | |||
| for (i = markersX.size(); --i >= 0;) | |||
| { | |||
| const Marker* m = markersX.getUnchecked(i); | |||
| const float pos = (float) m->position.resolve (this); | |||
| minX = jmin (minX, pos); | |||
| maxX = jmax (maxX, pos); | |||
| } | |||
| if (minX <= maxX) | |||
| { | |||
| if (bounds.getHeight() > 0) | |||
| { | |||
| minX = jmin (minX, bounds.getX()); | |||
| maxX = jmax (maxX, bounds.getRight()); | |||
| } | |||
| bounds.setLeft (minX); | |||
| bounds.setWidth (maxX - minX); | |||
| } | |||
| } | |||
| if (markersY.size() > 0) | |||
| { | |||
| float minY = std::numeric_limits<float>::max(); | |||
| float maxY = std::numeric_limits<float>::min(); | |||
| for (i = markersY.size(); --i >= 0;) | |||
| { | |||
| const Marker* m = markersY.getUnchecked(i); | |||
| const float pos = (float) m->position.resolve (this); | |||
| minY = jmin (minY, pos); | |||
| maxY = jmax (maxY, pos); | |||
| } | |||
| if (minY <= maxY) | |||
| { | |||
| if (bounds.getHeight() > 0) | |||
| { | |||
| minY = jmin (minY, bounds.getY()); | |||
| maxY = jmax (maxY, bounds.getBottom()); | |||
| } | |||
| bounds.setTop (minY); | |||
| bounds.setHeight (maxY - minY); | |||
| } | |||
| } | |||
| } | |||
| return bounds; | |||
| } | |||
| const Rectangle<float> DrawableComposite::getBounds() const | |||
| { | |||
| return getUntransformedBounds (true).transformed (calculateTransform()); | |||
| } | |||
| bool DrawableComposite::hitTest (float x, float y) const | |||
| { | |||
| calculateTransform().inverted().transformPoint (x, y); | |||
| for (int i = 0; i < drawables.size(); ++i) | |||
| if (drawables.getUnchecked(i)->hitTest (x, y)) | |||
| return true; | |||
| return false; | |||
| } | |||
| Drawable* DrawableComposite::createCopy() const | |||
| { | |||
| return new DrawableComposite (*this); | |||
| } | |||
| void DrawableComposite::invalidatePoints() | |||
| { | |||
| for (int i = 0; i < drawables.size(); ++i) | |||
| drawables.getUnchecked(i)->invalidatePoints(); | |||
| } | |||
| //============================================================================== | |||
| const Identifier DrawableComposite::valueTreeType ("Group"); | |||
| @@ -564,21 +547,14 @@ void DrawableComposite::ValueTreeWrapper::removeMarker (bool xAxis, const ValueT | |||
| } | |||
| //============================================================================== | |||
| const Rectangle<float> DrawableComposite::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) | |||
| void DrawableComposite::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) | |||
| { | |||
| const ValueTreeWrapper wrapper (tree); | |||
| setName (wrapper.getID()); | |||
| Rectangle<float> damage; | |||
| bool redrawAll = false; | |||
| const RelativeParallelogram newBounds (wrapper.getBoundingBox()); | |||
| if (bounds != newBounds) | |||
| { | |||
| redrawAll = true; | |||
| damage = getBounds(); | |||
| bounds = newBounds; | |||
| } | |||
| const int numMarkersX = wrapper.getNumMarkers (true); | |||
| const int numMarkersY = wrapper.getNumMarkers (false); | |||
| @@ -586,12 +562,6 @@ const Rectangle<float> DrawableComposite::refreshFromValueTree (const ValueTree& | |||
| // Remove deleted markers... | |||
| if (markersX.size() > numMarkersX || markersY.size() > numMarkersY) | |||
| { | |||
| if (! redrawAll) | |||
| { | |||
| redrawAll = true; | |||
| damage = getBounds(); | |||
| } | |||
| markersX.removeRange (jmax (2, numMarkersX), markersX.size()); | |||
| markersY.removeRange (jmax (2, numMarkersY), markersY.size()); | |||
| } | |||
| @@ -603,19 +573,10 @@ const Rectangle<float> DrawableComposite::refreshFromValueTree (const ValueTree& | |||
| const Marker newMarker (wrapper.getMarker (true, wrapper.getMarkerState (true, i))); | |||
| Marker* m = markersX[i]; | |||
| if (m == 0 || newMarker != *m) | |||
| { | |||
| if (! redrawAll) | |||
| { | |||
| redrawAll = true; | |||
| damage = getBounds(); | |||
| } | |||
| if (m == 0) | |||
| markersX.add (new Marker (newMarker)); | |||
| else | |||
| *m = newMarker; | |||
| } | |||
| if (m == 0) | |||
| markersX.add (new Marker (newMarker)); | |||
| else if (newMarker != *m) | |||
| *m = newMarker; | |||
| } | |||
| for (i = 0; i < numMarkersY; ++i) | |||
| @@ -623,78 +584,43 @@ const Rectangle<float> DrawableComposite::refreshFromValueTree (const ValueTree& | |||
| const Marker newMarker (wrapper.getMarker (false, wrapper.getMarkerState (false, i))); | |||
| Marker* m = markersY[i]; | |||
| if (m == 0 || newMarker != *m) | |||
| { | |||
| if (! redrawAll) | |||
| { | |||
| redrawAll = true; | |||
| damage = getBounds(); | |||
| } | |||
| if (m == 0) | |||
| markersY.add (new Marker (newMarker)); | |||
| else | |||
| *m = newMarker; | |||
| } | |||
| if (m == 0) | |||
| markersY.add (new Marker (newMarker)); | |||
| else if (newMarker != *m) | |||
| *m = newMarker; | |||
| } | |||
| // Remove deleted drawables.. | |||
| for (i = drawables.size(); --i >= wrapper.getNumDrawables();) | |||
| { | |||
| Drawable* const d = drawables.getUnchecked(i); | |||
| if (! redrawAll) | |||
| damage = damage.getUnion (d->getBounds()); | |||
| d->parent = 0; | |||
| drawables.remove (i); | |||
| } | |||
| for (i = getNumDrawables(); --i >= wrapper.getNumDrawables();) | |||
| delete getDrawable(i); | |||
| // Update drawables and add new ones.. | |||
| for (i = 0; i < wrapper.getNumDrawables(); ++i) | |||
| { | |||
| const ValueTree newDrawable (wrapper.getDrawableState (i)); | |||
| Drawable* d = drawables[i]; | |||
| Drawable* d = getDrawable(i); | |||
| if (d != 0) | |||
| { | |||
| if (newDrawable.hasType (d->getValueTreeType())) | |||
| { | |||
| const Rectangle<float> area (d->refreshFromValueTree (newDrawable, imageProvider)); | |||
| if (! redrawAll) | |||
| damage = damage.getUnion (area); | |||
| d->refreshFromValueTree (newDrawable, imageProvider); | |||
| } | |||
| else | |||
| { | |||
| if (! redrawAll) | |||
| damage = damage.getUnion (d->getBounds()); | |||
| d = createChildFromValueTree (this, newDrawable, imageProvider); | |||
| drawables.set (i, d); | |||
| if (! redrawAll) | |||
| damage = damage.getUnion (d->getBounds()); | |||
| delete d; | |||
| d = 0; | |||
| } | |||
| } | |||
| else | |||
| if (d == 0) | |||
| { | |||
| d = createChildFromValueTree (this, newDrawable, imageProvider); | |||
| drawables.set (i, d); | |||
| if (! redrawAll) | |||
| damage = damage.getUnion (d->getBounds()); | |||
| addAndMakeVisible (d, i); | |||
| } | |||
| } | |||
| invalidatePoints(); | |||
| if (redrawAll) | |||
| damage = damage.getUnion (getBounds()); | |||
| else if (! damage.isEmpty()) | |||
| damage = damage.transformed (calculateTransform()); | |||
| return damage; | |||
| refreshTransformFromBounds(); | |||
| } | |||
| const ValueTree DrawableComposite::createValueTree (ImageProvider* imageProvider) const | |||
| @@ -706,8 +632,8 @@ const ValueTree DrawableComposite::createValueTree (ImageProvider* imageProvider | |||
| v.setBoundingBox (bounds, 0); | |||
| int i; | |||
| for (i = 0; i < drawables.size(); ++i) | |||
| v.addDrawable (drawables.getUnchecked(i)->createValueTree (imageProvider), -1, 0); | |||
| for (i = 0; i < getNumDrawables(); ++i) | |||
| v.addDrawable (getDrawable(i)->createValueTree (imageProvider), -1, 0); | |||
| for (i = 0; i < markersX.size(); ++i) | |||
| v.setMarker (true, *markersX.getUnchecked(i), 0); | |||
| @@ -93,7 +93,7 @@ public: | |||
| @see getDrawable | |||
| */ | |||
| int getNumDrawables() const throw() { return drawables.size(); } | |||
| int getNumDrawables() const throw(); | |||
| /** Returns one of the drawables that are contained in this one. | |||
| @@ -105,7 +105,7 @@ public: | |||
| @see getNumDrawables | |||
| */ | |||
| Drawable* getDrawable (int index) const throw() { return drawables [index]; } | |||
| Drawable* getDrawable (int index) const; | |||
| /** Looks for a child drawable with the specified name. */ | |||
| Drawable* getDrawableWithName (const String& name) const throw(); | |||
| @@ -119,20 +119,6 @@ public: | |||
| void bringToFront (int index); | |||
| //============================================================================== | |||
| /** 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 | |||
| */ | |||
| const RelativeRectangle getContentArea() const; | |||
| /** Changes the main content area. | |||
| The content area is actually defined by the markers named "left", "right", "top" and | |||
| "bottom", but this method is a shortcut that sets them all at once. | |||
| @see setBoundingBox, contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName | |||
| */ | |||
| void setContentArea (const RelativeRectangle& newArea); | |||
| /** Sets the parallelogram that defines the target position of the content rectangle when the drawable is rendered. | |||
| @see setContentArea | |||
| */ | |||
| @@ -148,6 +134,20 @@ 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 | |||
| */ | |||
| const RelativeRectangle getContentArea() const; | |||
| /** Changes the main content area. | |||
| The content area is actually defined by the markers named "left", "right", "top" and | |||
| "bottom", but this method is a shortcut that sets them all at once. | |||
| @see setBoundingBox, contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName | |||
| */ | |||
| void setContentArea (const RelativeRectangle& newArea); | |||
| /** Resets the content area and the bounding transform to fit around the area occupied | |||
| by the child components (ignoring any markers). | |||
| */ | |||
| @@ -183,17 +183,9 @@ public: | |||
| //============================================================================== | |||
| /** @internal */ | |||
| void render (const Drawable::RenderingContext& context) const; | |||
| /** @internal */ | |||
| const Rectangle<float> getBounds() const; | |||
| /** @internal */ | |||
| bool hitTest (float x, float y) const; | |||
| /** @internal */ | |||
| Drawable* createCopy() const; | |||
| /** @internal */ | |||
| void invalidatePoints(); | |||
| /** @internal */ | |||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||
| void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||
| /** @internal */ | |||
| const ValueTree createValueTree (ImageProvider* imageProvider) const; | |||
| /** @internal */ | |||
| @@ -202,6 +194,16 @@ public: | |||
| const Identifier getValueTreeType() const { return valueTreeType; } | |||
| /** @internal */ | |||
| const Expression getSymbolValue (const String& symbol, const String& member) const; | |||
| /** @internal */ | |||
| const Rectangle<float> getDrawableBounds() const; | |||
| /** @internal */ | |||
| void markerHasMoved(); | |||
| /** @internal */ | |||
| void childBoundsChanged (Component*); | |||
| /** @internal */ | |||
| void childrenChanged(); | |||
| /** @internal */ | |||
| void parentHierarchyChanged(); | |||
| //============================================================================== | |||
| /** Internally-used class for wrapping a DrawableComposite's state into a ValueTree. */ | |||
| @@ -248,12 +250,12 @@ public: | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| OwnedArray <Drawable> drawables; | |||
| RelativeParallelogram bounds; | |||
| OwnedArray <Marker> markersX, markersY; | |||
| bool updateBoundsReentrant; | |||
| const Rectangle<float> getUntransformedBounds (bool includeMarkers) const; | |||
| const AffineTransform calculateTransform() const; | |||
| void refreshTransformFromBounds(); | |||
| void updateBoundsToFitChildren(); | |||
| DrawableComposite& operator= (const DrawableComposite&); | |||
| }; | |||
| @@ -59,12 +59,16 @@ void DrawableImage::setImage (const Image& imageToUse) | |||
| { | |||
| image = imageToUse; | |||
| setBounds (imageToUse.getBounds()); | |||
| if (image.isValid()) | |||
| { | |||
| 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())); | |||
| } | |||
| refreshTransformFromBounds(); | |||
| } | |||
| void DrawableImage::setOpacity (const float newOpacity) | |||
| @@ -80,68 +84,57 @@ void DrawableImage::setOverlayColour (const Colour& newOverlayColour) | |||
| void DrawableImage::setBoundingBox (const RelativeParallelogram& newBounds) | |||
| { | |||
| bounds = newBounds; | |||
| refreshTransformFromBounds(); | |||
| } | |||
| //============================================================================== | |||
| const AffineTransform DrawableImage::calculateTransform() const | |||
| void DrawableImage::refreshTransformFromBounds() | |||
| { | |||
| if (image.isNull()) | |||
| return AffineTransform::identity; | |||
| if (! image.isNull()) | |||
| { | |||
| Point<float> resolved[3]; | |||
| bounds.resolveThreePoints (resolved, getParent()); | |||
| Point<float> resolved[3]; | |||
| bounds.resolveThreePoints (resolved, parent); | |||
| 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()); | |||
| 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].getX(), resolved[0].getY(), | |||
| tr.getX(), tr.getY(), | |||
| bl.getX(), bl.getY())); | |||
| return AffineTransform::fromTargetPoints (resolved[0].getX(), resolved[0].getY(), | |||
| tr.getX(), tr.getY(), | |||
| bl.getX(), bl.getY()); | |||
| if (! t.isSingularity()) | |||
| setTransform (t); | |||
| } | |||
| } | |||
| void DrawableImage::render (const Drawable::RenderingContext& context) const | |||
| //============================================================================== | |||
| void DrawableImage::paint (Graphics& g) | |||
| { | |||
| if (image.isValid()) | |||
| { | |||
| const AffineTransform t (calculateTransform().followedBy (context.transform)); | |||
| if (opacity > 0.0f && ! overlayColour.isOpaque()) | |||
| { | |||
| context.g.setOpacity (context.opacity * opacity); | |||
| context.g.drawImageTransformed (image, t, false); | |||
| g.setOpacity (opacity); | |||
| g.drawImageAt (image, 0, 0, false); | |||
| } | |||
| if (! overlayColour.isTransparent()) | |||
| { | |||
| context.g.setColour (overlayColour.withMultipliedAlpha (context.opacity)); | |||
| context.g.drawImageTransformed (image, t, true); | |||
| g.setColour (overlayColour.withMultipliedAlpha (opacity)); | |||
| g.drawImageAt (image, 0, 0, true); | |||
| } | |||
| } | |||
| } | |||
| const Rectangle<float> DrawableImage::getBounds() const | |||
| const Rectangle<float> DrawableImage::getDrawableBounds() const | |||
| { | |||
| if (image.isNull()) | |||
| return Rectangle<float>(); | |||
| return bounds.getBounds (parent); | |||
| return image.getBounds().toFloat(); | |||
| } | |||
| bool DrawableImage::hitTest (float x, float y) const | |||
| bool DrawableImage::hitTest (int x, int y) const | |||
| { | |||
| if (image.isNull()) | |||
| return false; | |||
| calculateTransform().inverted().transformPoint (x, y); | |||
| const int ix = roundToInt (x); | |||
| const int iy = roundToInt (y); | |||
| return ix >= 0 | |||
| && iy >= 0 | |||
| && ix < image.getWidth() | |||
| && iy < image.getHeight() | |||
| && image.getPixelAt (ix, iy).getAlpha() >= 127; | |||
| return (! image.isNull()) | |||
| && image.getPixelAt (x, y).getAlpha() >= 127; | |||
| } | |||
| Drawable* DrawableImage::createCopy() const | |||
| @@ -149,10 +142,6 @@ Drawable* DrawableImage::createCopy() const | |||
| return new DrawableImage (*this); | |||
| } | |||
| void DrawableImage::invalidatePoints() | |||
| { | |||
| } | |||
| //============================================================================== | |||
| const Identifier DrawableImage::valueTreeType ("Image"); | |||
| @@ -237,7 +226,7 @@ void DrawableImage::ValueTreeWrapper::setBoundingBox (const RelativeParallelogra | |||
| //============================================================================== | |||
| const Rectangle<float> DrawableImage::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) | |||
| void DrawableImage::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) | |||
| { | |||
| const ValueTreeWrapper controller (tree); | |||
| setName (controller.getID()); | |||
| @@ -255,19 +244,14 @@ const Rectangle<float> DrawableImage::refreshFromValueTree (const ValueTree& tre | |||
| const RelativeParallelogram newBounds (controller.getBoundingBox()); | |||
| if (newOpacity != opacity || overlayColour != newOverlayColour || image != newImage || bounds != newBounds) | |||
| if (newOpacity != opacity || overlayColour != newOverlayColour || image != newImage) | |||
| { | |||
| const Rectangle<float> damage (getBounds()); | |||
| repaint(); | |||
| opacity = newOpacity; | |||
| overlayColour = newOverlayColour; | |||
| bounds = newBounds; | |||
| image = newImage; | |||
| return damage.getUnion (getBounds()); | |||
| setImage (newImage); | |||
| } | |||
| return Rectangle<float>(); | |||
| } | |||
| const ValueTree DrawableImage::createValueTree (ImageProvider* imageProvider) const | |||
| @@ -84,17 +84,15 @@ public: | |||
| //============================================================================== | |||
| /** @internal */ | |||
| void render (const Drawable::RenderingContext& context) const; | |||
| void paint (Graphics& g); | |||
| /** @internal */ | |||
| const Rectangle<float> getBounds() const; | |||
| /** @internal */ | |||
| bool hitTest (float x, float y) const; | |||
| bool hitTest (int x, int y) const; | |||
| /** @internal */ | |||
| Drawable* createCopy() const; | |||
| /** @internal */ | |||
| void invalidatePoints(); | |||
| const Rectangle<float> getDrawableBounds() const; | |||
| /** @internal */ | |||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||
| void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||
| /** @internal */ | |||
| const ValueTree createValueTree (ImageProvider* imageProvider) const; | |||
| /** @internal */ | |||
| @@ -136,7 +134,7 @@ private: | |||
| Colour overlayColour; | |||
| RelativeParallelogram bounds; | |||
| const AffineTransform calculateTransform() const; | |||
| void refreshTransformFromBounds(); | |||
| DrawableImage& operator= (const DrawableImage&); | |||
| }; | |||
| @@ -42,7 +42,7 @@ DrawablePath::DrawablePath (const DrawablePath& other) | |||
| if (other.relativePath != 0) | |||
| relativePath = new RelativePointPath (*other.relativePath); | |||
| else | |||
| cachedPath = other.cachedPath; | |||
| setPath (other.path); | |||
| } | |||
| DrawablePath::~DrawablePath() | |||
| @@ -57,18 +57,18 @@ Drawable* DrawablePath::createCopy() const | |||
| //============================================================================== | |||
| void DrawablePath::setPath (const Path& newPath) | |||
| { | |||
| cachedPath = newPath; | |||
| strokeChanged(); | |||
| path = newPath; | |||
| pathChanged(); | |||
| } | |||
| const Path& DrawablePath::getPath() const | |||
| { | |||
| return getCachedPath(); | |||
| return path; | |||
| } | |||
| const Path& DrawablePath::getStrokePath() const | |||
| { | |||
| return getCachedStrokePath(); | |||
| return strokePath; | |||
| } | |||
| bool DrawablePath::rebuildPath (Path& path) const | |||
| @@ -76,7 +76,7 @@ bool DrawablePath::rebuildPath (Path& path) const | |||
| if (relativePath != 0) | |||
| { | |||
| path.clear(); | |||
| relativePath->createPath (path, parent); | |||
| relativePath->createPath (path, getParent()); | |||
| return true; | |||
| } | |||
| @@ -440,38 +440,31 @@ void DrawablePath::ValueTreeWrapper::Element::removePoint (UndoManager* undoMana | |||
| } | |||
| //============================================================================== | |||
| const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) | |||
| void DrawablePath::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) | |||
| { | |||
| Rectangle<float> damageRect; | |||
| ValueTreeWrapper v (tree); | |||
| setName (v.getID()); | |||
| bool needsRedraw = refreshFillTypes (v, parent, imageProvider); | |||
| if (refreshFillTypes (v, getParent(), imageProvider)) | |||
| repaint(); | |||
| ScopedPointer<RelativePointPath> newRelativePath (new RelativePointPath (tree)); | |||
| Path newPath; | |||
| newRelativePath->createPath (newPath, parent); | |||
| newRelativePath->createPath (newPath, getParent()); | |||
| if (! newRelativePath->containsAnyDynamicPoints()) | |||
| newRelativePath = 0; | |||
| const PathStrokeType newStroke (v.getStrokeType()); | |||
| if (strokeType != newStroke || cachedPath != newPath) | |||
| if (strokeType != newStroke || path != newPath) | |||
| { | |||
| damageRect = getBounds(); | |||
| cachedPath.swapWithPath (newPath); | |||
| strokeChanged(); | |||
| path.swapWithPath (newPath); | |||
| strokeType = newStroke; | |||
| needsRedraw = true; | |||
| pathChanged(); | |||
| } | |||
| relativePath = newRelativePath; | |||
| if (needsRedraw) | |||
| damageRect = damageRect.getUnion (getBounds()); | |||
| return damageRect; | |||
| } | |||
| const ValueTree DrawablePath::createValueTree (ImageProvider* imageProvider) const | |||
| @@ -488,7 +481,7 @@ const ValueTree DrawablePath::createValueTree (ImageProvider* imageProvider) con | |||
| } | |||
| else | |||
| { | |||
| RelativePointPath rp (getCachedPath()); | |||
| RelativePointPath rp (path); | |||
| rp.writeTo (tree, 0); | |||
| } | |||
| @@ -64,7 +64,7 @@ public: | |||
| /** @internal */ | |||
| Drawable* createCopy() const; | |||
| /** @internal */ | |||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||
| void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||
| /** @internal */ | |||
| const ValueTree createValueTree (ImageProvider* imageProvider) const; | |||
| /** @internal */ | |||
| @@ -68,13 +68,13 @@ void DrawableRectangle::setCornerSize (const RelativePoint& newSize) | |||
| bool DrawableRectangle::rebuildPath (Path& path) const | |||
| { | |||
| Point<float> points[3]; | |||
| bounds.resolveThreePoints (points, parent); | |||
| bounds.resolveThreePoints (points, getParent()); | |||
| const float w = Line<float> (points[0], points[1]).getLength(); | |||
| const float h = Line<float> (points[0], points[2]).getLength(); | |||
| const float cornerSizeX = (float) cornerSize.x.resolve (parent); | |||
| const float cornerSizeY = (float) cornerSize.y.resolve (parent); | |||
| const float cornerSizeX = (float) cornerSize.x.resolve (getParent()); | |||
| const float cornerSizeY = (float) cornerSize.y.resolve (getParent()); | |||
| path.clear(); | |||
| @@ -92,7 +92,7 @@ bool DrawableRectangle::rebuildPath (Path& path) const | |||
| const AffineTransform DrawableRectangle::calculateTransform() const | |||
| { | |||
| Point<float> resolved[3]; | |||
| bounds.resolveThreePoints (resolved, parent); | |||
| bounds.resolveThreePoints (resolved, getParent()); | |||
| return AffineTransform::fromTargetPoints (resolved[0].getX(), resolved[0].getY(), | |||
| resolved[1].getX(), resolved[1].getY(), | |||
| @@ -143,13 +143,13 @@ Value DrawableRectangle::ValueTreeWrapper::getCornerSizeValue (UndoManager* undo | |||
| } | |||
| //============================================================================== | |||
| const Rectangle<float> DrawableRectangle::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) | |||
| void DrawableRectangle::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) | |||
| { | |||
| Rectangle<float> damageRect; | |||
| ValueTreeWrapper v (tree); | |||
| setName (v.getID()); | |||
| bool needsRedraw = refreshFillTypes (v, parent, imageProvider); | |||
| if (refreshFillTypes (v, getParent(), imageProvider)) | |||
| repaint(); | |||
| RelativeParallelogram newBounds (v.getRectangle()); | |||
| @@ -158,19 +158,12 @@ const Rectangle<float> DrawableRectangle::refreshFromValueTree (const ValueTree& | |||
| if (strokeType != newStroke || newBounds != bounds || newCornerSize != cornerSize) | |||
| { | |||
| damageRect = getBounds(); | |||
| repaint(); | |||
| bounds = newBounds; | |||
| strokeType = newStroke; | |||
| cornerSize = newCornerSize; | |||
| pathChanged(); | |||
| strokeChanged(); | |||
| needsRedraw = true; | |||
| } | |||
| if (needsRedraw) | |||
| damageRect = damageRect.getUnion (getBounds()); | |||
| return damageRect; | |||
| } | |||
| const ValueTree DrawableRectangle::createValueTree (ImageProvider* imageProvider) const | |||
| @@ -64,7 +64,7 @@ public: | |||
| /** @internal */ | |||
| Drawable* createCopy() const; | |||
| /** @internal */ | |||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||
| void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||
| /** @internal */ | |||
| const ValueTree createValueTree (ImageProvider* imageProvider) const; | |||
| /** @internal */ | |||
| @@ -35,18 +35,14 @@ BEGIN_JUCE_NAMESPACE | |||
| DrawableShape::DrawableShape() | |||
| : strokeType (0.0f), | |||
| mainFill (Colours::black), | |||
| strokeFill (Colours::black), | |||
| pathNeedsUpdating (true), | |||
| strokeNeedsUpdating (true) | |||
| strokeFill (Colours::black) | |||
| { | |||
| } | |||
| DrawableShape::DrawableShape (const DrawableShape& other) | |||
| : strokeType (other.strokeType), | |||
| mainFill (other.mainFill), | |||
| strokeFill (other.strokeFill), | |||
| pathNeedsUpdating (true), | |||
| strokeNeedsUpdating (true) | |||
| strokeFill (other.strokeFill) | |||
| { | |||
| } | |||
| @@ -67,7 +63,7 @@ void DrawableShape::setStrokeFill (const FillType& newFill) | |||
| void DrawableShape::setStrokeType (const PathStrokeType& newStrokeType) | |||
| { | |||
| strokeType = newStrokeType; | |||
| strokeNeedsUpdating = true; | |||
| strokeChanged(); | |||
| } | |||
| void DrawableShape::setStrokeThickness (const float newThickness) | |||
| @@ -80,18 +76,6 @@ bool DrawableShape::isStrokeVisible() const throw() | |||
| return strokeType.getStrokeThickness() > 0.0f && ! strokeFill.isInvisible(); | |||
| } | |||
| void DrawableShape::setBrush (const Drawable::RenderingContext& context, const FillType& type) | |||
| { | |||
| FillType f (type); | |||
| if (f.isGradient()) | |||
| f.gradient->multiplyOpacity (context.opacity); | |||
| else | |||
| f.setOpacity (f.getOpacity() * context.opacity); | |||
| f.transform = f.transform.followedBy (context.transform); | |||
| context.g.setFillType (f); | |||
| } | |||
| bool DrawableShape::refreshFillTypes (const FillAndStrokeState& newState, | |||
| Expression::EvaluationContext* /*nameFinder*/, | |||
| ImageProvider* imageProvider) | |||
| @@ -99,7 +83,7 @@ bool DrawableShape::refreshFillTypes (const FillAndStrokeState& newState, | |||
| bool hasChanged = false; | |||
| { | |||
| const FillType f (newState.getMainFill (parent, imageProvider)); | |||
| const FillType f (newState.getMainFill (getParent(), imageProvider)); | |||
| if (mainFill != f) | |||
| { | |||
| @@ -109,7 +93,7 @@ bool DrawableShape::refreshFillTypes (const FillAndStrokeState& newState, | |||
| } | |||
| { | |||
| const FillType f (newState.getStrokeFill (parent, imageProvider)); | |||
| const FillType f (newState.getStrokeFill (getParent(), imageProvider)); | |||
| if (strokeFill != f) | |||
| { | |||
| @@ -129,73 +113,50 @@ void DrawableShape::writeTo (FillAndStrokeState& state, ImageProvider* imageProv | |||
| } | |||
| //============================================================================== | |||
| void DrawableShape::render (const Drawable::RenderingContext& context) const | |||
| void DrawableShape::paint (Graphics& g) | |||
| { | |||
| setBrush (context, mainFill); | |||
| context.g.fillPath (getCachedPath(), context.transform); | |||
| transformContextToCorrectOrigin (g); | |||
| g.setFillType (mainFill); | |||
| g.fillPath (path); | |||
| if (isStrokeVisible()) | |||
| { | |||
| setBrush (context, strokeFill); | |||
| context.g.fillPath (getCachedStrokePath(), context.transform); | |||
| g.setFillType (strokeFill); | |||
| g.fillPath (strokePath); | |||
| } | |||
| } | |||
| void DrawableShape::pathChanged() | |||
| { | |||
| pathNeedsUpdating = true; | |||
| strokeChanged(); | |||
| } | |||
| void DrawableShape::strokeChanged() | |||
| { | |||
| strokeNeedsUpdating = true; | |||
| } | |||
| strokePath.clear(); | |||
| strokeType.createStrokedPath (strokePath, path, AffineTransform::identity, 4.0f); | |||
| void DrawableShape::invalidatePoints() | |||
| { | |||
| pathNeedsUpdating = true; | |||
| strokeNeedsUpdating = true; | |||
| setBoundsToEnclose (getDrawableBounds()); | |||
| repaint(); | |||
| } | |||
| const Path& DrawableShape::getCachedPath() const | |||
| { | |||
| if (pathNeedsUpdating) | |||
| { | |||
| pathNeedsUpdating = false; | |||
| if (rebuildPath (cachedPath)) | |||
| strokeNeedsUpdating = true; | |||
| } | |||
| return cachedPath; | |||
| } | |||
| const Path& DrawableShape::getCachedStrokePath() const | |||
| { | |||
| if (strokeNeedsUpdating) | |||
| { | |||
| cachedStroke.clear(); | |||
| strokeType.createStrokedPath (cachedStroke, getCachedPath(), AffineTransform::identity, 4.0f); | |||
| strokeNeedsUpdating = false; // (must be called after getCachedPath) | |||
| } | |||
| return cachedStroke; | |||
| } | |||
| const Rectangle<float> DrawableShape::getBounds() const | |||
| const Rectangle<float> DrawableShape::getDrawableBounds() const | |||
| { | |||
| if (isStrokeVisible()) | |||
| return getCachedStrokePath().getBounds(); | |||
| return strokePath.getBounds(); | |||
| else | |||
| return getCachedPath().getBounds(); | |||
| return path.getBounds(); | |||
| } | |||
| bool DrawableShape::hitTest (float x, float y) const | |||
| bool DrawableShape::hitTest (int x, int y) const | |||
| { | |||
| return getCachedPath().contains (x, y) | |||
| || (isStrokeVisible() && getCachedStrokePath().contains (x, y)); | |||
| } | |||
| const float globalX = x - originRelativeToComponent.getX(); | |||
| const float globalY = y - originRelativeToComponent.getY(); | |||
| return path.contains (globalX, globalY) | |||
| || (isStrokeVisible() && strokePath.contains (globalX, globalY)); | |||
| } | |||
| //============================================================================== | |||
| const Identifier DrawableShape::FillAndStrokeState::type ("type"); | |||
| @@ -127,13 +127,11 @@ public: | |||
| }; | |||
| /** @internal */ | |||
| void invalidatePoints(); | |||
| const Rectangle<float> getDrawableBounds() const; | |||
| /** @internal */ | |||
| void render (const Drawable::RenderingContext& context) const; | |||
| void paint (Graphics& g); | |||
| /** @internal */ | |||
| const Rectangle<float> getBounds() const; | |||
| /** @internal */ | |||
| bool hitTest (float x, float y) const; | |||
| bool hitTest (int x, int y) const; | |||
| protected: | |||
| //============================================================================== | |||
| @@ -156,20 +154,13 @@ protected: | |||
| /** Writes the stroke and fill details to a FillAndStrokeState object. */ | |||
| void writeTo (FillAndStrokeState& state, ImageProvider* imageProvider, UndoManager* undoManager) const; | |||
| /** Returns the current cached path outline. */ | |||
| const Path& getCachedPath() const; | |||
| /** Returns the current cached stroke outline. */ | |||
| const Path& getCachedStrokePath() const; | |||
| //============================================================================== | |||
| PathStrokeType strokeType; | |||
| mutable Path cachedPath, cachedStroke; | |||
| Path path, strokePath; | |||
| private: | |||
| FillType mainFill, strokeFill; | |||
| mutable bool pathNeedsUpdating, strokeNeedsUpdating; | |||
| static void setBrush (const Drawable::RenderingContext& context, const FillType& type); | |||
| DrawableShape& operator= (const DrawableShape&); | |||
| }; | |||
| @@ -57,14 +57,22 @@ DrawableText::~DrawableText() | |||
| } | |||
| //============================================================================== | |||
| void DrawableText::refreshBounds() | |||
| { | |||
| setBoundsToEnclose (getDrawableBounds()); | |||
| repaint(); | |||
| } | |||
| void DrawableText::setText (const String& newText) | |||
| { | |||
| text = newText; | |||
| refreshBounds(); | |||
| } | |||
| void DrawableText::setColour (const Colour& newColour) | |||
| { | |||
| colour = newColour; | |||
| repaint(); | |||
| } | |||
| void DrawableText::setFont (const Font& newFont, bool applySizeAndScale) | |||
| @@ -74,38 +82,45 @@ void DrawableText::setFont (const Font& newFont, bool applySizeAndScale) | |||
| if (applySizeAndScale) | |||
| { | |||
| Point<float> corners[3]; | |||
| bounds.resolveThreePoints (corners, parent); | |||
| bounds.resolveThreePoints (corners, getParent()); | |||
| setFontSizeControlPoint (RelativePoint (RelativeParallelogram::getPointForInternalCoord (corners, | |||
| Point<float> (font.getHorizontalScale() * font.getHeight(), font.getHeight())))); | |||
| } | |||
| refreshBounds(); | |||
| } | |||
| void DrawableText::setJustification (const Justification& newJustification) | |||
| { | |||
| justification = newJustification; | |||
| repaint(); | |||
| } | |||
| void DrawableText::setBoundingBox (const RelativeParallelogram& newBounds) | |||
| { | |||
| bounds = newBounds; | |||
| refreshBounds(); | |||
| } | |||
| void DrawableText::setFontSizeControlPoint (const RelativePoint& newPoint) | |||
| { | |||
| fontSizeControlPoint = newPoint; | |||
| refreshBounds(); | |||
| } | |||
| //============================================================================== | |||
| void DrawableText::render (const Drawable::RenderingContext& context) const | |||
| void DrawableText::paint (Graphics& g) | |||
| { | |||
| transformContextToCorrectOrigin (g); | |||
| Point<float> points[3]; | |||
| bounds.resolveThreePoints (points, parent); | |||
| bounds.resolveThreePoints (points, 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 (bounds.getInternalCoordForPoint (points, fontSizeControlPoint.resolve (parent))); | |||
| const Point<float> fontCoords (bounds.getInternalCoordForPoint (points, fontSizeControlPoint.resolve (getParent()))); | |||
| const float fontHeight = jlimit (0.01f, jmax (0.01f, h), fontCoords.getY()); | |||
| const float fontWidth = jlimit (0.01f, jmax (0.01f, w), fontCoords.getX()); | |||
| @@ -113,27 +128,18 @@ void DrawableText::render (const Drawable::RenderingContext& context) const | |||
| f.setHeight (fontHeight); | |||
| f.setHorizontalScale (fontWidth / fontHeight); | |||
| context.g.setColour (colour.withMultipliedAlpha (context.opacity)); | |||
| g.setColour (colour); | |||
| 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)); | |||
| } | |||
| const Rectangle<float> DrawableText::getBounds() const | |||
| { | |||
| return bounds.getBounds (parent); | |||
| ga.draw (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())); | |||
| } | |||
| bool DrawableText::hitTest (float x, float y) const | |||
| const Rectangle<float> DrawableText::getDrawableBounds() const | |||
| { | |||
| Path p; | |||
| bounds.getPath (p, parent); | |||
| return p.contains (x, y); | |||
| return bounds.getBounds (getParent()); | |||
| } | |||
| Drawable* DrawableText::createCopy() const | |||
| @@ -141,10 +147,6 @@ Drawable* DrawableText::createCopy() const | |||
| return new DrawableText (*this); | |||
| } | |||
| void DrawableText::invalidatePoints() | |||
| { | |||
| } | |||
| //============================================================================== | |||
| const Identifier DrawableText::valueTreeType ("Text"); | |||
| @@ -237,7 +239,7 @@ void DrawableText::ValueTreeWrapper::setFontSizeControlPoint (const RelativePoin | |||
| } | |||
| //============================================================================== | |||
| const Rectangle<float> DrawableText::refreshFromValueTree (const ValueTree& tree, ImageProvider*) | |||
| void DrawableText::refreshFromValueTree (const ValueTree& tree, ImageProvider*) | |||
| { | |||
| ValueTreeWrapper v (tree); | |||
| setName (v.getID()); | |||
| @@ -252,20 +254,14 @@ const Rectangle<float> DrawableText::refreshFromValueTree (const ValueTree& tree | |||
| if (text != newText || font != newFont || justification != newJustification | |||
| || colour != newColour || bounds != newBounds || newFontPoint != fontSizeControlPoint) | |||
| { | |||
| const Rectangle<float> damage (getBounds()); | |||
| repaint(); | |||
| setBoundingBox (newBounds); | |||
| setFontSizeControlPoint (newFontPoint); | |||
| setColour (newColour); | |||
| setFont (newFont, false); | |||
| setJustification (newJustification); | |||
| setText (newText); | |||
| return damage.getUnion (getBounds()); | |||
| } | |||
| return Rectangle<float>(); | |||
| } | |||
| const ValueTree DrawableText::createValueTree (ImageProvider*) const | |||
| @@ -87,23 +87,19 @@ public: | |||
| //============================================================================== | |||
| /** @internal */ | |||
| void render (const Drawable::RenderingContext& context) const; | |||
| /** @internal */ | |||
| const Rectangle<float> getBounds() const; | |||
| /** @internal */ | |||
| bool hitTest (float x, float y) const; | |||
| void paint (Graphics& g); | |||
| /** @internal */ | |||
| Drawable* createCopy() const; | |||
| /** @internal */ | |||
| void invalidatePoints(); | |||
| /** @internal */ | |||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||
| void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||
| /** @internal */ | |||
| const ValueTree createValueTree (ImageProvider* imageProvider) const; | |||
| /** @internal */ | |||
| static const Identifier valueTreeType; | |||
| /** @internal */ | |||
| const Identifier getValueTreeType() const { return valueTreeType; } | |||
| /** @internal */ | |||
| const Rectangle<float> getDrawableBounds() const; | |||
| //============================================================================== | |||
| /** Internally-used class for wrapping a DrawableText's state into a ValueTree. */ | |||
| @@ -146,6 +142,8 @@ private: | |||
| Colour colour; | |||
| Justification justification; | |||
| void refreshBounds(); | |||
| DrawableText& operator= (const DrawableText&); | |||
| }; | |||