Change LookAndFeel (knob graphics)

Hey all,

I’m an intern currently working on a plug-in being developed in JUCE. Most of the DSP and parameter management involved is done, so I’m turning to figure out how to implement graphics I will be provided replacing the default Slider::Rotary appearance.

In browsing old threads on the forum a bit, it seems that I will need a subclass of LookAndFeel that overrides drawRotarySlider with an implementation that uses a Graphics representation of the Image I want to use.

What I’m not understanding is how I set the LookAndFeel of a plugin to use my subclass… is it an attribute of the Editor, the Processor, or do Component objects individually reference a LookAndFeel? How can this be set?

Are there other ways to go about having custom graphics for knobs? Are there examples you could share either for implementations of PNG or SVG implementations of custom graphics for components?

1 Like

Hi TristineWild,

in essence you are right - you need to subclass the LookAndFeel class and override the drawRotarySlide method.

Have a look at this code for an example on how to do this:

Fabian

Thanks for the quick reply and example!

To use an image for the knob, I presume I would call one of the drawImage methods of the object ‘g’ within my overridden drawRotarySlider method?

Yup exactly. Martin has also written a great tutorial on this and we will be releasing it in the next few days.

2 Likes

In fact, if you’re using the “film strip” technique for drawing sliders/knobs, the only real difference between linear and rotary sliders is the mouse tracking. (The tutorial doesn’t cover this as the recommended way of drawing UI components in JUCE is draw them rather than use film strips.)

This means in your LookAndFeel that you can override these two methods to do nothing:
void drawLinearSliderBackground (Graphics&, int, int, int, int, float, float, float, const Slider::SliderStyle, Slider&) override { } void drawLinearSliderThumb (Graphics&, int, int, int, int, float, float, float, const Slider::SliderStyle, Slider&) override { }

Then you can simply have drawLinearSlider() and drawRotarySlider() draw the image into the specified rectangle based on the current slider value.

Fixed- the Image demoKnob was going out of scope but the ImageCache retained it for a short period. Solution is to declare demoKnob as an attribute of the editor

Perhaps this question is better suited for a new thread, but any idea as to why my image rotates properly for a couple seconds but then f&%ks off to oblivion / disappears?

class MyLookAndFeel : public LookAndFeel_V3
{
public:
    void drawRotarySlider (Graphics& g, int x, int y, int width, int height, float sliderPos,
                           const float rotaryStartAngle, const float rotaryEndAngle, Slider& slider) override
    {
        const float angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
        const float pivotX = ImageCache::getFromHashCode(0).getWidth()/2.0;
        const float pivotY = ImageCache::getFromHashCode(0).getHeight()/2.0;
        g.drawImageTransformed(ImageCache::getFromHashCode(0), AffineTransform::rotation(angle, pivotX, pivotY));
    }
};

. . . .

ImageTestAudioProcessorEditor::ImageTestAudioProcessorEditor (ImageTestAudioProcessor& p)
    : AudioProcessorEditor (&p), processor (p)
{
    //Load images from binary data into cache
    Image demoKnob = ImageFileFormat::loadFrom(ImgBin::demoKnob_png, (size_t) ImgBin::demoKnob_png);
    ImageCache::addImageToCache(demoKnob, 0);

. . . .

    //set LookAndFeel (to make use of images)
    lookandfeel = new MyLookAndFeel;
    sliderWetGain.setLookAndFeel(lookandfeel);

    setSize (400, 150);
}

. . . .

void ImageTestAudioProcessorEditor::resized()
{
    . . . .
	sliderWetGain.setBounds(340, 90, 50, 50);
}

This is just an excerpt of the code; an important detail might be that sliderWetGain also has a SliderAttachment connecting a parameter to it. Without using my custom LookAndFeel, controlling sliderWetGain performs fine.

It’s more likely to be the transform that you’re applying. You pivot around the centre of the source image, but you’re not taking account of the destination rectangle passed in x, y, width, and height.

(Guess you fixed it !)

Woops, deleted my post right as you replied.

I believe it was a silly OoP issue… since I was creating the Image object in the editor’s constructor, the image was going out of scope >.>

… scaling will probably still be an issue if the destination rectangle isn’t the same size as your source image. Note that the destination rectangle isn’t the same size as the component, it’ll be smaller, even if you get rid of the text box.

Thanks for pointing that out, I presume you’re right. Didn’t run into that as my image and the destination rectangle have identical dimensions atm.

Ah, I see. You were using ImageCache, which is why it survived for a few seconds before being garbage collected.

Yeah. I’m restoring the posted code should anybody stumble upon this thread and accidentally create the same bug.

Did this tutorial ever get released?

…did it? I can’t see it on Tutorials - JUCE

It’s been a while but I think it’s this one:
https://docs.juce.com/master/tutorial_look_and_feel_customisation.html