New to GUI programming

I wanted to avoid using JUCE. This isn’t to meant to offend anyone, but I thought as beginner using smaller libraries, or simply including vst directly would be the best choice. It’s been about a year since I started exploring my options for audio programming, and now I am much better informed. Plugins are the way to go. The host handles the final audio output, while I just take care of the signal processing.

Now I want to build custom GUIs built with inkscape or GIMP, but most gui code is prebuilt. How do I take something I made in a graphics editor and put it in my gui?

Also is there a way to everything I want to do with my vst without JUCE or Qt? Can I use openGL for 2D GUI implimentation?

If you’re using SVGs for your UI you’ll want to check out the Drawable class. You can load an SVG with it and place the result directly on screen as a type of Component!

If you’re using raster graphics like sprite sheets and such you will be relying on loading and drawing Image objects (or similarly, using the DrawableImage type).

As for getting the SVG or raster graphics “into” JUCE there’s some helpful info in this blog post about it. Basically you can add files to your project that the Projucer app will convert to an array of bytes (const char*) that you can then give JUCE to load.

You could write it yourself, but depending on your needs it may be fairly complicated/involved. If you’re looking to do Object-Oriented-style interfaces like what JUCE offers, you may be best suited using the library.

If you really just want to easily put controls on screen without hassle or an Object-Oriented API I would look at something like Dear IMGUI. That kind of approach lets you define your UI all as a series of functions which can handle events and draw to screen, so putting the interface and actions together would be pretty straightforward.

1 Like

Great! Now I want to scrap the default audio code given to me by the Projucer and simply include the vst specification. How do I go about this?

I would take a look at the VST docs as well as JUCE’s own VST wrapper code, if you want to implement a VST from scratch

Why? Implementing all the boilerplate code for a VST2(*) (let alone VST3, AU etc plugins) is a massive hassle. Juce makes all that much more simpler with the AudioProcessor base class and the helper classes for that.

(*) It should be mentioned that as a newcomer to plugin development, you can’t release VST2 plugins publicly anymore, though. Steinberg had a deadline in October 2018 for obtaining the VST2 license. If you are able to obtain the VST2 SDK from somewhere, you can of course privately build and use VST2 plugins yourself.

That’s very not beginner attitude. And if you don’t really need some complex graphs this would actually back fire you in the long run.

JUCE actually provides a toolkit that’s much beginner friendly than others.
You can try iPlug2 if you want some bleeding edge and more permissive license out of the box. but for getting started JUCE is the way to go.

As @Xenakios asked you rightfully,

Why? Implementing all the boilerplate code

In the end, all “plug-ins” got the same concepts. it’s not that different and if you really need to understand something (or track/fix a nasty bug) you can just dive into that code instead.

2 Likes

Having tried more or less everything.

In order of preference:

  • Do it with native JUCE calls. Just look at how you did it in GIMP or whatever and repeat it with calls to Graphics::drawRect, setColour, setGradientFill(…).
  • Load an SVG and render that (typically fancy curves and icons are easier as SVGs
  • Use a PNG

Things I would avoid:

  • The projucer’s UI editor
  • Implementing a custom framework for converting things from drawing programs directly into UI code, or trying to render from some UI template file
  • Using LookAndFeel for buttons. Just inherit from juce::Button and override paintButton(…).
4 Likes

Here’s our latest:

https://www.deviousmachines.com/duck/

The curved thing I think is an SVG. The logos I think are SVGs as well, though we’ve used PNGs before. The icons are either SVGs, juce::Path for the curve shapes or from Font Awesome (the power button).

Everything else, including the knobs are just calls to basic juce::Graphics methods.

3 Likes

Exactly! Plus I use SVG just for icons/logos too. I rewrote to JUCE a huge Windows application which I created 15 years ago, did GUI’s for embedded Linux devices as well as iOS/Android devices so this approach seems to fit everywhere now. No need to use some external tools etc.

I have mixed feelings about LookAndFeel and Buttons too. For example I don’t fully understand why Label has its own font which can be overridden by L&F eventually, but TextButton doesn’t and you have to override a method in a L&F class. I created DrawableTextButton which can have a SVG icon and text placed freely and extended Look&Feel class with methods to paint this button according to the L&F paradigm but it caused circular references between both classes and a need of dynamic cast etc. It works but I feel too that a better (or maybe more convenient) approach is just to override paintButton in the end.

I would disagree with this. That would mean you need lots of Button classes around each for a particular type of drawing. This makes it very difficult to change colour themes or even LAFs entirely.

What I’ve started doing more and more these days is using Component::getProperties() to store additional information about buttons. This means you can do things like add icons/paths, specify corner size etc. and just draw it accordingly from the LAF without having to create new classes.

One step further from that would be to create multiple free drawMyButton (...) functions which get delegated to depending on properties set.

In general, the more UI code I can move to a LAF type class (even with my own methods), the happier I am. It makes it much, much easier to change graphics this way and keeps all the drawing logic out of the behavioural classes and behind essentially a single API.

5 Likes

Good idea, I wish I knew about Component::getProperties before…

Colour themes we do with some global statics and a message sent to all components to repaint(). Works perfectly.

Corner sizes are global constexpr.

Having a single class with loads of if/switch statements to handle all the button possibilities has always just got me into a complete mess after a while. Whereas a simple button you know where to look for the problems, and if the style needs changing you know exactly what you are breaking:)

