New layout system for JUCE

gui

#1

I’m a big fan of JUCE. I’ve been using it for a quite a while on some projects in development, and I really think it gets a lot of things right.

However, the one nagging disappointment I’ve had is in regards to user interface layout. I wish JUCE had a higher-level mechanism that didn’t involve pixel coordinates, rectangle offsets, etc. Some of the functions JUCE has for rectangle slicing are useful, but once you’re positioning things relative to pixels, everything is very fragile. Making room for a new component in the middle of a user interface is going to be a major pain. Or resizing several components to align in a specific way. I just wanted something at a higher-level.

So…I made my own. I’ve created a layout system based on JSON - I can describe at a very high level the layout of components, as well as their resizing behavior. These JSON interface layouts can be designed independently of the application they will be used in. I have a GUI testing app that just previews my layout system.

When it’s time to actually use a layout in an application, you simply create a JUCE window, add in a layout component, and tell it to load itself from a JSON file. The application doesn’t need to know what’s contained in the layout. Of course, it needs to be able to respond to events, but these events are at a high level - the application doesn’t need to know if it’s a button event or a slider event, etc.

In fact, besides laying out the user interface, this system acts as sort of a key/value repository. The application can ask what’s the value of “amplitude”, and whether amplitude is controlled with a slider, a text field, or a combo box, the application doesn’t care - it just gets a value back. Similarly, it can set the value of “amplitude”, and the appropriate control will get updated.

The JSON format right now has support for most of the standard JUCE components, plus some new ones I’ve created. And if an application has its own custom components that it wants to support in a layout, it can register a factory method that will parse components that are unknown to the basic system (a drawing canvas, a waveform view, etc.).

Finally, these JSON files can be loaded as regular files, or from JUCE resources (or any string, really). What I’m doing is loading them from files in Debug builds (so that I can tweak them live), and as resources in Release builds. But you could load from files in a release build too if you wanted to allow users to tweak the interface of your application.

Sound good? I’m very pleased with it - it’s going to save me tons of time, and let me focus on the interesting parts of my application.

Basic Intro

Here’s a basic introduction to my system. Not a tutorial really, but it does walk through the process of setting up a basic user interface. (Switch over to a regular YouTube page if you want to see the JSON text I’m typing in.)

Complex Layouts

And here’s an example of a more complex, actual application running with my layout system.

And I guess that’s the basic idea of what I’ve been working on. Thoughts?

-Mike


Best way to layout complex GUIs
#2

I love it. Would you be willing to share the source for collaboration?


#3

great work!


#4

its really cool. do you make your layout system public ??


#5

Awesome, and i don’t use that term often. JUCE (Jules), this would be a vey welcome addition to the framework!
Mike, can you also demonstrate how your coupled your data to the view, i mean can you explain how a slider/button actually set’s a value?


#6

Very nice. The basic idea is quite similar to my https://github.com/ffAudio/ffLayouts.
But I like the auto update on the file.

How do you access the components in your code later on? Retrieve pointers via Component->getComponentID()?

I experimented with creating components defined in the layout file, but I reckoned, that developers still want to have control of the lifetime of their components. So I went the other way round and let the layout lookup the instances by the componentID. Visual only components like a GroupComponent and Splitters I created on the fly, just as you are doing.

But I really like your work. Nice one.


#7

Wow! that looks fantastic - add animation syntax to that too and I think that would indeed be a very powerful system for JUCE users! (not that it isn’t already)


#8

Awesome work! I too am eager to understand better how this plays out with widget value setting/getting.
If this could play nice with the plugin parameter toolchain (i.e ValueTreeState/AudioProcessorParameter/Attachment/Widget) there could be a major bump of DSP work done by JUCE users :slight_smile:


#9

Much appreciation for your work, it looks like you put very much effort and thinking into that!

Now, if you accept some constructive criticism, there are a few things that spring to my mind while watching your presentation, because several times in the past I found myself writing similar systems for speeding up the tedious job of assembling widgets into complete GUIs.

In my past experience, what became apparent is that a data file (JSON in your case), can never be more expressive than C++ code. Sure, it may be quicker to write a JSON file for creating very simple layouts, but in the long run there is a high chance of systems like this one to develop the Inner-Platform Effect.

What generally happens is that, the more you need support for more widgets or more of their properties, you add customizations and features to your JSON syntax and parser, and that’s additional work needed just for the sake of replicating stuff that would already have been available if you were using the C++ in the first place.

Now, don’t get me wrong: your work is impressive and certainly useful in a series of circumstances.
My observation above is, so to say, a suggestion for you not to attempt to do with it more than what it is designed to, and also maybe as a preventive response to those who would like this included in JUCE.

The second observation is very specific to your first video: at around 6:00 you introduce a “verticalLayout” to stack the button and the color picker one above the other.

For that, I would have expected that you needed to put the button and color picker “inside” the newly created “verticalLayout”, i.e. that you had to nest them as “children” inside the curly braces where also the other properties for the “verticalLayout” are.

Instead, what seems to happen is that the children of the top level layout (which is horizontal) that come after the appearance of the vertical one, are arranged in vertical fashion inside the latter instead. Is that correct?


#10

I can not visit youtube video.Buy I agree your viewpoint, from juce 3.0 juce 4.3 to juce5.0, the GUI fewer changes.


#11

