| @@ -35,7 +35,7 @@ public: | |||
| { | |||
| Random random; | |||
| const int size = 10 + random.nextInt (30); | |||
| const float size = 10.0f + random.nextInt (30); | |||
| ballBounds.setBounds (random.nextFloat() * 100.0f, | |||
| random.nextFloat() * 100.0f, | |||
| @@ -186,9 +186,9 @@ public: | |||
| void timerCallback() | |||
| { | |||
| Random random; | |||
| blobPosition.setBounds (random.nextInt (getWidth()), | |||
| random.nextInt (getHeight()), | |||
| 40, 30); | |||
| blobPosition.setBounds ((float) random.nextInt (getWidth()), | |||
| (float) random.nextInt (getHeight()), | |||
| 40.0f, 30.0f); | |||
| repaint(); | |||
| } | |||
| @@ -223,7 +223,7 @@ public: | |||
| numIns (0), | |||
| numOuts (0) | |||
| { | |||
| shadow.setShadowProperties (2.5f, 0.5f, -1, 0); | |||
| shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 3, Point<int> (0, 1)); | |||
| setComponentEffect (&shadow); | |||
| setSize (150, 60); | |||
| @@ -27,86 +27,145 @@ | |||
| #pragma optimize ("t", on) | |||
| #endif | |||
| //============================================================================== | |||
| DropShadowEffect::DropShadowEffect() | |||
| : offsetX (0), | |||
| offsetY (0), | |||
| radius (4), | |||
| opacity (0.6f) | |||
| static void blurSingleChannelImage (uint8* const data, const int width, const int height, | |||
| const int lineStride, const int repetitions) noexcept | |||
| { | |||
| uint8* line = data; | |||
| for (int y = height; --y >= 0;) | |||
| { | |||
| for (int i = repetitions; --i >= 0;) | |||
| { | |||
| uint8* p = line; | |||
| *p++ = (((int) p[0]) + p[1]) / 2; | |||
| for (int x = width - 2; --x >= 0;) | |||
| *p++ = (((int) p[-1]) + p[0] + p[1] + 1) / 3; | |||
| *p = (((int) p[0]) + p[-1]) / 2; | |||
| } | |||
| line += lineStride; | |||
| } | |||
| for (int i = repetitions; --i >= 0;) | |||
| { | |||
| line = data; | |||
| { | |||
| uint8* p1 = line; | |||
| uint8* p2 = line + lineStride; | |||
| for (int x = width; --x >= 0;) | |||
| *p1++ = (((int) *p1) + *p2++) / 2; | |||
| } | |||
| line += lineStride; | |||
| for (int y = height - 2; --y >= 0;) | |||
| { | |||
| uint8* p1 = line; | |||
| uint8* p2 = line - lineStride; | |||
| uint8* p3 = line + lineStride; | |||
| for (int x = width; --x >= 0;) | |||
| *p1++ = (((int) *p1) + *p2++ + *p3++ + 1) / 3; | |||
| line += lineStride; | |||
| } | |||
| uint8* p1 = line; | |||
| uint8* p2 = line - lineStride; | |||
| for (int x = width; --x >= 0;) | |||
| *p1++ = (((int) *p1) + *p2++) / 2; | |||
| } | |||
| } | |||
| DropShadowEffect::~DropShadowEffect() | |||
| static void blurSingleChannelImage (Image& image, int radius) | |||
| { | |||
| const Image::BitmapData bm (image, Image::BitmapData::readWrite); | |||
| blurSingleChannelImage (bm.data, bm.width, bm.height, bm.lineStride, 2 * radius); | |||
| } | |||
| void DropShadowEffect::setShadowProperties (const float newRadius, | |||
| const float newOpacity, | |||
| const int newShadowOffsetX, | |||
| const int newShadowOffsetY) | |||
| #if JUCE_MSVC && JUCE_DEBUG | |||
| #pragma optimize ("", on) // resets optimisations to the project defaults | |||
| #endif | |||
| //============================================================================== | |||
| DropShadow::DropShadow() noexcept | |||
| : colour (0x90000000), radius (4) | |||
| { | |||
| radius = jmax (1.1f, newRadius); | |||
| offsetX = newShadowOffsetX; | |||
| offsetY = newShadowOffsetY; | |||
| opacity = newOpacity; | |||
| } | |||
| void DropShadowEffect::drawShadow (Graphics& g, const Image& srcImage, | |||
| float radius, float alpha, int offsetX, int offsetY) | |||
| DropShadow::DropShadow (const Colour& shadowColour, const int r, const Point<int>& o) noexcept | |||
| : colour (shadowColour), radius (r), offset (o) | |||
| { | |||
| const int w = srcImage.getWidth(); | |||
| const int h = srcImage.getHeight(); | |||
| Image shadowImage (Image::SingleChannel, w, h, false); | |||
| const Image::BitmapData srcData (srcImage, Image::BitmapData::readOnly); | |||
| const Image::BitmapData destData (shadowImage, Image::BitmapData::readWrite); | |||
| jassert (radius > 0); | |||
| } | |||
| const int filter = roundToInt (63.0f / radius); | |||
| const int radiusMinus1 = roundToInt ((radius - 1.0f) * 63.0f); | |||
| void DropShadow::drawForImage (Graphics& g, const Image& srcImage) const | |||
| { | |||
| jassert (radius > 0); | |||
| for (int x = w; --x >= 0;) | |||
| if (srcImage.isValid()) | |||
| { | |||
| int shadowAlpha = 0; | |||
| const PixelARGB* src = ((const PixelARGB*) srcData.data) + x; | |||
| uint8* shadowPix = destData.data + x; | |||
| Image shadowImage (srcImage.convertedToFormat (Image::SingleChannel)); | |||
| shadowImage.duplicateIfShared(); | |||
| for (int y = h; --y >= 0;) | |||
| { | |||
| shadowAlpha = ((shadowAlpha * radiusMinus1 + (src->getAlpha() << 6)) * filter) >> 12; | |||
| blurSingleChannelImage (shadowImage, radius); | |||
| *shadowPix = (uint8) shadowAlpha; | |||
| src = addBytesToPointer (src, srcData.lineStride); | |||
| shadowPix += destData.lineStride; | |||
| } | |||
| g.setColour (colour); | |||
| g.drawImageAt (shadowImage, offset.x, offset.y, true); | |||
| } | |||
| } | |||
| void DropShadow::drawForPath (Graphics& g, const Path& path) const | |||
| { | |||
| jassert (radius > 0); | |||
| const Rectangle<int> area (path.getBounds().translated ((float) offset.x, (float) offset.y) | |||
| .getSmallestIntegerContainer() | |||
| .getIntersection (g.getClipBounds()) | |||
| .expanded (radius + 1, radius + 1)); | |||
| for (int y = h; --y >= 0;) | |||
| if (area.getWidth() > 2 && area.getHeight() > 2) | |||
| { | |||
| int shadowAlpha = 0; | |||
| uint8* shadowPix = destData.getLinePointer (y); | |||
| Image renderedPath (Image::SingleChannel, area.getWidth(), area.getHeight(), true); | |||
| for (int x = w; --x >= 0;) | |||
| { | |||
| shadowAlpha = ((shadowAlpha * radiusMinus1 + (*shadowPix << 6)) * filter) >> 12; | |||
| *shadowPix++ = (uint8) shadowAlpha; | |||
| Graphics g2 (renderedPath); | |||
| g2.setColour (Colours::white); | |||
| g2.fillPath (path, AffineTransform::translation ((float) (offset.x - area.getX()), | |||
| (float) (offset.y - area.getY()))); | |||
| } | |||
| blurSingleChannelImage (renderedPath, radius); | |||
| g.setColour (colour); | |||
| g.drawImageAt (renderedPath, area.getX(), area.getY(), true); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| DropShadowEffect::DropShadowEffect() {} | |||
| DropShadowEffect::~DropShadowEffect() {} | |||
| g.setColour (Colours::black.withAlpha (alpha)); | |||
| g.drawImageAt (shadowImage, offsetX, offsetY, true); | |||
| void DropShadowEffect::setShadowProperties (const DropShadow& newShadow) | |||
| { | |||
| shadow = newShadow; | |||
| } | |||
| void DropShadowEffect::applyEffect (Image& image, Graphics& g, float scaleFactor, float alpha) | |||
| { | |||
| drawShadow (g, image, radius * scaleFactor, opacity * alpha, | |||
| (int) (offsetX * scaleFactor), (int) (offsetY * scaleFactor)); | |||
| DropShadow s (shadow); | |||
| s.radius = roundToInt (s.radius * scaleFactor); | |||
| s.colour = s.colour.withMultipliedAlpha (alpha); | |||
| s.offset.x = roundToInt (s.offset.x * scaleFactor); | |||
| s.offset.y = roundToInt (s.offset.y * scaleFactor); | |||
| s.drawForImage (g, image); | |||
| g.setOpacity (alpha); | |||
| g.drawImageAt (image, 0, 0); | |||
| } | |||
| #if JUCE_MSVC && JUCE_DEBUG | |||
| #pragma optimize ("", on) // resets optimisations to the project defaults | |||
| #endif | |||
| @@ -29,6 +29,37 @@ | |||
| #include "juce_ImageEffectFilter.h" | |||
| //============================================================================== | |||
| /** | |||
| Defines a drop-shadow effect. | |||
| */ | |||
| struct JUCE_API DropShadow | |||
| { | |||
| /** Creates a default drop-shadow effect. */ | |||
| DropShadow() noexcept; | |||
| /** Creates a drop-shadow object with the given parameters. */ | |||
| DropShadow (const Colour& shadowColour, int radius, const Point<int>& offset) noexcept; | |||
| /** Renders a drop-shadow based on the alpha-channel of the given image. */ | |||
| void drawForImage (Graphics& g, const Image& srcImage) const; | |||
| /** Renders a drop-shadow based on the shape of a path. */ | |||
| void drawForPath (Graphics& g, const Path& path) const; | |||
| /** The colour with which to render the shadow. | |||
| In most cases you'll probably want to leave this as black with an alpha | |||
| value of around 0.5 | |||
| */ | |||
| Colour colour; | |||
| /** The approximate spread of the shadow. */ | |||
| int radius; | |||
| /** The offset of the shadow. */ | |||
| Point<int> offset; | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| An effect filter that adds a drop-shadow behind the image's content. | |||
| @@ -50,9 +81,7 @@ class JUCE_API DropShadowEffect : public ImageEffectFilter | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a default drop-shadow effect. | |||
| To customise the shadow's appearance, use the setShadowProperties() | |||
| method. | |||
| To customise the shadow's appearance, use the setShadowProperties() method. | |||
| */ | |||
| DropShadowEffect(); | |||
| @@ -60,23 +89,8 @@ public: | |||
| ~DropShadowEffect(); | |||
| //============================================================================== | |||
| /** Sets up parameters affecting the shadow's appearance. | |||
| @param newRadius the (approximate) radius of the blur used | |||
| @param newOpacity the opacity with which the shadow is rendered | |||
| @param newShadowOffsetX allows the shadow to be shifted in relation to the | |||
| component's contents | |||
| @param newShadowOffsetY allows the shadow to be shifted in relation to the | |||
| component's contents | |||
| */ | |||
| void setShadowProperties (float newRadius, | |||
| float newOpacity, | |||
| int newShadowOffsetX, | |||
| int newShadowOffsetY); | |||
| static void drawShadow (Graphics& g, const Image& srcImage, | |||
| float radius, float alpha, int offsetX, int offsetY); | |||
| /** Sets up parameters affecting the shadow's appearance. */ | |||
| void setShadowProperties (const DropShadow& newShadow); | |||
| //============================================================================== | |||
| /** @internal */ | |||
| @@ -85,8 +99,7 @@ public: | |||
| private: | |||
| //============================================================================== | |||
| int offsetX, offsetY; | |||
| float radius, opacity; | |||
| DropShadow shadow; | |||
| JUCE_LEAK_DETECTOR (DropShadowEffect); | |||
| }; | |||
| @@ -241,4 +241,51 @@ void ImageConvolutionKernel::applyToImage (Image& destImage, | |||
| } | |||
| } | |||
| } | |||
| else if (destData.pixelStride == 1) | |||
| { | |||
| for (int y = area.getY(); y < bottom; ++y) | |||
| { | |||
| uint8* dest = line; | |||
| line += destData.lineStride; | |||
| for (int x = area.getX(); x < right; ++x) | |||
| { | |||
| float c1 = 0; | |||
| for (int yy = 0; yy < size; ++yy) | |||
| { | |||
| const int sy = y + yy - (size >> 1); | |||
| if (sy >= srcData.height) | |||
| break; | |||
| if (sy >= 0) | |||
| { | |||
| int sx = x - (size >> 1); | |||
| const uint8* src = srcData.getPixelPointer (sx, sy); | |||
| for (int xx = 0; xx < size; ++xx) | |||
| { | |||
| if (sx >= srcData.width) | |||
| break; | |||
| if (sx >= 0) | |||
| { | |||
| const float kernelMult = values [xx + yy * size]; | |||
| c1 += kernelMult * *src++; | |||
| } | |||
| else | |||
| { | |||
| src += 3; | |||
| } | |||
| ++sx; | |||
| } | |||
| } | |||
| } | |||
| *dest++ = (uint8) roundToInt (c1); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -23,49 +23,24 @@ | |||
| ============================================================================== | |||
| */ | |||
| ArrowButton::ArrowButton (const String& name, | |||
| float arrowDirectionInRadians, | |||
| const Colour& arrowColour) | |||
| : Button (name), | |||
| colour (arrowColour) | |||
| ArrowButton::ArrowButton (const String& name, float arrowDirectionInRadians, const Colour& arrowColour) | |||
| : Button (name), colour (arrowColour) | |||
| { | |||
| path.lineTo (0.0f, 1.0f); | |||
| path.lineTo (1.0f, 0.5f); | |||
| path.closeSubPath(); | |||
| path.applyTransform (AffineTransform::rotation (float_Pi * 2.0f * arrowDirectionInRadians, | |||
| 0.5f, 0.5f)); | |||
| setComponentEffect (&shadow); | |||
| updateShadowAndOffset(); | |||
| path.addTriangle (0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f); | |||
| path.applyTransform (AffineTransform::rotation (float_Pi * 2.0f * arrowDirectionInRadians, 0.5f, 0.5f)); | |||
| } | |||
| ArrowButton::~ArrowButton() | |||
| { | |||
| } | |||
| ArrowButton::~ArrowButton() {} | |||
| void ArrowButton::paintButton (Graphics& g, | |||
| bool /*isMouseOverButton*/, | |||
| bool /*isButtonDown*/) | |||
| void ArrowButton::paintButton (Graphics& g, bool /*isMouseOverButton*/, bool isButtonDown) | |||
| { | |||
| g.setColour (colour); | |||
| Path p (path); | |||
| g.fillPath (path, path.getTransformToScaleToFit ((float) offset, | |||
| (float) offset, | |||
| (float) (getWidth() - 3), | |||
| (float) (getHeight() - 3), | |||
| false)); | |||
| } | |||
| const float offset = isButtonDown ? 1.0f : 0.0f; | |||
| p.applyTransform (path.getTransformToScaleToFit (offset, offset, getWidth() - 3.0f, getHeight() - 3.0f, false)); | |||
| void ArrowButton::buttonStateChanged() | |||
| { | |||
| updateShadowAndOffset(); | |||
| } | |||
| DropShadow (Colours::black.withAlpha (0.3f), isButtonDown ? 2 : 4, Point<int>()).drawForPath (g, p); | |||
| void ArrowButton::updateShadowAndOffset() | |||
| { | |||
| offset = (isDown()) ? 1 : 0; | |||
| shadow.setShadowProperties ((isDown()) ? 1.2f : 3.0f, | |||
| 0.3f, -1, 0); | |||
| g.setColour (colour); | |||
| g.fillPath (p); | |||
| } | |||
| @@ -55,23 +55,12 @@ public: | |||
| protected: | |||
| //============================================================================== | |||
| /** @internal */ | |||
| void paintButton (Graphics& g, | |||
| bool isMouseOverButton, | |||
| bool isButtonDown); | |||
| /** @internal */ | |||
| void buttonStateChanged(); | |||
| void paintButton (Graphics&, bool isMouseOverButton, bool isButtonDown); | |||
| private: | |||
| //============================================================================== | |||
| Colour colour; | |||
| DropShadowEffect shadow; | |||
| Path path; | |||
| int offset; | |||
| void updateShadowAndOffset(); | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ArrowButton); | |||
| }; | |||
| @@ -64,8 +64,8 @@ void ShapeButton::setShape (const Path& newShape, | |||
| shape = newShape; | |||
| maintainShapeProportions = maintainShapeProportions_; | |||
| shadow.setShadowProperties (3.0f, 0.5f, 0, 0); | |||
| setComponentEffect ((hasShadow) ? &shadow : 0); | |||
| shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 3, Point<int>())); | |||
| setComponentEffect (hasShadow ? &shadow : nullptr); | |||
| if (resizeNowToFitThisShape) | |||
| { | |||
| @@ -24,13 +24,8 @@ | |||
| */ | |||
| TabBarButton::TabBarButton (const String& name, TabbedButtonBar& owner_) | |||
| : Button (name), | |||
| owner (owner_), | |||
| overlapPixels (0), | |||
| extraCompPlacement (afterText) | |||
| : Button (name), owner (owner_), overlapPixels (0), extraCompPlacement (afterText) | |||
| { | |||
| shadow.setShadowProperties (2.2f, 0.7f, 0, 0); | |||
| setComponentEffect (&shadow); | |||
| setWantsKeyboardFocus (false); | |||
| } | |||
| @@ -109,7 +109,6 @@ public: | |||
| protected: | |||
| friend class TabbedButtonBar; | |||
| TabbedButtonBar& owner; | |||
| DropShadowEffect shadow; | |||
| int overlapPixels; | |||
| ScopedPointer<Component> extraComponent; | |||
| @@ -1977,10 +1977,9 @@ int LookAndFeel::getDefaultMenuBarHeight() | |||
| //============================================================================== | |||
| DropShadower* LookAndFeel::createDropShadowerForComponent (Component*) | |||
| { | |||
| return new DropShadower (0.4f, 1, 5, 10); | |||
| return new DropShadower (DropShadow (Colours::black.withAlpha (0.4f), 10, Point<int> (0, 2))); | |||
| } | |||
| //============================================================================== | |||
| void LookAndFeel::drawStretchableLayoutResizerBar (Graphics& g, | |||
| int w, int h, | |||
| @@ -2085,7 +2084,7 @@ int LookAndFeel::getTabButtonBestWidth (TabBarButton& button, int tabDepth) | |||
| + getTabButtonOverlap (tabDepth) * 2; | |||
| } | |||
| void LookAndFeel::createTabButtonShape (TabBarButton& button, Path& p, bool isMouseOver, bool isMouseDown) | |||
| void LookAndFeel::createTabButtonShape (TabBarButton& button, Path& p, bool /*isMouseOver*/, bool /*isMouseDown*/) | |||
| { | |||
| const Rectangle<int> activeArea (button.getActiveArea()); | |||
| const float w = (float) activeArea.getWidth(); | |||
| @@ -2144,7 +2143,7 @@ void LookAndFeel::createTabButtonShape (TabBarButton& button, Path& p, bool isMo | |||
| p = p.createPathWithRoundedCorners (3.0f); | |||
| } | |||
| void LookAndFeel::fillTabButtonShape (TabBarButton& button, Graphics& g, const Path& path, bool isMouseOver, bool isMouseDown) | |||
| void LookAndFeel::fillTabButtonShape (TabBarButton& button, Graphics& g, const Path& path, bool /*isMouseOver*/, bool /*isMouseDown*/) | |||
| { | |||
| const Colour tabBackground (button.getTabBackgroundColour()); | |||
| const bool isFrontTab = button.isFrontTab(); | |||
| @@ -2165,8 +2164,8 @@ void LookAndFeel::drawTabButtonText (TabBarButton& button, Graphics& g, bool isM | |||
| { | |||
| const Rectangle<float> area (button.getTextArea().toFloat()); | |||
| int length = area.getWidth(); | |||
| int depth = area.getHeight(); | |||
| float length = area.getWidth(); | |||
| float depth = area.getHeight(); | |||
| if (button.getTabbedButtonBar().isVertical()) | |||
| std::swap (length, depth); | |||
| @@ -2178,7 +2177,7 @@ void LookAndFeel::drawTabButtonText (TabBarButton& button, Graphics& g, bool isM | |||
| textLayout.addFittedText (font, button.getButtonText().trim(), | |||
| 0.0f, 0.0f, (float) length, (float) depth, | |||
| Justification::centred, | |||
| jmax (1, depth / 12)); | |||
| jmax (1, ((int) depth) / 12)); | |||
| AffineTransform t; | |||
| @@ -2191,21 +2190,20 @@ void LookAndFeel::drawTabButtonText (TabBarButton& button, Graphics& g, bool isM | |||
| default: jassertfalse; break; | |||
| } | |||
| Colour col; | |||
| if (button.isFrontTab() && (button.isColourSpecified (TabbedButtonBar::frontTextColourId) | |||
| || isColourSpecified (TabbedButtonBar::frontTextColourId))) | |||
| g.setColour (findColour (TabbedButtonBar::frontTextColourId)); | |||
| col = findColour (TabbedButtonBar::frontTextColourId); | |||
| else if (button.isColourSpecified (TabbedButtonBar::tabTextColourId) | |||
| || isColourSpecified (TabbedButtonBar::tabTextColourId)) | |||
| g.setColour (findColour (TabbedButtonBar::tabTextColourId)); | |||
| col = findColour (TabbedButtonBar::tabTextColourId); | |||
| else | |||
| g.setColour (button.getTabBackgroundColour().contrasting()); | |||
| col = button.getTabBackgroundColour().contrasting(); | |||
| if (! (isMouseOver || isMouseDown)) | |||
| g.setOpacity (0.8f); | |||
| if (! button.isEnabled()) | |||
| g.setOpacity (0.3f); | |||
| const float alpha = button.isEnabled() ? ((isMouseOver || isMouseDown) ? 1.0f : 0.8f) : 0.3f; | |||
| g.setColour (col.withMultipliedAlpha (alpha)); | |||
| textLayout.draw (g, t); | |||
| } | |||
| @@ -2218,8 +2216,9 @@ void LookAndFeel::drawTabButton (TabBarButton& button, Graphics& g, bool isMouse | |||
| tabShape.applyTransform (AffineTransform::translation ((float) activeArea.getX(), | |||
| (float) activeArea.getY())); | |||
| fillTabButtonShape (button, g, tabShape, isMouseOver, isMouseDown); | |||
| DropShadow (Colours::black.withAlpha (0.5f), 2, Point<int> (0, 1)).drawForPath (g, tabShape); | |||
| fillTabButtonShape (button, g, tabShape, isMouseOver, isMouseDown); | |||
| drawTabButtonText (button, g, isMouseOver, isMouseDown); | |||
| } | |||
| @@ -2458,21 +2457,10 @@ void LookAndFeel::drawCallOutBoxBackground (CallOutBox& box, Graphics& g, | |||
| { | |||
| if (cachedImage.isNull()) | |||
| { | |||
| const int w = box.getWidth(); | |||
| const int h = box.getHeight(); | |||
| Image renderedPath (Image::ARGB, w, h, true); | |||
| { | |||
| Graphics g2 (renderedPath); | |||
| g2.setColour (Colour::greyLevel (0.23f).withAlpha (0.9f)); | |||
| g2.fillPath (path); | |||
| } | |||
| cachedImage = Image (Image::ARGB, w, h, true); | |||
| cachedImage = Image (Image::ARGB, box.getWidth(), box.getHeight(), true); | |||
| Graphics g2 (cachedImage); | |||
| DropShadowEffect::drawShadow (g2, renderedPath, 5.0f, 0.4f, 0, 2); | |||
| DropShadow (Colours::black.withAlpha (0.7f), 8, Point<int> (0, 2)).drawForPath (g2, path); | |||
| } | |||
| g.setColour (Colours::black); | |||
| @@ -2779,16 +2767,16 @@ void LookAndFeel::drawShinyButtonShape (Graphics& g, | |||
| Path outline; | |||
| LookAndFeelHelpers::createRoundedPath (outline, x, y, w, h, cs, | |||
| ! (flatOnLeft || flatOnTop), | |||
| ! (flatOnLeft || flatOnTop), | |||
| ! (flatOnRight || flatOnTop), | |||
| ! (flatOnLeft || flatOnBottom), | |||
| ! (flatOnLeft || flatOnBottom), | |||
| ! (flatOnRight || flatOnBottom)); | |||
| ColourGradient cg (baseColour, 0.0f, y, | |||
| baseColour.overlaidWith (Colour (0x070000ff)), 0.0f, y + h, | |||
| false); | |||
| cg.addColour (0.5, baseColour.overlaidWith (Colour (0x33ffffff))); | |||
| cg.addColour (0.5, baseColour.overlaidWith (Colour (0x33ffffff))); | |||
| cg.addColour (0.51, baseColour.overlaidWith (Colour (0x110000ff))); | |||
| g.setGradientFill (cg); | |||
| @@ -28,7 +28,7 @@ BubbleComponent::BubbleComponent() | |||
| { | |||
| setInterceptsMouseClicks (false, false); | |||
| shadow.setShadowProperties (5.0f, 0.35f, 0, 0); | |||
| shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.35f), 5, Point<int>())); | |||
| setComponentEffect (&shadow); | |||
| } | |||
| @@ -97,16 +97,8 @@ private: | |||
| //============================================================================== | |||
| DropShadower::DropShadower (const float alpha_, | |||
| const int xOffset_, | |||
| const int yOffset_, | |||
| const float blurRadius_) | |||
| : owner (nullptr), | |||
| xOffset (xOffset_), | |||
| yOffset (yOffset_), | |||
| alpha (alpha_), | |||
| blurRadius (blurRadius_), | |||
| reentrant (false) | |||
| DropShadower::DropShadower (const DropShadow& shadow_) | |||
| : owner (nullptr), shadow (shadow_), reentrant (false) | |||
| { | |||
| } | |||
| @@ -179,55 +171,51 @@ void DropShadower::updateShadows() | |||
| { | |||
| const ScopedValueSetter<bool> setter (reentrant, true, false); | |||
| const int shadowEdge = jmax (xOffset, yOffset) + (int) blurRadius; | |||
| const int shadowEdge = jmax (shadow.offset.x, shadow.offset.y) + shadow.radius; | |||
| if (createShadowWindows) | |||
| { | |||
| // keep a cached version of the image to save doing the gaussian too often | |||
| String imageId; | |||
| imageId << shadowEdge << ',' << xOffset << ',' << yOffset << ',' << alpha; | |||
| const int shadowEdge2 = shadowEdge * 2; | |||
| const int imageSize = shadowEdge * 5; | |||
| const int hash = imageId.hashCode(); | |||
| // keep a cached version of the image to save doing the gaussian too often | |||
| int64 hash = shadow.radius ^ 0x2342dfa7; | |||
| hash = hash * 101 + shadow.offset.x; | |||
| hash = hash * 101 + shadow.offset.y; | |||
| hash = hash * 65537 + shadow.colour.getARGB(); | |||
| Image bigIm (ImageCache::getFromHashCode (hash)); | |||
| if (bigIm.isNull()) | |||
| { | |||
| bigIm = Image (Image::ARGB, shadowEdge * 5, shadowEdge * 5, true); | |||
| bigIm = Image (Image::ARGB, imageSize, imageSize, true); | |||
| Graphics g (bigIm); | |||
| Graphics bigG (bigIm); | |||
| bigG.setColour (Colours::black.withAlpha (alpha)); | |||
| bigG.fillRect (shadowEdge + xOffset, | |||
| shadowEdge + yOffset, | |||
| bigIm.getWidth() - (shadowEdge * 2), | |||
| bigIm.getHeight() - (shadowEdge * 2)); | |||
| Path p; | |||
| p.addRectangle ((float) (shadowEdge + shadow.offset.x), | |||
| (float) (shadowEdge + shadow.offset.y), | |||
| (float) (imageSize - shadowEdge2), | |||
| (float) (imageSize - shadowEdge2)); | |||
| ImageConvolutionKernel blurKernel (roundToInt (blurRadius * 2.0f)); | |||
| blurKernel.createGaussianBlur (blurRadius); | |||
| blurKernel.applyToImage (bigIm, bigIm, | |||
| Rectangle<int> (xOffset, yOffset, | |||
| bigIm.getWidth(), bigIm.getHeight())); | |||
| shadow.drawForPath (g, p); | |||
| ImageCache::addImageToCache (bigIm, hash); | |||
| } | |||
| const int iw = bigIm.getWidth(); | |||
| const int ih = bigIm.getHeight(); | |||
| const int shadowEdge2 = shadowEdge * 2; | |||
| setShadowImage (bigIm, 0, shadowEdge, shadowEdge2, 0, 0); | |||
| setShadowImage (bigIm, 1, shadowEdge, shadowEdge2, 0, ih - shadowEdge2); | |||
| setShadowImage (bigIm, 2, shadowEdge, shadowEdge, 0, shadowEdge2); | |||
| setShadowImage (bigIm, 3, shadowEdge, shadowEdge2, iw - shadowEdge, 0); | |||
| setShadowImage (bigIm, 4, shadowEdge, shadowEdge2, iw - shadowEdge, ih - shadowEdge2); | |||
| setShadowImage (bigIm, 5, shadowEdge, shadowEdge, iw - shadowEdge, shadowEdge2); | |||
| setShadowImage (bigIm, 6, shadowEdge, shadowEdge, shadowEdge, 0); | |||
| setShadowImage (bigIm, 7, shadowEdge, shadowEdge, iw - shadowEdge2, 0); | |||
| setShadowImage (bigIm, 8, shadowEdge, shadowEdge, shadowEdge2, 0); | |||
| setShadowImage (bigIm, 9, shadowEdge, shadowEdge, shadowEdge, ih - shadowEdge); | |||
| setShadowImage (bigIm, 10, shadowEdge, shadowEdge, iw - shadowEdge2, ih - shadowEdge); | |||
| setShadowImage (bigIm, 11, shadowEdge, shadowEdge, shadowEdge2, ih - shadowEdge); | |||
| jassert (imageSize == bigIm.getWidth() && imageSize == bigIm.getHeight()); | |||
| setShadowImage (bigIm, 0, shadowEdge, shadowEdge2, 0, 0); | |||
| setShadowImage (bigIm, 1, shadowEdge, shadowEdge2, 0, imageSize - shadowEdge2); | |||
| setShadowImage (bigIm, 2, shadowEdge, shadowEdge, 0, shadowEdge2); | |||
| setShadowImage (bigIm, 3, shadowEdge, shadowEdge2, imageSize - shadowEdge, 0); | |||
| setShadowImage (bigIm, 4, shadowEdge, shadowEdge2, imageSize - shadowEdge, imageSize - shadowEdge2); | |||
| setShadowImage (bigIm, 5, shadowEdge, shadowEdge, imageSize - shadowEdge, shadowEdge2); | |||
| setShadowImage (bigIm, 6, shadowEdge, shadowEdge, shadowEdge, 0); | |||
| setShadowImage (bigIm, 7, shadowEdge, shadowEdge, imageSize - shadowEdge2, 0); | |||
| setShadowImage (bigIm, 8, shadowEdge, shadowEdge, shadowEdge2, 0); | |||
| setShadowImage (bigIm, 9, shadowEdge, shadowEdge, shadowEdge, imageSize - shadowEdge); | |||
| setShadowImage (bigIm, 10, shadowEdge, shadowEdge, imageSize - shadowEdge2, imageSize - shadowEdge); | |||
| setShadowImage (bigIm, 11, shadowEdge, shadowEdge, shadowEdge2, imageSize - shadowEdge); | |||
| for (int i = 0; i < 4; ++i) | |||
| shadowWindows.add (new ShadowWindow (*owner, i, shadowImageSections)); | |||
| @@ -43,50 +43,35 @@ | |||
| Component::addToDesktop(), and the system will create one of these if it's | |||
| needed (which it obviously isn't on the Mac, for example). | |||
| */ | |||
| class JUCE_API DropShadower : public ComponentListener | |||
| class JUCE_API DropShadower : private ComponentListener | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a DropShadower. | |||
| @param alpha the opacity of the shadows, from 0 to 1.0 | |||
| @param xOffset the horizontal displacement of the shadow, in pixels | |||
| @param yOffset the vertical displacement of the shadow, in pixels | |||
| @param blurRadius the radius of the blur to use for creating the shadow | |||
| */ | |||
| DropShadower (float alpha = 0.5f, | |||
| int xOffset = 1, | |||
| int yOffset = 5, | |||
| float blurRadius = 10.0f); | |||
| /** Creates a DropShadower. */ | |||
| DropShadower (const DropShadow& shadowType); | |||
| /** Destructor. */ | |||
| virtual ~DropShadower(); | |||
| ~DropShadower(); | |||
| /** Attaches the DropShadower to the component you want to shadow. */ | |||
| void setOwner (Component* componentToFollow); | |||
| //============================================================================== | |||
| /** @internal */ | |||
| void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized); | |||
| /** @internal */ | |||
| void componentBroughtToFront (Component& component); | |||
| /** @internal */ | |||
| void componentParentHierarchyChanged (Component& component); | |||
| /** @internal */ | |||
| void componentVisibilityChanged (Component& component); | |||
| private: | |||
| //============================================================================== | |||
| Component* owner; | |||
| OwnedArray<Component> shadowWindows; | |||
| Image shadowImageSections[12]; | |||
| const int xOffset, yOffset; | |||
| const float alpha, blurRadius; | |||
| DropShadow shadow; | |||
| bool reentrant; | |||
| void componentMovedOrResized (Component&, bool, bool); | |||
| void componentBroughtToFront (Component&); | |||
| void componentParentHierarchyChanged (Component&); | |||
| void componentVisibilityChanged (Component&); | |||
| void updateShadows(); | |||
| void setShadowImage (const Image& src, int num, int w, int h, int sx, int sy); | |||
| void setShadowImage (const Image&, int num, int w, int h, int sx, int sy); | |||
| void bringShadowWindowsToFront(); | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DropShadower); | |||
| @@ -759,7 +759,7 @@ public: | |||
| shouldDeactivateTitleBar = oldDeactivate; | |||
| if (shadower != nullptr) | |||
| shadower->componentBroughtToFront (*component); | |||
| handleBroughtToFront(); | |||
| return true; | |||
| } | |||
| @@ -39,7 +39,7 @@ OldSchoolLookAndFeel::OldSchoolLookAndFeel() | |||
| setColour (PopupMenu::highlightedTextColourId, Colours::black); | |||
| setColour (TextEditor::focusedOutlineColourId, findColour (TextButton::buttonColourId)); | |||
| scrollbarShadow.setShadowProperties (2.2f, 0.5f, 0, 0); | |||
| scrollbarShadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 2, Point<int>())); | |||
| } | |||
| OldSchoolLookAndFeel::~OldSchoolLookAndFeel() | |||