| @@ -162,27 +162,22 @@ public: | |||||
| drawable = newDrawable; | drawable = newDrawable; | ||||
| drawable.addListener (this); | drawable.addListener (this); | ||||
| drawableObject = Drawable::createFromValueTree (drawable, 0); // xxx image provider missing | drawableObject = Drawable::createFromValueTree (drawable, 0); // xxx image provider missing | ||||
| addAndMakeVisible (drawableObject); | |||||
| resized(); | resized(); | ||||
| repaint(); | repaint(); | ||||
| } | } | ||||
| void paint (Graphics& g) | |||||
| { | |||||
| if (drawableObject != 0) | |||||
| drawableObject->drawAt (g, 0, 0, 1.0f); | |||||
| } | |||||
| void resized() | void resized() | ||||
| { | { | ||||
| DrawableComposite* dc = dynamic_cast <DrawableComposite*> (static_cast <Drawable*> (drawableObject)); | |||||
| /* DrawableComposite* dc = dynamic_cast <DrawableComposite*> (static_cast <Drawable*> (drawableObject)); | |||||
| if (dc != 0) | if (dc != 0) | ||||
| { | { | ||||
| const RelativeCoordinate origin, right (getWidth()), bottom (getHeight()); | const RelativeCoordinate origin, right (getWidth()), bottom (getHeight()); | ||||
| dc->setContentArea (RelativeRectangle (origin, right, origin, bottom)); | dc->setContentArea (RelativeRectangle (origin, right, origin, bottom)); | ||||
| dc->resetBoundingBoxToContentArea(); | |||||
| } | |||||
| //dc->resetBoundingBoxToContentArea(); | |||||
| }*/ | |||||
| } | } | ||||
| void valueTreePropertyChanged (ValueTree&, const Identifier&) { updateGraphics(); } | void valueTreePropertyChanged (ValueTree&, const Identifier&) { updateGraphics(); } | ||||
| @@ -196,10 +191,7 @@ private: | |||||
| void updateGraphics() | void updateGraphics() | ||||
| { | { | ||||
| if (drawableObject != 0) | 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) | if (d != 0) | ||||
| { | { | ||||
| Rectangle<float> bounds (d->getBounds()); | |||||
| Rectangle<float> bounds (d->getDrawableBounds()); | |||||
| delete d; | delete d; | ||||
| PaintElement* newElement | PaintElement* newElement | ||||
| @@ -310,7 +310,7 @@ public: | |||||
| const Rectangle<int> parentArea (((PaintRoutineEditor*) getParentComponent())->getComponentArea()); | const Rectangle<int> parentArea (((PaintRoutineEditor*) getParentComponent())->getComponentArea()); | ||||
| Rectangle<int> r (getCurrentBounds (parentArea)); | 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)); | r.setSize ((int) (bounds.getWidth() + 0.999f), (int) (bounds.getHeight() + 0.999f)); | ||||
| @@ -33,7 +33,7 @@ | |||||
| */ | */ | ||||
| #define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
| #define JUCE_MINOR_VERSION 52 | #define JUCE_MINOR_VERSION 52 | ||||
| #define JUCE_BUILDNUMBER 95 | |||||
| #define JUCE_BUILDNUMBER 96 | |||||
| /** Current Juce version number. | /** Current Juce version number. | ||||
| @@ -36,6 +36,7 @@ DrawableButton::DrawableButton (const String& name, | |||||
| const DrawableButton::ButtonStyle buttonStyle) | const DrawableButton::ButtonStyle buttonStyle) | ||||
| : Button (name), | : Button (name), | ||||
| style (buttonStyle), | style (buttonStyle), | ||||
| currentImage (0), | |||||
| edgeIndent (3) | edgeIndent (3) | ||||
| { | { | ||||
| if (buttonStyle == ImageOnButtonBackground) | if (buttonStyle == ImageOnButtonBackground) | ||||
| @@ -52,14 +53,9 @@ DrawableButton::DrawableButton (const String& name, | |||||
| DrawableButton::~DrawableButton() | DrawableButton::~DrawableButton() | ||||
| { | { | ||||
| deleteImages(); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void DrawableButton::deleteImages() | |||||
| { | |||||
| } | |||||
| void DrawableButton::setImages (const Drawable* normal, | void DrawableButton::setImages (const Drawable* normal, | ||||
| const Drawable* over, | const Drawable* over, | ||||
| const Drawable* down, | const Drawable* down, | ||||
| @@ -69,36 +65,18 @@ void DrawableButton::setImages (const Drawable* normal, | |||||
| const Drawable* downOn, | const Drawable* downOn, | ||||
| const Drawable* disabledOn) | const Drawable* disabledOn) | ||||
| { | { | ||||
| deleteImages(); | |||||
| jassert (normal != 0); // you really need to give it at least a normal image.. | 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) | if (style != newStyle) | ||||
| { | { | ||||
| style = newStyle; | style = newStyle; | ||||
| repaint(); | |||||
| buttonStateChanged(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -134,21 +112,88 @@ void DrawableButton::setEdgeIndent (const int numPixelsIndent) | |||||
| { | { | ||||
| edgeIndent = numPixelsIndent; | edgeIndent = numPixelsIndent; | ||||
| repaint(); | 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, | void DrawableButton::paintButton (Graphics& g, | ||||
| bool isMouseOverButton, | bool isMouseOverButton, | ||||
| bool isButtonDown) | bool isButtonDown) | ||||
| { | { | ||||
| Rectangle<int> imageSpace; | |||||
| if (style == ImageOnButtonBackground) | 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, | getLookAndFeel().drawButtonBackground (g, *this, | ||||
| getBackgroundColour(), | getBackgroundColour(), | ||||
| isMouseOverButton, | isMouseOverButton, | ||||
| @@ -162,13 +207,6 @@ void DrawableButton::paintButton (Graphics& g, | |||||
| ? jmin (16, proportionOfHeight (0.25f)) | ? jmin (16, proportionOfHeight (0.25f)) | ||||
| : 0; | : 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) | if (textH > 0) | ||||
| { | { | ||||
| g.setFont ((float) textH); | g.setFont ((float) textH); | ||||
| @@ -182,39 +220,10 @@ void DrawableButton::paintButton (Graphics& g, | |||||
| Justification::centred, 1); | 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()) | if (isDown()) | ||||
| return getDownImage(); | return getDownImage(); | ||||
| @@ -225,15 +234,15 @@ const Drawable* DrawableButton::getCurrentImage() const throw() | |||||
| return getNormalImage(); | return getNormalImage(); | ||||
| } | } | ||||
| const Drawable* DrawableButton::getNormalImage() const throw() | |||||
| Drawable* DrawableButton::getNormalImage() const throw() | |||||
| { | { | ||||
| return (getToggleState() && normalImageOn != 0) ? normalImageOn | return (getToggleState() && normalImageOn != 0) ? normalImageOn | ||||
| : normalImage; | : normalImage; | ||||
| } | } | ||||
| const Drawable* DrawableButton::getOverImage() const throw() | |||||
| Drawable* DrawableButton::getOverImage() const throw() | |||||
| { | { | ||||
| const Drawable* d = normalImage; | |||||
| Drawable* d = normalImage; | |||||
| if (getToggleState()) | if (getToggleState()) | ||||
| { | { | ||||
| @@ -253,9 +262,9 @@ const Drawable* DrawableButton::getOverImage() const throw() | |||||
| return d; | return d; | ||||
| } | } | ||||
| const Drawable* DrawableButton::getDownImage() const throw() | |||||
| Drawable* DrawableButton::getDownImage() const throw() | |||||
| { | { | ||||
| const Drawable* d = normalImage; | |||||
| Drawable* d = normalImage; | |||||
| if (getToggleState()) | if (getToggleState()) | ||||
| { | { | ||||
| @@ -148,10 +148,10 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Returns the image that the button is currently displaying. */ | /** 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. | /** 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, | void paintButton (Graphics& g, | ||||
| bool isMouseOverButton, | bool isMouseOverButton, | ||||
| bool isButtonDown); | bool isButtonDown); | ||||
| /** @internal */ | |||||
| void buttonStateChanged(); | |||||
| /** @internal */ | |||||
| void resized(); | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| ButtonStyle style; | ButtonStyle style; | ||||
| ScopedPointer <Drawable> normalImage, overImage, downImage, disabledImage; | ScopedPointer <Drawable> normalImage, overImage, downImage, disabledImage; | ||||
| ScopedPointer <Drawable> normalImageOn, overImageOn, downImageOn, disabledImageOn; | ScopedPointer <Drawable> normalImageOn, overImageOn, downImageOn, disabledImageOn; | ||||
| Drawable* currentImage; | |||||
| Colour backgroundOff, backgroundOn; | Colour backgroundOff, backgroundOn; | ||||
| int edgeIndent; | int edgeIndent; | ||||
| void deleteImages(); | |||||
| DrawableButton (const DrawableButton&); | DrawableButton (const DrawableButton&); | ||||
| DrawableButton& operator= (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), | : ToolbarItemComponent (itemId_, buttonText, true), | ||||
| normalImage (normalImage_), | normalImage (normalImage_), | ||||
| toggledOnImage (toggledOnImage_) | |||||
| toggledOnImage (toggledOnImage_), | |||||
| currentImage (0) | |||||
| { | { | ||||
| jassert (normalImage_ != 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; | preferredSize = minSize = maxSize = toolbarDepth; | ||||
| return true; | 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; | Drawable* d = normalImage; | ||||
| if (getToggleState() && toggledOnImage != 0) | if (getToggleState() && toggledOnImage != 0) | ||||
| d = toggledOnImage; | 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); | void paintButtonArea (Graphics& g, int width, int height, bool isMouseOver, bool isMouseDown); | ||||
| /** @internal */ | /** @internal */ | ||||
| void contentAreaChanged (const Rectangle<int>& newBounds); | void contentAreaChanged (const Rectangle<int>& newBounds); | ||||
| /** @internal */ | |||||
| void buttonStateChanged(); | |||||
| /** @internal */ | |||||
| void resized(); | |||||
| /** @internal */ | |||||
| void enablementChanged(); | |||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| private: | private: | ||||
| ScopedPointer <Drawable> normalImage, toggledOnImage; | |||||
| ScopedPointer<Drawable> normalImage, toggledOnImage; | |||||
| Drawable* currentImage; | |||||
| void updateDrawable(); | |||||
| ToolbarButton (const ToolbarButton&); | ToolbarButton (const ToolbarButton&); | ||||
| ToolbarButton& operator= (const ToolbarButton&); | ToolbarButton& operator= (const ToolbarButton&); | ||||
| @@ -44,7 +44,6 @@ ToolbarItemFactory::~ToolbarItemFactory() | |||||
| { | { | ||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| class ItemDragAndDropOverlayComponent : public Component | class ItemDragAndDropOverlayComponent : public Component | ||||
| { | { | ||||
| @@ -57,10 +56,6 @@ public: | |||||
| setMouseCursor (MouseCursor::DraggingHandCursor); | setMouseCursor (MouseCursor::DraggingHandCursor); | ||||
| } | } | ||||
| ~ItemDragAndDropOverlayComponent() | |||||
| { | |||||
| } | |||||
| void paint (Graphics& g) | void paint (Graphics& g) | ||||
| { | { | ||||
| ToolbarItemComponent* const tc = dynamic_cast <ToolbarItemComponent*> (getParentComponent()); | 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)); | 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()); | const Identifier type (tree.getType()); | ||||
| Drawable* d = 0; | 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) | if (d != 0) | ||||
| { | { | ||||
| d->parent = parent; | |||||
| if (parent != 0) | |||||
| parent->insertDrawable (d); | |||||
| d->refreshFromValueTree (tree, imageProvider); | d->refreshFromValueTree (tree, imageProvider); | ||||
| } | } | ||||
| @@ -26,7 +26,7 @@ | |||||
| #ifndef __JUCE_DRAWABLE_JUCEHEADER__ | #ifndef __JUCE_DRAWABLE_JUCEHEADER__ | ||||
| #define __JUCE_DRAWABLE_JUCEHEADER__ | #define __JUCE_DRAWABLE_JUCEHEADER__ | ||||
| #include "../contexts/juce_Graphics.h" | |||||
| #include "../../components/juce_Component.h" | |||||
| #include "../geometry/juce_RelativeCoordinate.h" | #include "../geometry/juce_RelativeCoordinate.h" | ||||
| #include "../../../text/juce_XmlElement.h" | #include "../../../text/juce_XmlElement.h" | ||||
| #include "../../../containers/juce_ValueTree.h" | #include "../../../containers/juce_ValueTree.h" | ||||
| @@ -39,7 +39,7 @@ class DrawableComposite; | |||||
| @see DrawableComposite, DrawableImage, DrawablePath, DrawableText | @see DrawableComposite, DrawableImage, DrawablePath, DrawableText | ||||
| */ | */ | ||||
| class JUCE_API Drawable | |||||
| class JUCE_API Drawable : public Component | |||||
| { | { | ||||
| protected: | protected: | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -62,6 +62,11 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Renders this Drawable object. | /** 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 | @see drawWithin | ||||
| */ | */ | ||||
| void draw (Graphics& g, float opacity, | void draw (Graphics& g, float opacity, | ||||
| @@ -75,10 +80,12 @@ public: | |||||
| @code | @code | ||||
| draw (g, AffineTransform::translation (x, y)). | draw (g, AffineTransform::translation (x, y)). | ||||
| @endcode | @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 | /** Renders the Drawable within a rectangle, scaling it to fit neatly inside without | ||||
| changing its aspect-ratio. | changing its aspect-ratio. | ||||
| @@ -86,6 +93,10 @@ public: | |||||
| The object can placed arbitrarily within the rectangle based on a Justification type, | 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. | 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 g the graphics context to render onto | ||||
| @param destArea the target rectangle to fit the drawable into | @param destArea the target rectangle to fit the drawable into | ||||
| @param placement defines the alignment and rescaling to use to fit | @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. */ | /** 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. | /** 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. | /** 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. | @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. | /** Creates a ValueTree to represent this Drawable. | ||||
| The VarTree that is returned can be turned back into a Drawable with | 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. */ | /** Returns the tag ID that is used for a ValueTree that stores this type of drawable. */ | ||||
| virtual const Identifier getValueTreeType() const = 0; | 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. */ | /** Internal class used to manage ValueTrees that represent Drawables. */ | ||||
| class ValueTreeWrapperBase | class ValueTreeWrapperBase | ||||
| @@ -250,15 +234,23 @@ public: | |||||
| protected: | protected: | ||||
| friend class DrawableComposite; | friend class DrawableComposite; | ||||
| friend class DrawableShape; | |||||
| /** @internal */ | /** @internal */ | ||||
| DrawableComposite* parent; | |||||
| static Drawable* createChildFromValueTree (DrawableComposite* parent, const ValueTree& tree, ImageProvider* imageProvider); | |||||
| /** @internal */ | /** @internal */ | ||||
| virtual void invalidatePoints() = 0; | |||||
| void transformContextToCorrectOrigin (Graphics& g); | |||||
| /** @internal */ | /** @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: | private: | ||||
| String name; | |||||
| void nonConstDraw (Graphics& g, float opacity, const AffineTransform& transform); | |||||
| Drawable (const Drawable&); | Drawable (const Drawable&); | ||||
| Drawable& operator= (const Drawable&); | Drawable& operator= (const Drawable&); | ||||
| @@ -36,7 +36,8 @@ BEGIN_JUCE_NAMESPACE | |||||
| //============================================================================== | //============================================================================== | ||||
| DrawableComposite::DrawableComposite() | 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), | setContentArea (RelativeRectangle (RelativeCoordinate (0.0), | ||||
| RelativeCoordinate (100.0), | RelativeCoordinate (100.0), | ||||
| @@ -48,8 +49,8 @@ DrawableComposite::DrawableComposite (const DrawableComposite& other) | |||||
| { | { | ||||
| bounds = other.bounds; | 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); | markersX.addCopiesOf (other.markersX); | ||||
| markersY.addCopiesOf (other.markersY); | markersY.addCopiesOf (other.markersY); | ||||
| @@ -57,18 +58,24 @@ DrawableComposite::DrawableComposite (const DrawableComposite& other) | |||||
| DrawableComposite::~DrawableComposite() | 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) | void DrawableComposite::insertDrawable (Drawable* drawable, const int index) | ||||
| { | { | ||||
| if (drawable != 0) | 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) | 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) | 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() | 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; | return 0; | ||||
| } | } | ||||
| void DrawableComposite::bringToFront (const int index) | 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 | const RelativeRectangle DrawableComposite::getContentArea() const | ||||
| { | { | ||||
| @@ -138,6 +155,13 @@ void DrawableComposite::setContentArea (const RelativeRectangle& newArea) | |||||
| setMarker (contentRightMarkerName, true, newArea.right); | setMarker (contentRightMarkerName, true, newArea.right); | ||||
| setMarker (contentTopMarkerName, false, newArea.top); | setMarker (contentTopMarkerName, false, newArea.top); | ||||
| setMarker (contentBottomMarkerName, false, newArea.bottom); | setMarker (contentBottomMarkerName, false, newArea.bottom); | ||||
| refreshTransformFromBounds(); | |||||
| } | |||||
| void DrawableComposite::setBoundingBox (const RelativeParallelogram& newBoundingBox) | |||||
| { | |||||
| bounds = newBoundingBox; | |||||
| refreshTransformFromBounds(); | |||||
| } | } | ||||
| void DrawableComposite::resetBoundingBoxToContentArea() | void DrawableComposite::resetBoundingBoxToContentArea() | ||||
| @@ -151,15 +175,111 @@ void DrawableComposite::resetBoundingBoxToContentArea() | |||||
| void DrawableComposite::resetContentAreaAndBoundingBoxToFitChildren() | 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(); | 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() | int DrawableComposite::getNumMarkers (const bool xAxis) const throw() | ||||
| { | { | ||||
| return (xAxis ? markersX : markersY).size(); | return (xAxis ? markersX : markersY).size(); | ||||
| @@ -182,7 +302,7 @@ void DrawableComposite::setMarker (const String& name, const bool xAxis, const R | |||||
| if (m->position != position) | if (m->position != position) | ||||
| { | { | ||||
| m->position = position; | m->position = position; | ||||
| invalidatePoints(); | |||||
| markerHasMoved(); | |||||
| } | } | ||||
| return; | return; | ||||
| @@ -190,7 +310,7 @@ void DrawableComposite::setMarker (const String& name, const bool xAxis, const R | |||||
| } | } | ||||
| (xAxis ? markersX : markersY).add (new Marker (name, position)); | (xAxis ? markersX : markersY).add (new Marker (name, position)); | ||||
| invalidatePoints(); | |||||
| markerHasMoved(); | |||||
| } | } | ||||
| void DrawableComposite::removeMarker (const bool xAxis, const int index) | 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 | const Expression DrawableComposite::getSymbolValue (const String& symbol, const String& member) const | ||||
| { | { | ||||
| jassert (member.isEmpty()) // the only symbols available in a Drawable are markers. | 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); | 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 | Drawable* DrawableComposite::createCopy() const | ||||
| { | { | ||||
| return new DrawableComposite (*this); | return new DrawableComposite (*this); | ||||
| } | } | ||||
| void DrawableComposite::invalidatePoints() | |||||
| { | |||||
| for (int i = 0; i < drawables.size(); ++i) | |||||
| drawables.getUnchecked(i)->invalidatePoints(); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| const Identifier DrawableComposite::valueTreeType ("Group"); | 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); | const ValueTreeWrapper wrapper (tree); | ||||
| setName (wrapper.getID()); | setName (wrapper.getID()); | ||||
| Rectangle<float> damage; | |||||
| bool redrawAll = false; | |||||
| const RelativeParallelogram newBounds (wrapper.getBoundingBox()); | const RelativeParallelogram newBounds (wrapper.getBoundingBox()); | ||||
| if (bounds != newBounds) | if (bounds != newBounds) | ||||
| { | |||||
| redrawAll = true; | |||||
| damage = getBounds(); | |||||
| bounds = newBounds; | bounds = newBounds; | ||||
| } | |||||
| const int numMarkersX = wrapper.getNumMarkers (true); | const int numMarkersX = wrapper.getNumMarkers (true); | ||||
| const int numMarkersY = wrapper.getNumMarkers (false); | const int numMarkersY = wrapper.getNumMarkers (false); | ||||
| @@ -586,12 +562,6 @@ const Rectangle<float> DrawableComposite::refreshFromValueTree (const ValueTree& | |||||
| // Remove deleted markers... | // Remove deleted markers... | ||||
| if (markersX.size() > numMarkersX || markersY.size() > numMarkersY) | if (markersX.size() > numMarkersX || markersY.size() > numMarkersY) | ||||
| { | { | ||||
| if (! redrawAll) | |||||
| { | |||||
| redrawAll = true; | |||||
| damage = getBounds(); | |||||
| } | |||||
| markersX.removeRange (jmax (2, numMarkersX), markersX.size()); | markersX.removeRange (jmax (2, numMarkersX), markersX.size()); | ||||
| markersY.removeRange (jmax (2, numMarkersY), markersY.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))); | const Marker newMarker (wrapper.getMarker (true, wrapper.getMarkerState (true, i))); | ||||
| Marker* m = markersX[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) | 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))); | const Marker newMarker (wrapper.getMarker (false, wrapper.getMarkerState (false, i))); | ||||
| Marker* m = markersY[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.. | // 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.. | // Update drawables and add new ones.. | ||||
| for (i = 0; i < wrapper.getNumDrawables(); ++i) | for (i = 0; i < wrapper.getNumDrawables(); ++i) | ||||
| { | { | ||||
| const ValueTree newDrawable (wrapper.getDrawableState (i)); | const ValueTree newDrawable (wrapper.getDrawableState (i)); | ||||
| Drawable* d = drawables[i]; | |||||
| Drawable* d = getDrawable(i); | |||||
| if (d != 0) | if (d != 0) | ||||
| { | { | ||||
| if (newDrawable.hasType (d->getValueTreeType())) | if (newDrawable.hasType (d->getValueTreeType())) | ||||
| { | { | ||||
| const Rectangle<float> area (d->refreshFromValueTree (newDrawable, imageProvider)); | |||||
| if (! redrawAll) | |||||
| damage = damage.getUnion (area); | |||||
| d->refreshFromValueTree (newDrawable, imageProvider); | |||||
| } | } | ||||
| else | 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); | 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 | const ValueTree DrawableComposite::createValueTree (ImageProvider* imageProvider) const | ||||
| @@ -706,8 +632,8 @@ const ValueTree DrawableComposite::createValueTree (ImageProvider* imageProvider | |||||
| v.setBoundingBox (bounds, 0); | v.setBoundingBox (bounds, 0); | ||||
| int i; | 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) | for (i = 0; i < markersX.size(); ++i) | ||||
| v.setMarker (true, *markersX.getUnchecked(i), 0); | v.setMarker (true, *markersX.getUnchecked(i), 0); | ||||
| @@ -93,7 +93,7 @@ public: | |||||
| @see getDrawable | @see getDrawable | ||||
| */ | */ | ||||
| int getNumDrawables() const throw() { return drawables.size(); } | |||||
| int getNumDrawables() const throw(); | |||||
| /** Returns one of the drawables that are contained in this one. | /** Returns one of the drawables that are contained in this one. | ||||
| @@ -105,7 +105,7 @@ public: | |||||
| @see getNumDrawables | @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. */ | /** Looks for a child drawable with the specified name. */ | ||||
| Drawable* getDrawableWithName (const String& name) const throw(); | Drawable* getDrawableWithName (const String& name) const throw(); | ||||
| @@ -119,20 +119,6 @@ public: | |||||
| void bringToFront (int index); | 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. | /** Sets the parallelogram that defines the target position of the content rectangle when the drawable is rendered. | ||||
| @see setContentArea | @see setContentArea | ||||
| */ | */ | ||||
| @@ -148,6 +134,20 @@ public: | |||||
| */ | */ | ||||
| void resetBoundingBoxToContentArea(); | 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 | /** Resets the content area and the bounding transform to fit around the area occupied | ||||
| by the child components (ignoring any markers). | by the child components (ignoring any markers). | ||||
| */ | */ | ||||
| @@ -183,17 +183,9 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** @internal */ | /** @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; | Drawable* createCopy() const; | ||||
| /** @internal */ | /** @internal */ | ||||
| void invalidatePoints(); | |||||
| /** @internal */ | |||||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| /** @internal */ | /** @internal */ | ||||
| const ValueTree createValueTree (ImageProvider* imageProvider) const; | const ValueTree createValueTree (ImageProvider* imageProvider) const; | ||||
| /** @internal */ | /** @internal */ | ||||
| @@ -202,6 +194,16 @@ public: | |||||
| const Identifier getValueTreeType() const { return valueTreeType; } | const Identifier getValueTreeType() const { return valueTreeType; } | ||||
| /** @internal */ | /** @internal */ | ||||
| const Expression getSymbolValue (const String& symbol, const String& member) const; | 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. */ | /** Internally-used class for wrapping a DrawableComposite's state into a ValueTree. */ | ||||
| @@ -248,12 +250,12 @@ public: | |||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| private: | private: | ||||
| OwnedArray <Drawable> drawables; | |||||
| RelativeParallelogram bounds; | RelativeParallelogram bounds; | ||||
| OwnedArray <Marker> markersX, markersY; | 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&); | DrawableComposite& operator= (const DrawableComposite&); | ||||
| }; | }; | ||||
| @@ -59,12 +59,16 @@ void DrawableImage::setImage (const Image& imageToUse) | |||||
| { | { | ||||
| image = imageToUse; | image = imageToUse; | ||||
| setBounds (imageToUse.getBounds()); | |||||
| if (image.isValid()) | if (image.isValid()) | ||||
| { | { | ||||
| bounds.topLeft = RelativePoint (Point<float> (0.0f, 0.0f)); | bounds.topLeft = RelativePoint (Point<float> (0.0f, 0.0f)); | ||||
| bounds.topRight = RelativePoint (Point<float> ((float) image.getWidth(), 0.0f)); | bounds.topRight = RelativePoint (Point<float> ((float) image.getWidth(), 0.0f)); | ||||
| bounds.bottomLeft = RelativePoint (Point<float> (0.0f, (float) image.getHeight())); | bounds.bottomLeft = RelativePoint (Point<float> (0.0f, (float) image.getHeight())); | ||||
| } | } | ||||
| refreshTransformFromBounds(); | |||||
| } | } | ||||
| void DrawableImage::setOpacity (const float newOpacity) | void DrawableImage::setOpacity (const float newOpacity) | ||||
| @@ -80,68 +84,57 @@ void DrawableImage::setOverlayColour (const Colour& newOverlayColour) | |||||
| void DrawableImage::setBoundingBox (const RelativeParallelogram& newBounds) | void DrawableImage::setBoundingBox (const RelativeParallelogram& newBounds) | ||||
| { | { | ||||
| bounds = 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()) | if (image.isValid()) | ||||
| { | { | ||||
| const AffineTransform t (calculateTransform().followedBy (context.transform)); | |||||
| if (opacity > 0.0f && ! overlayColour.isOpaque()) | 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()) | 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 | Drawable* DrawableImage::createCopy() const | ||||
| @@ -149,10 +142,6 @@ Drawable* DrawableImage::createCopy() const | |||||
| return new DrawableImage (*this); | return new DrawableImage (*this); | ||||
| } | } | ||||
| void DrawableImage::invalidatePoints() | |||||
| { | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| const Identifier DrawableImage::valueTreeType ("Image"); | 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); | const ValueTreeWrapper controller (tree); | ||||
| setName (controller.getID()); | setName (controller.getID()); | ||||
| @@ -255,19 +244,14 @@ const Rectangle<float> DrawableImage::refreshFromValueTree (const ValueTree& tre | |||||
| const RelativeParallelogram newBounds (controller.getBoundingBox()); | 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; | opacity = newOpacity; | ||||
| overlayColour = newOverlayColour; | overlayColour = newOverlayColour; | ||||
| bounds = newBounds; | bounds = newBounds; | ||||
| image = newImage; | |||||
| return damage.getUnion (getBounds()); | |||||
| setImage (newImage); | |||||
| } | } | ||||
| return Rectangle<float>(); | |||||
| } | } | ||||
| const ValueTree DrawableImage::createValueTree (ImageProvider* imageProvider) const | const ValueTree DrawableImage::createValueTree (ImageProvider* imageProvider) const | ||||
| @@ -84,17 +84,15 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** @internal */ | /** @internal */ | ||||
| void render (const Drawable::RenderingContext& context) const; | |||||
| void paint (Graphics& g); | |||||
| /** @internal */ | /** @internal */ | ||||
| const Rectangle<float> getBounds() const; | |||||
| /** @internal */ | |||||
| bool hitTest (float x, float y) const; | |||||
| bool hitTest (int x, int y) const; | |||||
| /** @internal */ | /** @internal */ | ||||
| Drawable* createCopy() const; | Drawable* createCopy() const; | ||||
| /** @internal */ | /** @internal */ | ||||
| void invalidatePoints(); | |||||
| const Rectangle<float> getDrawableBounds() const; | |||||
| /** @internal */ | /** @internal */ | ||||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| /** @internal */ | /** @internal */ | ||||
| const ValueTree createValueTree (ImageProvider* imageProvider) const; | const ValueTree createValueTree (ImageProvider* imageProvider) const; | ||||
| /** @internal */ | /** @internal */ | ||||
| @@ -136,7 +134,7 @@ private: | |||||
| Colour overlayColour; | Colour overlayColour; | ||||
| RelativeParallelogram bounds; | RelativeParallelogram bounds; | ||||
| const AffineTransform calculateTransform() const; | |||||
| void refreshTransformFromBounds(); | |||||
| DrawableImage& operator= (const DrawableImage&); | DrawableImage& operator= (const DrawableImage&); | ||||
| }; | }; | ||||
| @@ -42,7 +42,7 @@ DrawablePath::DrawablePath (const DrawablePath& other) | |||||
| if (other.relativePath != 0) | if (other.relativePath != 0) | ||||
| relativePath = new RelativePointPath (*other.relativePath); | relativePath = new RelativePointPath (*other.relativePath); | ||||
| else | else | ||||
| cachedPath = other.cachedPath; | |||||
| setPath (other.path); | |||||
| } | } | ||||
| DrawablePath::~DrawablePath() | DrawablePath::~DrawablePath() | ||||
| @@ -57,18 +57,18 @@ Drawable* DrawablePath::createCopy() const | |||||
| //============================================================================== | //============================================================================== | ||||
| void DrawablePath::setPath (const Path& newPath) | void DrawablePath::setPath (const Path& newPath) | ||||
| { | { | ||||
| cachedPath = newPath; | |||||
| strokeChanged(); | |||||
| path = newPath; | |||||
| pathChanged(); | |||||
| } | } | ||||
| const Path& DrawablePath::getPath() const | const Path& DrawablePath::getPath() const | ||||
| { | { | ||||
| return getCachedPath(); | |||||
| return path; | |||||
| } | } | ||||
| const Path& DrawablePath::getStrokePath() const | const Path& DrawablePath::getStrokePath() const | ||||
| { | { | ||||
| return getCachedStrokePath(); | |||||
| return strokePath; | |||||
| } | } | ||||
| bool DrawablePath::rebuildPath (Path& path) const | bool DrawablePath::rebuildPath (Path& path) const | ||||
| @@ -76,7 +76,7 @@ bool DrawablePath::rebuildPath (Path& path) const | |||||
| if (relativePath != 0) | if (relativePath != 0) | ||||
| { | { | ||||
| path.clear(); | path.clear(); | ||||
| relativePath->createPath (path, parent); | |||||
| relativePath->createPath (path, getParent()); | |||||
| return true; | 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); | ValueTreeWrapper v (tree); | ||||
| setName (v.getID()); | setName (v.getID()); | ||||
| bool needsRedraw = refreshFillTypes (v, parent, imageProvider); | |||||
| if (refreshFillTypes (v, getParent(), imageProvider)) | |||||
| repaint(); | |||||
| ScopedPointer<RelativePointPath> newRelativePath (new RelativePointPath (tree)); | ScopedPointer<RelativePointPath> newRelativePath (new RelativePointPath (tree)); | ||||
| Path newPath; | Path newPath; | ||||
| newRelativePath->createPath (newPath, parent); | |||||
| newRelativePath->createPath (newPath, getParent()); | |||||
| if (! newRelativePath->containsAnyDynamicPoints()) | if (! newRelativePath->containsAnyDynamicPoints()) | ||||
| newRelativePath = 0; | newRelativePath = 0; | ||||
| const PathStrokeType newStroke (v.getStrokeType()); | 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; | strokeType = newStroke; | ||||
| needsRedraw = true; | |||||
| pathChanged(); | |||||
| } | } | ||||
| relativePath = newRelativePath; | relativePath = newRelativePath; | ||||
| if (needsRedraw) | |||||
| damageRect = damageRect.getUnion (getBounds()); | |||||
| return damageRect; | |||||
| } | } | ||||
| const ValueTree DrawablePath::createValueTree (ImageProvider* imageProvider) const | const ValueTree DrawablePath::createValueTree (ImageProvider* imageProvider) const | ||||
| @@ -488,7 +481,7 @@ const ValueTree DrawablePath::createValueTree (ImageProvider* imageProvider) con | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| RelativePointPath rp (getCachedPath()); | |||||
| RelativePointPath rp (path); | |||||
| rp.writeTo (tree, 0); | rp.writeTo (tree, 0); | ||||
| } | } | ||||
| @@ -64,7 +64,7 @@ public: | |||||
| /** @internal */ | /** @internal */ | ||||
| Drawable* createCopy() const; | Drawable* createCopy() const; | ||||
| /** @internal */ | /** @internal */ | ||||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| /** @internal */ | /** @internal */ | ||||
| const ValueTree createValueTree (ImageProvider* imageProvider) const; | const ValueTree createValueTree (ImageProvider* imageProvider) const; | ||||
| /** @internal */ | /** @internal */ | ||||
| @@ -68,13 +68,13 @@ void DrawableRectangle::setCornerSize (const RelativePoint& newSize) | |||||
| bool DrawableRectangle::rebuildPath (Path& path) const | bool DrawableRectangle::rebuildPath (Path& path) const | ||||
| { | { | ||||
| Point<float> points[3]; | Point<float> points[3]; | ||||
| bounds.resolveThreePoints (points, parent); | |||||
| bounds.resolveThreePoints (points, getParent()); | |||||
| const float w = Line<float> (points[0], points[1]).getLength(); | const float w = Line<float> (points[0], points[1]).getLength(); | ||||
| const float h = Line<float> (points[0], points[2]).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(); | path.clear(); | ||||
| @@ -92,7 +92,7 @@ bool DrawableRectangle::rebuildPath (Path& path) const | |||||
| const AffineTransform DrawableRectangle::calculateTransform() const | const AffineTransform DrawableRectangle::calculateTransform() const | ||||
| { | { | ||||
| Point<float> resolved[3]; | Point<float> resolved[3]; | ||||
| bounds.resolveThreePoints (resolved, parent); | |||||
| bounds.resolveThreePoints (resolved, getParent()); | |||||
| return AffineTransform::fromTargetPoints (resolved[0].getX(), resolved[0].getY(), | return AffineTransform::fromTargetPoints (resolved[0].getX(), resolved[0].getY(), | ||||
| resolved[1].getX(), resolved[1].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); | ValueTreeWrapper v (tree); | ||||
| setName (v.getID()); | setName (v.getID()); | ||||
| bool needsRedraw = refreshFillTypes (v, parent, imageProvider); | |||||
| if (refreshFillTypes (v, getParent(), imageProvider)) | |||||
| repaint(); | |||||
| RelativeParallelogram newBounds (v.getRectangle()); | RelativeParallelogram newBounds (v.getRectangle()); | ||||
| @@ -158,19 +158,12 @@ const Rectangle<float> DrawableRectangle::refreshFromValueTree (const ValueTree& | |||||
| if (strokeType != newStroke || newBounds != bounds || newCornerSize != cornerSize) | if (strokeType != newStroke || newBounds != bounds || newCornerSize != cornerSize) | ||||
| { | { | ||||
| damageRect = getBounds(); | |||||
| repaint(); | |||||
| bounds = newBounds; | bounds = newBounds; | ||||
| strokeType = newStroke; | strokeType = newStroke; | ||||
| cornerSize = newCornerSize; | cornerSize = newCornerSize; | ||||
| pathChanged(); | pathChanged(); | ||||
| strokeChanged(); | |||||
| needsRedraw = true; | |||||
| } | } | ||||
| if (needsRedraw) | |||||
| damageRect = damageRect.getUnion (getBounds()); | |||||
| return damageRect; | |||||
| } | } | ||||
| const ValueTree DrawableRectangle::createValueTree (ImageProvider* imageProvider) const | const ValueTree DrawableRectangle::createValueTree (ImageProvider* imageProvider) const | ||||
| @@ -64,7 +64,7 @@ public: | |||||
| /** @internal */ | /** @internal */ | ||||
| Drawable* createCopy() const; | Drawable* createCopy() const; | ||||
| /** @internal */ | /** @internal */ | ||||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| /** @internal */ | /** @internal */ | ||||
| const ValueTree createValueTree (ImageProvider* imageProvider) const; | const ValueTree createValueTree (ImageProvider* imageProvider) const; | ||||
| /** @internal */ | /** @internal */ | ||||
| @@ -35,18 +35,14 @@ BEGIN_JUCE_NAMESPACE | |||||
| DrawableShape::DrawableShape() | DrawableShape::DrawableShape() | ||||
| : strokeType (0.0f), | : strokeType (0.0f), | ||||
| mainFill (Colours::black), | mainFill (Colours::black), | ||||
| strokeFill (Colours::black), | |||||
| pathNeedsUpdating (true), | |||||
| strokeNeedsUpdating (true) | |||||
| strokeFill (Colours::black) | |||||
| { | { | ||||
| } | } | ||||
| DrawableShape::DrawableShape (const DrawableShape& other) | DrawableShape::DrawableShape (const DrawableShape& other) | ||||
| : strokeType (other.strokeType), | : strokeType (other.strokeType), | ||||
| mainFill (other.mainFill), | 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) | void DrawableShape::setStrokeType (const PathStrokeType& newStrokeType) | ||||
| { | { | ||||
| strokeType = newStrokeType; | strokeType = newStrokeType; | ||||
| strokeNeedsUpdating = true; | |||||
| strokeChanged(); | |||||
| } | } | ||||
| void DrawableShape::setStrokeThickness (const float newThickness) | void DrawableShape::setStrokeThickness (const float newThickness) | ||||
| @@ -80,18 +76,6 @@ bool DrawableShape::isStrokeVisible() const throw() | |||||
| return strokeType.getStrokeThickness() > 0.0f && ! strokeFill.isInvisible(); | 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, | bool DrawableShape::refreshFillTypes (const FillAndStrokeState& newState, | ||||
| Expression::EvaluationContext* /*nameFinder*/, | Expression::EvaluationContext* /*nameFinder*/, | ||||
| ImageProvider* imageProvider) | ImageProvider* imageProvider) | ||||
| @@ -99,7 +83,7 @@ bool DrawableShape::refreshFillTypes (const FillAndStrokeState& newState, | |||||
| bool hasChanged = false; | bool hasChanged = false; | ||||
| { | { | ||||
| const FillType f (newState.getMainFill (parent, imageProvider)); | |||||
| const FillType f (newState.getMainFill (getParent(), imageProvider)); | |||||
| if (mainFill != f) | 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) | 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()) | if (isStrokeVisible()) | ||||
| { | { | ||||
| setBrush (context, strokeFill); | |||||
| context.g.fillPath (getCachedStrokePath(), context.transform); | |||||
| g.setFillType (strokeFill); | |||||
| g.fillPath (strokePath); | |||||
| } | } | ||||
| } | } | ||||
| void DrawableShape::pathChanged() | void DrawableShape::pathChanged() | ||||
| { | { | ||||
| pathNeedsUpdating = true; | |||||
| strokeChanged(); | |||||
| } | } | ||||
| void DrawableShape::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()) | if (isStrokeVisible()) | ||||
| return getCachedStrokePath().getBounds(); | |||||
| return strokePath.getBounds(); | |||||
| else | 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"); | const Identifier DrawableShape::FillAndStrokeState::type ("type"); | ||||
| @@ -127,13 +127,11 @@ public: | |||||
| }; | }; | ||||
| /** @internal */ | /** @internal */ | ||||
| void invalidatePoints(); | |||||
| const Rectangle<float> getDrawableBounds() const; | |||||
| /** @internal */ | /** @internal */ | ||||
| void render (const Drawable::RenderingContext& context) const; | |||||
| void paint (Graphics& g); | |||||
| /** @internal */ | /** @internal */ | ||||
| const Rectangle<float> getBounds() const; | |||||
| /** @internal */ | |||||
| bool hitTest (float x, float y) const; | |||||
| bool hitTest (int x, int y) const; | |||||
| protected: | protected: | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -156,20 +154,13 @@ protected: | |||||
| /** Writes the stroke and fill details to a FillAndStrokeState object. */ | /** Writes the stroke and fill details to a FillAndStrokeState object. */ | ||||
| void writeTo (FillAndStrokeState& state, ImageProvider* imageProvider, UndoManager* undoManager) const; | 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; | PathStrokeType strokeType; | ||||
| mutable Path cachedPath, cachedStroke; | |||||
| Path path, strokePath; | |||||
| private: | private: | ||||
| FillType mainFill, strokeFill; | FillType mainFill, strokeFill; | ||||
| mutable bool pathNeedsUpdating, strokeNeedsUpdating; | |||||
| static void setBrush (const Drawable::RenderingContext& context, const FillType& type); | |||||
| DrawableShape& operator= (const DrawableShape&); | DrawableShape& operator= (const DrawableShape&); | ||||
| }; | }; | ||||
| @@ -57,14 +57,22 @@ DrawableText::~DrawableText() | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void DrawableText::refreshBounds() | |||||
| { | |||||
| setBoundsToEnclose (getDrawableBounds()); | |||||
| repaint(); | |||||
| } | |||||
| void DrawableText::setText (const String& newText) | void DrawableText::setText (const String& newText) | ||||
| { | { | ||||
| text = newText; | text = newText; | ||||
| refreshBounds(); | |||||
| } | } | ||||
| void DrawableText::setColour (const Colour& newColour) | void DrawableText::setColour (const Colour& newColour) | ||||
| { | { | ||||
| colour = newColour; | colour = newColour; | ||||
| repaint(); | |||||
| } | } | ||||
| void DrawableText::setFont (const Font& newFont, bool applySizeAndScale) | void DrawableText::setFont (const Font& newFont, bool applySizeAndScale) | ||||
| @@ -74,38 +82,45 @@ void DrawableText::setFont (const Font& newFont, bool applySizeAndScale) | |||||
| if (applySizeAndScale) | if (applySizeAndScale) | ||||
| { | { | ||||
| Point<float> corners[3]; | Point<float> corners[3]; | ||||
| bounds.resolveThreePoints (corners, parent); | |||||
| bounds.resolveThreePoints (corners, getParent()); | |||||
| setFontSizeControlPoint (RelativePoint (RelativeParallelogram::getPointForInternalCoord (corners, | setFontSizeControlPoint (RelativePoint (RelativeParallelogram::getPointForInternalCoord (corners, | ||||
| Point<float> (font.getHorizontalScale() * font.getHeight(), font.getHeight())))); | Point<float> (font.getHorizontalScale() * font.getHeight(), font.getHeight())))); | ||||
| } | } | ||||
| refreshBounds(); | |||||
| } | } | ||||
| void DrawableText::setJustification (const Justification& newJustification) | void DrawableText::setJustification (const Justification& newJustification) | ||||
| { | { | ||||
| justification = newJustification; | justification = newJustification; | ||||
| repaint(); | |||||
| } | } | ||||
| void DrawableText::setBoundingBox (const RelativeParallelogram& newBounds) | void DrawableText::setBoundingBox (const RelativeParallelogram& newBounds) | ||||
| { | { | ||||
| bounds = newBounds; | bounds = newBounds; | ||||
| refreshBounds(); | |||||
| } | } | ||||
| void DrawableText::setFontSizeControlPoint (const RelativePoint& newPoint) | void DrawableText::setFontSizeControlPoint (const RelativePoint& newPoint) | ||||
| { | { | ||||
| fontSizeControlPoint = newPoint; | fontSizeControlPoint = newPoint; | ||||
| refreshBounds(); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void DrawableText::render (const Drawable::RenderingContext& context) const | |||||
| void DrawableText::paint (Graphics& g) | |||||
| { | { | ||||
| transformContextToCorrectOrigin (g); | |||||
| Point<float> points[3]; | Point<float> points[3]; | ||||
| bounds.resolveThreePoints (points, parent); | |||||
| bounds.resolveThreePoints (points, getParent()); | |||||
| const float w = Line<float> (points[0], points[1]).getLength(); | const float w = Line<float> (points[0], points[1]).getLength(); | ||||
| const float h = Line<float> (points[0], points[2]).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 fontHeight = jlimit (0.01f, jmax (0.01f, h), fontCoords.getY()); | ||||
| const float fontWidth = jlimit (0.01f, jmax (0.01f, w), fontCoords.getX()); | 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.setHeight (fontHeight); | ||||
| f.setHorizontalScale (fontWidth / fontHeight); | f.setHorizontalScale (fontWidth / fontHeight); | ||||
| context.g.setColour (colour.withMultipliedAlpha (context.opacity)); | |||||
| g.setColour (colour); | |||||
| GlyphArrangement ga; | GlyphArrangement ga; | ||||
| ga.addFittedText (f, text, 0, 0, w, h, justification, 0x100000); | 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 | Drawable* DrawableText::createCopy() const | ||||
| @@ -141,10 +147,6 @@ Drawable* DrawableText::createCopy() const | |||||
| return new DrawableText (*this); | return new DrawableText (*this); | ||||
| } | } | ||||
| void DrawableText::invalidatePoints() | |||||
| { | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| const Identifier DrawableText::valueTreeType ("Text"); | 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); | ValueTreeWrapper v (tree); | ||||
| setName (v.getID()); | setName (v.getID()); | ||||
| @@ -252,20 +254,14 @@ const Rectangle<float> DrawableText::refreshFromValueTree (const ValueTree& tree | |||||
| if (text != newText || font != newFont || justification != newJustification | if (text != newText || font != newFont || justification != newJustification | ||||
| || colour != newColour || bounds != newBounds || newFontPoint != fontSizeControlPoint) | || colour != newColour || bounds != newBounds || newFontPoint != fontSizeControlPoint) | ||||
| { | { | ||||
| const Rectangle<float> damage (getBounds()); | |||||
| repaint(); | |||||
| setBoundingBox (newBounds); | setBoundingBox (newBounds); | ||||
| setFontSizeControlPoint (newFontPoint); | setFontSizeControlPoint (newFontPoint); | ||||
| setColour (newColour); | setColour (newColour); | ||||
| setFont (newFont, false); | setFont (newFont, false); | ||||
| setJustification (newJustification); | setJustification (newJustification); | ||||
| setText (newText); | setText (newText); | ||||
| return damage.getUnion (getBounds()); | |||||
| } | } | ||||
| return Rectangle<float>(); | |||||
| } | } | ||||
| const ValueTree DrawableText::createValueTree (ImageProvider*) const | const ValueTree DrawableText::createValueTree (ImageProvider*) const | ||||
| @@ -87,23 +87,19 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** @internal */ | /** @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 */ | /** @internal */ | ||||
| Drawable* createCopy() const; | Drawable* createCopy() const; | ||||
| /** @internal */ | /** @internal */ | ||||
| void invalidatePoints(); | |||||
| /** @internal */ | |||||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| void refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| /** @internal */ | /** @internal */ | ||||
| const ValueTree createValueTree (ImageProvider* imageProvider) const; | const ValueTree createValueTree (ImageProvider* imageProvider) const; | ||||
| /** @internal */ | /** @internal */ | ||||
| static const Identifier valueTreeType; | static const Identifier valueTreeType; | ||||
| /** @internal */ | /** @internal */ | ||||
| const Identifier getValueTreeType() const { return valueTreeType; } | const Identifier getValueTreeType() const { return valueTreeType; } | ||||
| /** @internal */ | |||||
| const Rectangle<float> getDrawableBounds() const; | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Internally-used class for wrapping a DrawableText's state into a ValueTree. */ | /** Internally-used class for wrapping a DrawableText's state into a ValueTree. */ | ||||
| @@ -146,6 +142,8 @@ private: | |||||
| Colour colour; | Colour colour; | ||||
| Justification justification; | Justification justification; | ||||
| void refreshBounds(); | |||||
| DrawableText& operator= (const DrawableText&); | DrawableText& operator= (const DrawableText&); | ||||
| }; | }; | ||||