class MainToolbarButton
	:
	public Button
{
public:
	MainToolbarButton(int icon)
		:
		Button({}),
		icon(icon)
	{}

	void paintButton(Graphics& g, bool isMouseOverButton, bool) override 
	{
        g.setColour(LcColours::mainToolbarFg.contrasting(isMouseOverButton ? 0.1f : 0.0f));
        jcf::FontAwesomeIcons::drawIcon(g, icon, getLocalBounds().toFloat());
	}

protected:
	int icon;
};

This is ok, unless you have drastically different colour schemes which require differences to the way you actually draw things. For example, our light scheme has gradients used for shadows with are not only lighter than the dark themes but actually go different ways. Similarly, highlights for buttons are just completely different in dark/light themes.

Ok, but what if you have buttons where you want different corner sizes? Some could be lozenge shape, some could be a soft round, some hard etc.


I think having dedicated classes works up to a certain size but in something like Waveform where we have thousands of buttons/controls/elements etc. it doesn’t really scale.
Simply adding a new ID and then specialising drawing based on that, even if all it does is delegate to a named method is much simpler IMO.

In your example, using a ShapeButton or similar would be much more preferable (although I do have a subclass which simply takes a path in its constructor).

We just have two colours one for the top and bottom of the gradient in the gobal statics now.

Well exactly. So where you want things different we just use a different class. Related things tend to use the same button rendering code, but to have a global lookandfeel that has all the button rendering code is just perverse complication :slight_smile:

And anyway I can’t figure out why, even if you wanted all the rendering code in one place why you’d not implement WaveformButton as a parent class containing a master complex paintButton(…) function with all your cases in. Having generic Button classes that go back to a separate master look and feel class only really makes sense if you want to swap the complete look and feel of the buttons around on the fly, and I’m not sure I can think of a good looking app that does that …?

Well what I’m saying is that we didn’t use to have several LAFs that drew everything, then I had to add support for multiple, user defined colour schemes, then half the drawing looked terrible and the only sensible way to fix it was to create multiple LAF instances. For example, we have at least a dark and light LAF and also a “simple” LAF for low-cpu usage (although these days I’ve simplified that by having a “low-detail” member in the LAF that is checked in all of the drawing methods to avoid overly complicated shapes and shadows etc.).

I guess it comes down to personal preference but in general I’ve simply found it much easier to reason about our drawing since moving towards everything having LAF methods rather than being hidden in Component subclasses.

I think maybe we’re talking at slightly cross purposes as for button there is generally less difference. For sliders though, we have multiple styles that can absolutely change dynamically depending on if they have active modifications (like LFOs etc.) or are currently being assigned to from MIDI etc. When you have components that change the way they look on a very dynamic basis like this, I’ve found it easier to use dynamic variables like properties to determine this behaviour. For static components, maybe a button subclass is more explicit.

In all honesty we have a mixture of both of these methods depending on the purpose. We do have a “StandardButton” class that draws most of our buttons but I’ve never particularly liked that. We also have several different button subclasses that have special behaviour but again, these are probably there because they predate std::function and the much more customisable way of using buttons these days.


I guess the overriding thing for me is that in my Component building I think of “this is a button, users will click on it to trigger some event”, in my lookAndFeelChanged method I think “some UI related property has changed, update the button properties to reflect this”, then in the LAF methods I think “this is how I draw a button with these properties”.

Again, maybe not necessary for small apps but I’ve found this scales much better for our use cases.

3 Likes