I partially agree with @yfede ,
This looks very promising but lacks some C++ <> json bridging rather than fully ditching json :wink:

if there’s something I can compliment Android over iOS is the use of XML Layouts and capability of responsiveness. (something Apple gradually took for making non-pixel-based layouts).

The key concept there bridging code and the layout is having Ids being able to eventually “objectifying” the elements created within the Layout.

Really hope JUCE 5.x would decide to incorporate this. especially where it’s already capable of compiling across multiple platforms!.


#12

Great work, really cool stuff!

Coincidentally, I’d been exploring similar ideas this week, as we’ve been discussing future GUI directions that we could take, and I’d been looking at how React Native works with an eye to pinching some tricks from it.

In general, I think you’re right that a dynamic layout defined as a document rather than a program is the thing that we’re currently lacking, and it’s the reason why people are jumping on these newer paradigms like React.

Some people on this thread are asking us to take your work into juce, but I think there are some reasons why we couldn’t do that in its current form. Some of the main requirements I’d be looking for in our solution would be:

  • flexbox as the layout engine. This has pretty much become the industry standard, and we added our own implementation for exactly this kind of use-case.
  • XML is a better fit than JSON. XML works better for this kind of hierarchy because the tag-names act as the class names, and it avoids needing to embed arrays for lists. I guess that’s why XML was the choice for things like React Native and XAML
  • Some kind of class definition system and CSS-like inheritance system for properties

An interesting trick that we could use could also be to use C++ classes that let you declare this kind of hierarchy directly in your code using a nice readable syntax… But to also allow it to be dynamically edited using a special version of the JUCE_LIVE_CONSTANT macro, so that you could try out changes to the layout at runtime, with all your custom widgets running, but which then gets compiled into your code without any actual XML or runtime parsing.

Not promising anything here in terms of what we’ll do, but your comments would be welcome!


#13

@jules +1 for react native implementation style.
Today it should be my first choice for a cross-platform UI tech.


#14

Agreed. I did a quick proof of concept for an interactive UI designer for a project I was approached about a while ago… and never completed… but it was based on using ComponentBuilder with PropertySet and exporting the layout as XML.

The vision was that you could group components on “Windows” which were basically other Components and link the widgets to objects.

I envisioned a layer system… similar to photoshop for the design app.

Rail


#15

@Lost_Marble isn’t it weird that a child component can change layout type of next components ?
Why dont you add a layout as a child of parent layout ?
Something like this:

{

"type": "horizontalLayout",
"children":[
    {
        "type": "textButton"
        "text": "button 1"
    },
    {
        "type": "verticalLayout"
        "children":[
        {
            "type": "textButton"
            "text": "button 2"
        },
        {
            "type": "textButton"
            "text": "button 3"
        }
     ]
    }
]

}


#16

I had the same complaint when I saw his initial system syntax.

Seems like he’s got a JSON->FlexBox parser that reloads the GUI when the JSON file changes. Neat!!


#17

In my opinion that is the only acceptable way to go: having to maintain a separate data file, however simple or complex, means that for most of the changes to the GUI one will have to edit the code AND the layout data file. Which is very inconvenient since these systems should be designed to speed up development, not to interrupt it by scattering stuff around.

…and again I am saying it from the standpoint of having tried this many times over the years.

At one time in particular, we thought that the hassle of editing the data file could have been mitigated by using SVG for that, which has the advantage of being XML under the hood AND when viewed gives a good idea of what the GUI will look like without the need of writing a custom parser for it.

The final result? a total trainwreck, used for a proof-of-concept project and then abandoned with hate.


#18

While we are talking about layout editors and PRODUCER - i’d really like to see a future version of the PJ editor change to creating SUBCLASSES of each widget and creating “event handlers” and handler code in the subclasses - rather than what happens now - which simply creates an instance of the widget class directly and then gives us a kind of shell “Placeholder” area where we have to add our code to pass a generic “clicked” event for example to each of our widgets.

So for example here - in a juice demo example app:

void MainComponent::buttonClicked (Button* buttonThatWasClicked)
{
//[UserbuttonClicked_Pre]
//[/UserbuttonClicked_Pre]

if (buttonThatWasClicked == quitButton)
{
    //[UserButtonCode_quitButton] -- add your button handler code here..

    JUCEApplication::quit();

    //[/UserButtonCode_quitButton]
}

//[UserbuttonClicked_Post]
//[/UserbuttonClicked_Post]

}

=====

just grabbing an example from some old code of mine to illustrate my point:

void WMixer_slMasterVolume::ON_ValueChanged()
{
float v = Get_Value();
float vol = v /100.00;
setMixdownInputBussOutputVolume (vol);
}

this scrap is from a subclass of a slider widget and gets a name incorporating the window it sits on AND the name of the widget itself - and then an ON_Valuechanged() override method ( event handler ) for it in which to place the code.

Apologies for being rushed - but i think this has to be the way going forward - for PJ to automatically create class declarations for SUBCLASSES of the widgets placed on a layout - and then also automatically create all the relevant override methods to act as “Event handlers” - leaving the juice app developer to just fill in the code for each method that needs to have mouse etc events handled.

This - of course is what most other IDE’s do under the surface that do “event handling”


#19

Or use lambda. Because having a dedicated class for each widget is a bit dated imho


#20

can you give some example code to illustrate how ?