Perhaps you can clean up this old bit of code that I had lying around… If you can polish it up into a component I could add to the library, that’d be cool…

```
class GradientDesigner : public Component,
public ChangeBroadcaster,
public ChangeListener,
public ButtonListener
{
public:
GradientDesigner (const ColourGradient& gradient_)
: gradient (gradient_),
selectedPoint (-1),
dragging (false),
draggingNewPoint (false),
draggingPos (0)
{
addChildComponent (&colourPicker);
linearButton.setButtonText ("Linear");
linearButton.setRadioGroupId (321);
linearButton.setConnectedEdges (TextButton::ConnectedOnRight | TextButton::ConnectedOnLeft);
radialButton.setButtonText ("Radial");
radialButton.setRadioGroupId (321);
radialButton.setConnectedEdges (TextButton::ConnectedOnRight | TextButton::ConnectedOnLeft);
addAndMakeVisible (&linearButton);
addAndMakeVisible (&radialButton);
linearButton.addListener (this);
radialButton.addListener (this);
colourPicker.addChangeListener (this);
}
void paint (Graphics& g)
{
g.fillAll (getLookAndFeel().findColour (ColourSelector::backgroundColourId));
g.fillCheckerBoard (previewArea, 10, 10, Colour (0xffdddddd), Colour (0xffffffff));
FillType f (gradient);
f.gradient->point1.setXY ((float) previewArea.getX(), (float) previewArea.getCentreY());
f.gradient->point2.setXY ((float) previewArea.getRight(), (float) previewArea.getCentreY());
g.setFillType (f);
g.fillRect (previewArea);
Path marker;
const float headSize = 4.5f;
marker.addLineSegment (Line<float> (0.0f, -2.0f, 0.0f, previewArea.getHeight() + 2.0f), 1.5f);
marker.addTriangle (0.0f, 1.0f, -headSize, -headSize, headSize, -headSize);
for (int i = 0; i < gradient.getNumColours(); ++i)
{
const double pos = gradient.getColourPosition (i);
const Colour col (gradient.getColour (i));
const AffineTransform t (AffineTransform::translation (previewArea.getX() + 0.5f + (float) (previewArea.getWidth() * pos),
(float) previewArea.getY()));
g.setColour (Colours::black.withAlpha (0.8f));
g.strokePath (marker, PathStrokeType (i == selectedPoint ? 2.0f : 1.5f), t);
g.setColour (i == selectedPoint ? Colours::lightblue : Colours::white);
g.fillPath (marker, t);
}
}
void resized()
{
previewArea.setBounds (7, 35, getWidth() - 14, 24);
const int w = 60;
linearButton.setBounds (getWidth() / 2 - w, 2, w, 20);
radialButton.setBounds (getWidth() / 2, 2, w, 20);
colourPicker.setBounds (0, previewArea.getBottom() + 16,
getWidth(), getHeight() - previewArea.getBottom() - 16);
}
void mouseDown (const MouseEvent& e)
{
dragging = false;
draggingNewPoint = false;
int point = getPointAt (e.x);
if (point >= 0)
setSelectedPoint (point);
}
void mouseDrag (const MouseEvent& e)
{
if ((! dragging) && ! e.mouseWasClicked())
{
preDragGradient = gradient;
const int mouseDownPoint = getPointAt (e.getMouseDownX());
if (mouseDownPoint >= 0)
{
if (mouseDownPoint > 0 && mouseDownPoint < gradient.getNumColours() - 1)
{
dragging = true;
draggingNewPoint = false;
draggingColour = gradient.getColour (mouseDownPoint);
preDragGradient.removeColour (mouseDownPoint);
selectedPoint = -1;
}
}
else
{
dragging = true;
draggingNewPoint = true;
selectedPoint = -1;
}
}
if (dragging)
{
draggingPos = jlimit (0.001, 0.999, (e.x - previewArea.getX()) / (double) previewArea.getWidth());
gradient = preDragGradient;
if (previewArea.expanded (6, 6).contains (e.x, e.y))
{
if (draggingNewPoint)
draggingColour = preDragGradient.getColourAtPosition (draggingPos);
selectedPoint = gradient.addColour (draggingPos, draggingColour);
updatePicker();
}
else
{
selectedPoint = -1;
}
sendChangeMessage();
repaint (previewArea.expanded (30, 30));
}
}
void mouseUp (const MouseEvent&)
{
dragging = false;
}
const ColourGradient& getGradient() const noexcept { return gradient; }
void setGradient (const ColourGradient& newGradient)
{
if (newGradient != gradient || selectedPoint < 0)
{
jassert (newGradient.getNumColours() > 1);
gradient = newGradient;
if (selectedPoint < 0)
selectedPoint = 0;
linearButton.setToggleState (! gradient.isRadial, false);
radialButton.setToggleState (gradient.isRadial, false);
updatePicker();
sendChangeMessage();
repaint();
}
}
void setSelectedPoint (int newIndex)
{
if (selectedPoint != newIndex)
{
selectedPoint = newIndex;
updatePicker();
repaint();
}
}
void changeListenerCallback (ChangeBroadcaster*)
{
if (selectedPoint >= 0 && (! dragging) && gradient.getColour (selectedPoint) != colourPicker.getCurrentColour())
{
gradient.setColour (selectedPoint, colourPicker.getCurrentColour());
repaint (previewArea);
sendChangeMessage();
}
}
void buttonClicked (Button* b)
{
ColourGradient g (gradient);
g.isRadial = (b == &radialButton);
setGradient (g);
}
private:
StoredSettings::ColourSelectorWithSwatches colourPicker;
ColourGradient gradient;
int selectedPoint;
bool dragging, draggingNewPoint;
double draggingPos;
Colour draggingColour;
ColourGradient preDragGradient;
Rectangle<int> previewArea;
TextButton linearButton, radialButton;
void updatePicker()
{
colourPicker.setVisible (selectedPoint >= 0);
if (selectedPoint >= 0)
colourPicker.setCurrentColour (gradient.getColour (selectedPoint));
}
int getPointAt (const int x) const
{
int best = -1;
double bestDiff = 6;
for (int i = gradient.getNumColours(); --i >= 0;)
{
const double pos = previewArea.getX() + previewArea.getWidth() * gradient.getColourPosition (i);
const double diff = std::abs (pos - x);
if (diff < bestDiff)
{
bestDiff = diff;
best = i;
}
}
return best;
}
};
```