mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
DragAndDropContainer: Allow custom scaling
This commit is contained in:
parent
6468088100
commit
34fdea0708
9 changed files with 195 additions and 72 deletions
|
|
@ -4,6 +4,26 @@ JUCE breaking changes
|
|||
develop
|
||||
=======
|
||||
|
||||
Change
|
||||
------
|
||||
ListBox::createSnapshotOfRows now returns ScaledImage instead of Image.
|
||||
|
||||
Possible Issues
|
||||
---------------
|
||||
User code that overrides this function will fail to build.
|
||||
|
||||
Workaround
|
||||
----------
|
||||
To emulate the old behaviour, simply wrap the Image that was previous returned
|
||||
into a ScaledImage and return that instead.
|
||||
|
||||
Rationale
|
||||
---------
|
||||
Returning a ScaledImage allows the overriding function to specify the scale
|
||||
at which the image should be drawn. Returning an oversampled image will provide
|
||||
smoother-looking results on high resolution displays.
|
||||
|
||||
|
||||
Change
|
||||
------
|
||||
AudioFrameRate::frameRate is now a class type instead of an enum.
|
||||
|
|
|
|||
82
modules/juce_graphics/images/juce_ScaledImage.h
Normal file
82
modules/juce_graphics/images/juce_ScaledImage.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/**
|
||||
An image that will be resampled before it is drawn.
|
||||
|
||||
A plain Image only stores plain pixels, but does not store any information
|
||||
about how these pixels correspond to points. This means that if the image's
|
||||
dimensions are interpreted as points, then the image will be blurry when
|
||||
drawn on high resolution displays. If the image's dimensions are instead
|
||||
interpreted as corresponding to exact pixel positions, then the logical
|
||||
size of the image will change depending on the scale factor of the screen
|
||||
used to draw it.
|
||||
|
||||
The ScaledImage class is designed to store an image alongside a scale
|
||||
factor that informs a renderer how to convert between the image's pixels
|
||||
and points.
|
||||
*/
|
||||
class JUCE_API ScaledImage
|
||||
{
|
||||
public:
|
||||
/** Creates a ScaledImage with an invalid image and unity scale.
|
||||
*/
|
||||
ScaledImage() = default;
|
||||
|
||||
/** Creates a ScaledImage from an Image, where the dimensions of the image
|
||||
in pixels are exactly equal to its dimensions in points.
|
||||
*/
|
||||
explicit ScaledImage (const Image& imageIn)
|
||||
: ScaledImage (imageIn, 1.0) {}
|
||||
|
||||
/** Creates a ScaledImage from an Image, using a custom scale factor.
|
||||
|
||||
A scale of 1.0 means that the image's dimensions in pixels is equal to
|
||||
its dimensions in points.
|
||||
|
||||
A scale of 2.0 means that the image contains 2 pixels per point in each
|
||||
direction.
|
||||
*/
|
||||
ScaledImage (const Image& imageIn, double scaleIn)
|
||||
: image (imageIn), scaleFactor (scaleIn) {}
|
||||
|
||||
/** Returns the image at its original dimentions. */
|
||||
Image getImage() const { return image; }
|
||||
|
||||
/** Returns the image's scale. */
|
||||
double getScale() const { return scaleFactor; }
|
||||
|
||||
/** Returns the bounds of this image expressed in points. */
|
||||
Rectangle<double> getScaledBounds() const { return image.getBounds().toDouble() / scaleFactor; }
|
||||
|
||||
private:
|
||||
Image image;
|
||||
double scaleFactor = 1.0;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -140,6 +140,7 @@ namespace juce
|
|||
#include "contexts/juce_GraphicsContext.h"
|
||||
#include "contexts/juce_LowLevelGraphicsContext.h"
|
||||
#include "images/juce_Image.h"
|
||||
#include "images/juce_ScaledImage.h"
|
||||
#include "colour/juce_FillType.h"
|
||||
#include "native/juce_RenderingHelpers.h"
|
||||
#include "contexts/juce_LowLevelGraphicsSoftwareRenderer.h"
|
||||
|
|
|
|||
|
|
@ -35,14 +35,15 @@ class DragAndDropContainer::DragImageComponent : public Component,
|
|||
private Timer
|
||||
{
|
||||
public:
|
||||
DragImageComponent (const Image& im,
|
||||
DragImageComponent (const ScaledImage& im,
|
||||
const var& desc,
|
||||
Component* const sourceComponent,
|
||||
const MouseInputSource* draggingSource,
|
||||
DragAndDropContainer& ddc,
|
||||
Point<int> offset)
|
||||
: sourceDetails (desc, sourceComponent, Point<int>()),
|
||||
image (im), owner (ddc),
|
||||
image (im),
|
||||
owner (ddc),
|
||||
mouseDragSource (draggingSource->getComponentUnderMouse()),
|
||||
imageOffset (transformOffsetCoordinates (sourceComponent, offset)),
|
||||
originalInputSourceIndex (draggingSource->getIndex()),
|
||||
|
|
@ -83,7 +84,7 @@ public:
|
|||
g.fillAll (Colours::white);
|
||||
|
||||
g.setOpacity (1.0f);
|
||||
g.drawImageAt (image, 0, 0);
|
||||
g.drawImage (image.getImage(), getLocalBounds().toFloat());
|
||||
}
|
||||
|
||||
void mouseUp (const MouseEvent& e) override
|
||||
|
|
@ -164,7 +165,7 @@ public:
|
|||
forceMouseCursorUpdate();
|
||||
}
|
||||
|
||||
void updateImage (const Image& newImage)
|
||||
void updateImage (const ScaledImage& newImage)
|
||||
{
|
||||
image = newImage;
|
||||
updateSize();
|
||||
|
|
@ -218,7 +219,7 @@ public:
|
|||
DragAndDropTarget::SourceDetails sourceDetails;
|
||||
|
||||
private:
|
||||
Image image;
|
||||
ScaledImage image;
|
||||
DragAndDropContainer& owner;
|
||||
WeakReference<Component> mouseDragSource, currentlyOverComp;
|
||||
const Point<int> imageOffset;
|
||||
|
|
@ -229,7 +230,8 @@ private:
|
|||
|
||||
void updateSize()
|
||||
{
|
||||
setSize (image.getWidth(), image.getHeight());
|
||||
const auto bounds = image.getScaledBounds().toNearestInt();
|
||||
setSize (bounds.getWidth(), bounds.getHeight());
|
||||
}
|
||||
|
||||
void forceMouseCursorUpdate()
|
||||
|
|
@ -388,17 +390,13 @@ private:
|
|||
|
||||
|
||||
//==============================================================================
|
||||
DragAndDropContainer::DragAndDropContainer()
|
||||
{
|
||||
}
|
||||
DragAndDropContainer::DragAndDropContainer() = default;
|
||||
|
||||
DragAndDropContainer::~DragAndDropContainer()
|
||||
{
|
||||
}
|
||||
DragAndDropContainer::~DragAndDropContainer() = default;
|
||||
|
||||
void DragAndDropContainer::startDragging (const var& sourceDescription,
|
||||
Component* sourceComponent,
|
||||
Image dragImage,
|
||||
const ScaledImage& dragImage,
|
||||
const bool allowDraggingToExternalWindows,
|
||||
const Point<int>* imageOffsetFromMouse,
|
||||
const MouseInputSource* inputSourceCausingDrag)
|
||||
|
|
@ -414,55 +412,53 @@ void DragAndDropContainer::startDragging (const var& sourceDescription,
|
|||
return;
|
||||
}
|
||||
|
||||
auto lastMouseDown = draggingSource->getLastMouseDownPosition().roundToInt();
|
||||
Point<int> imageOffset;
|
||||
const auto lastMouseDown = draggingSource->getLastMouseDownPosition().roundToInt();
|
||||
|
||||
if (dragImage.isNull())
|
||||
struct ImageAndOffset
|
||||
{
|
||||
dragImage = sourceComponent->createComponentSnapshot (sourceComponent->getLocalBounds())
|
||||
.convertedToFormat (Image::ARGB);
|
||||
ScaledImage image;
|
||||
Point<double> offset;
|
||||
};
|
||||
|
||||
dragImage.multiplyAllAlphas (0.6f);
|
||||
|
||||
auto lo = 150;
|
||||
auto hi = 400;
|
||||
|
||||
auto relPos = sourceComponent->getLocalPoint (nullptr, lastMouseDown);
|
||||
auto clipped = dragImage.getBounds().getConstrainedPoint (relPos);
|
||||
Random random;
|
||||
|
||||
for (auto y = dragImage.getHeight(); --y >= 0;)
|
||||
{
|
||||
auto dy = (y - clipped.getY()) * (y - clipped.getY());
|
||||
|
||||
for (auto x = dragImage.getWidth(); --x >= 0;)
|
||||
{
|
||||
auto dx = x - clipped.getX();
|
||||
auto distance = roundToInt (std::sqrt (dx * dx + dy));
|
||||
|
||||
if (distance > lo)
|
||||
{
|
||||
auto alpha = (distance > hi) ? 0
|
||||
: (float) (hi - distance) / (float) (hi - lo)
|
||||
+ random.nextFloat() * 0.008f;
|
||||
|
||||
dragImage.multiplyAlphaAt (x, y, alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imageOffset = clipped;
|
||||
}
|
||||
else
|
||||
const auto imageToUse = [&]() -> ImageAndOffset
|
||||
{
|
||||
if (imageOffsetFromMouse == nullptr)
|
||||
imageOffset = dragImage.getBounds().getCentre();
|
||||
else
|
||||
imageOffset = dragImage.getBounds().getConstrainedPoint (-*imageOffsetFromMouse);
|
||||
}
|
||||
if (! dragImage.getImage().isNull())
|
||||
return { dragImage, imageOffsetFromMouse != nullptr ? dragImage.getScaledBounds().getConstrainedPoint (-imageOffsetFromMouse->toDouble())
|
||||
: dragImage.getScaledBounds().getCentre() };
|
||||
|
||||
auto* dragImageComponent = dragImageComponents.add (new DragImageComponent (dragImage, sourceDescription, sourceComponent,
|
||||
draggingSource, *this, imageOffset));
|
||||
const auto scaleFactor = 2.0;
|
||||
auto image = sourceComponent->createComponentSnapshot (sourceComponent->getLocalBounds(), true, (float) scaleFactor)
|
||||
.convertedToFormat (Image::ARGB);
|
||||
image.multiplyAllAlphas (0.6f);
|
||||
|
||||
const auto relPos = sourceComponent->getLocalPoint (nullptr, lastMouseDown).toDouble();
|
||||
const auto clipped = (image.getBounds().toDouble() / scaleFactor).getConstrainedPoint (relPos);
|
||||
|
||||
Image fade (Image::SingleChannel, image.getWidth(), image.getHeight(), true);
|
||||
Graphics fadeContext (fade);
|
||||
|
||||
ColourGradient gradient;
|
||||
gradient.isRadial = true;
|
||||
gradient.point1 = clipped.toFloat() * scaleFactor;
|
||||
gradient.point2 = gradient.point1 + Point<float> (0.0f, scaleFactor * 400.0f);
|
||||
gradient.addColour (0.0, Colours::white);
|
||||
gradient.addColour (0.375, Colours::white);
|
||||
gradient.addColour (1.0, Colours::transparentWhite);
|
||||
|
||||
fadeContext.setGradientFill (gradient);
|
||||
fadeContext.fillAll();
|
||||
|
||||
Image composite (Image::ARGB, image.getWidth(), image.getHeight(), true);
|
||||
Graphics compositeContext (composite);
|
||||
|
||||
compositeContext.reduceClipRegion (fade, {});
|
||||
compositeContext.drawImageAt (image, 0, 0);
|
||||
|
||||
return { ScaledImage (composite, scaleFactor), clipped };
|
||||
}();
|
||||
|
||||
auto* dragImageComponent = dragImageComponents.add (new DragImageComponent (imageToUse.image, sourceDescription, sourceComponent,
|
||||
draggingSource, *this, imageToUse.offset.roundToInt()));
|
||||
|
||||
if (allowDraggingToExternalWindows)
|
||||
{
|
||||
|
|
@ -527,7 +523,7 @@ var DragAndDropContainer::getDragDescriptionForIndex (int index) const
|
|||
return dragImageComponents.getUnchecked (index)->sourceDetails.description;
|
||||
}
|
||||
|
||||
void DragAndDropContainer::setCurrentDragImage (const Image& newImage)
|
||||
void DragAndDropContainer::setCurrentDragImage (const ScaledImage& newImage)
|
||||
{
|
||||
// If you are performing drag and drop in a multi-touch environment then
|
||||
// you should use the setDragImageForIndex() method instead!
|
||||
|
|
@ -536,7 +532,7 @@ void DragAndDropContainer::setCurrentDragImage (const Image& newImage)
|
|||
dragImageComponents[0]->updateImage (newImage);
|
||||
}
|
||||
|
||||
void DragAndDropContainer::setDragImageForIndex (int index, const Image& newImage)
|
||||
void DragAndDropContainer::setDragImageForIndex (int index, const ScaledImage& newImage)
|
||||
{
|
||||
if (isPositiveAndBelow (index, dragImageComponents.size()))
|
||||
dragImageComponents.getUnchecked (index)->updateImage (newImage);
|
||||
|
|
|
|||
|
|
@ -94,11 +94,27 @@ public:
|
|||
*/
|
||||
void startDragging (const var& sourceDescription,
|
||||
Component* sourceComponent,
|
||||
Image dragImage = Image(),
|
||||
const ScaledImage& dragImage = ScaledImage(),
|
||||
bool allowDraggingToOtherJuceWindows = false,
|
||||
const Point<int>* imageOffsetFromMouse = nullptr,
|
||||
const MouseInputSource* inputSourceCausingDrag = nullptr);
|
||||
|
||||
[[deprecated ("This overload does not allow the image's scale to be specified. Use the other overload of startDragging instead.")]]
|
||||
void startDragging (const var& sourceDescription,
|
||||
Component* sourceComponent,
|
||||
Image dragImage,
|
||||
bool allowDraggingToOtherJuceWindows = false,
|
||||
const Point<int>* imageOffsetFromMouse = nullptr,
|
||||
const MouseInputSource* inputSourceCausingDrag = nullptr)
|
||||
{
|
||||
startDragging (sourceDescription,
|
||||
sourceComponent,
|
||||
ScaledImage (dragImage),
|
||||
allowDraggingToOtherJuceWindows,
|
||||
imageOffsetFromMouse,
|
||||
inputSourceCausingDrag);
|
||||
}
|
||||
|
||||
/** Returns true if something is currently being dragged. */
|
||||
bool isDragAndDropActive() const;
|
||||
|
||||
|
|
@ -130,13 +146,19 @@ public:
|
|||
|
||||
@see setDragImageForIndex
|
||||
*/
|
||||
void setCurrentDragImage (const Image& newImage);
|
||||
void setCurrentDragImage (const ScaledImage& newImage);
|
||||
|
||||
[[deprecated ("This overload does not allow the image's scale to be specified. Use the other overload of setCurrentDragImage instead.")]]
|
||||
void setCurrentDragImage (const Image& newImage) { setCurrentDragImage (ScaledImage (newImage)); }
|
||||
|
||||
/** Same as the setCurrentDragImage() method but takes a touch index parameter.
|
||||
|
||||
@see setCurrentDragImage
|
||||
*/
|
||||
void setDragImageForIndex (int index, const Image& newImage);
|
||||
*/
|
||||
void setDragImageForIndex (int index, const ScaledImage& newImage);
|
||||
|
||||
[[deprecated ("This overload does not allow the image's scale to be specified. Use the other overload of setDragImageForIndex instead.")]]
|
||||
void setDragImageForIndex (int index, const Image& newImage) { setDragImageForIndex (index, ScaledImage (newImage)); }
|
||||
|
||||
/** Utility to find the DragAndDropContainer for a given Component.
|
||||
|
||||
|
|
|
|||
|
|
@ -1040,7 +1040,7 @@ void ListBox::repaintRow (const int rowNumber) noexcept
|
|||
repaint (getRowPosition (rowNumber, true));
|
||||
}
|
||||
|
||||
Image ListBox::createSnapshotOfRows (const SparseSet<int>& rows, int& imageX, int& imageY)
|
||||
ScaledImage ListBox::createSnapshotOfRows (const SparseSet<int>& rows, int& imageX, int& imageY)
|
||||
{
|
||||
Rectangle<int> imageArea;
|
||||
auto firstRow = getRowContainingPosition (0, viewport->getY());
|
||||
|
|
@ -1062,7 +1062,8 @@ Image ListBox::createSnapshotOfRows (const SparseSet<int>& rows, int& imageX, in
|
|||
imageX = imageArea.getX();
|
||||
imageY = imageArea.getY();
|
||||
|
||||
auto listScale = Component::getApproximateScaleFactorForComponent (this);
|
||||
const auto additionalScale = 2.0f;
|
||||
const auto listScale = Component::getApproximateScaleFactorForComponent (this) * additionalScale;
|
||||
Image snapshot (Image::ARGB,
|
||||
roundToInt ((float) imageArea.getWidth() * listScale),
|
||||
roundToInt ((float) imageArea.getHeight() * listScale),
|
||||
|
|
@ -1075,9 +1076,9 @@ Image ListBox::createSnapshotOfRows (const SparseSet<int>& rows, int& imageX, in
|
|||
if (auto* rowComp = viewport->getComponentForRowIfOnscreen (firstRow + i))
|
||||
{
|
||||
Graphics g (snapshot);
|
||||
g.setOrigin (getLocalPoint (rowComp, Point<int>()) - imageArea.getPosition());
|
||||
g.setOrigin ((getLocalPoint (rowComp, Point<int>()) - imageArea.getPosition()) * additionalScale);
|
||||
|
||||
auto rowScale = Component::getApproximateScaleFactorForComponent (rowComp);
|
||||
const auto rowScale = Component::getApproximateScaleFactorForComponent (rowComp) * additionalScale;
|
||||
|
||||
if (g.reduceClipRegion (rowComp->getLocalBounds() * rowScale))
|
||||
{
|
||||
|
|
@ -1090,7 +1091,7 @@ Image ListBox::createSnapshotOfRows (const SparseSet<int>& rows, int& imageX, in
|
|||
}
|
||||
}
|
||||
|
||||
return snapshot;
|
||||
return { snapshot, additionalScale };
|
||||
}
|
||||
|
||||
void ListBox::startDragAndDrop (const MouseEvent& e, const SparseSet<int>& rowsToDrag, const var& dragDescription, bool allowDraggingToOtherWindows)
|
||||
|
|
|
|||
|
|
@ -538,7 +538,7 @@ public:
|
|||
|
||||
@see Component::createComponentSnapshot
|
||||
*/
|
||||
virtual Image createSnapshotOfRows (const SparseSet<int>& rows, int& x, int& y);
|
||||
virtual ScaledImage createSnapshotOfRows (const SparseSet<int>& rows, int& x, int& y);
|
||||
|
||||
/** Returns the viewport that this ListBox uses.
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ public:
|
|||
|
||||
if (DragAndDropContainer* const dnd = DragAndDropContainer::findParentDragContainerFor (this))
|
||||
{
|
||||
dnd->startDragging (Toolbar::toolbarDragDescriptor, getParentComponent(), Image(), true, nullptr, &e.source);
|
||||
dnd->startDragging (Toolbar::toolbarDragDescriptor, getParentComponent(), ScaledImage(), true, nullptr, &e.source);
|
||||
|
||||
if (ToolbarItemComponent* const tc = getToolbarItemComponent())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -476,14 +476,15 @@ private:
|
|||
{
|
||||
pos.setSize (pos.getWidth(), item.itemHeight);
|
||||
|
||||
const auto additionalScale = 2.0f;
|
||||
auto dragImage = Component::createComponentSnapshot (pos,
|
||||
true,
|
||||
Component::getApproximateScaleFactorForComponent (itemComponent));
|
||||
Component::getApproximateScaleFactorForComponent (itemComponent) * additionalScale);
|
||||
|
||||
dragImage.multiplyAllAlphas (0.6f);
|
||||
|
||||
auto imageOffset = pos.getPosition() - e.getPosition();
|
||||
dragContainer->startDragging (dragDescription, &owner, dragImage, true, &imageOffset, &e.source);
|
||||
dragContainer->startDragging (dragDescription, &owner, { dragImage, additionalScale }, true, &imageOffset, &e.source);
|
||||
|
||||
scopedScrollDisabler = std::make_unique<ScopedDisableViewportScroll> (*itemComponent);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue