New layout system for JUCE

gui

#21
MainComponent:MainComponent()
{
   mpSlider = Slider("myKnob")
   mpSlider->clicked = [this]() { myKnobClicked(*mpSlider) }
}

void MainComponent::myKnobClicked(Slider &slider)
{
}

Goodbye Listeners and Callbacks, Hello Lambdas?
#22

A few responses:

XML vs JSON: I find XML very heavy and not “readable”. My goal here was to make it quick and easy to lay out components, and my personal preference here won out. My eyes are quick at matching brackets, but all those tags in XML to me just make it cumbersome and difficult to edit. Actually, I first considered TOML to simplify things even more, but I think it may not be expressive enough.

Layout file vs C++, or doing “too much” in a data file. I get what you’re saying, but there’s nothing forcing me to use this for every bit of user interface in an application. You can totally mix & match, and even embed one of these layouts in the midst of hand-edited C++ components.

Layouts within layouts: Some of you were watching the JSON very closely! Yes, there’s an unexpected trick happening here. In my initial implmentation (and this still works), a layout within a layout could have its own children. In fact, it must have its own children if you want a nested layout. Formally, this makes sense - you’re building a tree or graph, and you must have a way for nodes to have children, and those nodes to have children.

But in practice, this gets cumbersome. Keeping track of matching brackets, and arrays within arrays gets kind of tedious. It’s totally doable, but my goal here was to make it very easy to define a layout, as well as change a layout later. So I added the ability to instead of placing children inside a layout, wrap them on either side.

You can make the argument that this isn’t a properly formalized structure (having the children actually inside the parent layout in the JSON reflects what is actually happening), and I get that. But my priority here is ease and simplicity over punctuation. I think the result is more readable, even if it may be a “cheat”.

Here’s a layout with nested layouts:

{ "comment" : "This file is supposed to be a simple layout that contains sub-layouts, emulating an app window with a toolbar and status bar",
"type" : "layout",
"padding" : 0,
"maxColumns" : 1,
"widgetHeight" : 24,
"textEditorHeight" : 16,
"showLayout" : true,
"rowScaling" : [ 0, 1, 0 ],

"children" : [
	{
		"type" : "layout",
		"horizontal" : "fill",
		"maxColumns" : 0,
		"colScaling" : [ 0, 0, 0, 1, 0, 0 ],
		"children" : [
			{
				"type" : "dummyTool",
				"text" : "Tool 1",
				"width" : 60,
				"height" : 60
			},
			{
				"type" : "dummyTool",
				"text" : "Tool 2",
				"width" : 60,
				"height" : 60
			},
			{
				"type" : "dummyTool",
				"text" : "Tool 3",
				"width" : 60,
				"height" : 60
			},
			{
				"width" : 120,
				"height" : 60
			},
			{
				"type" : "dummyTool",
				"text" : "Tool 4",
				"width" : 60,
				"height" : 60
			},
			{
				"type" : "dummyTool",
				"text" : "Tool 5",
				"width" : 60,
				"height" : 60
			}
		]
	},
	{
		"type" : "layout",
		"horizontal" : "fill",
		"vertical" : "fill",
		"maxColumns" : 0,
		"padding" : 0,
		"colScaling" : [ 0, 1, 0 ],
		"children" : [
			{
				"type" : "dummyToolPalette",
				"text" : "Tools",
				"width" : 60,
				"height" : 60,
				"horizontal" : "fill",
				"vertical" : "fill",
			},
			{
				"type" : "dummyCanvas",
				"text" : "Canvas",
				"width" : 600,
				"height" : 400,
				"horizontal" : "fill",
				"vertical" : "fill",
			},
			{
				"type" : "dummyLayers",
				"text" : "Layers",
				"width" : 200,
				"height" : 60,
				"horizontal" : "fill",
				"vertical" : "fill",
			}
		]
	},
	{
		"type" : "dummyStatusBar",
		"id" : "stats",
		"text" : "Status Bar",
		"width" : 100,
		"height" : 20,
		"horizontal" : "fill",
		"vertical" : "fill"
	}
]

}

And here’s the same thing, but “flattened”. Instead of layouts within layouts, a sub-layout can begin and end anywhere:

{ "comment" : "This file is supposed to be a simple layout that contains sub-layouts, emulating an app window with a toolbar and status bar",
"type" : "verticalLayout",
"padding" : 0,
"widgetHeight" : 24,
"textEditorHeight" : 16,
"showLayout" : false,
"rowScaling" : [ 0, 1, 0 ],
"childHorizontal" : "fill",
"childVertical" : "fill",

"children" : [
	{ "#": "------ Toolbar ------", "type" : "horizontalLayout", "colScaling" : [ 0, 0, 0, 1, 0, 0 ] },
	{
		"type" : "dummyTool",
		"text" : "Tool 1",
		"width" : 60,
		"height" : 60
	},
	{
		"type" : "dummyTool",
		"text" : "Tool 2",
		"width" : 60,
		"height" : 60
	},
	{
		"type" : "dummyTool",
		"text" : "Tool 3",
		"width" : 60,
		"height" : 60
	},
	{
		"width" : 120,
		"height" : 60
	},
	{
		"type" : "dummyTool",
		"text" : "Tool 4",
		"width" : 60,
		"height" : 60
	},
	{
		"type" : "dummyTool",
		"text" : "Tool 5",
		"width" : 60,
		"height" : 60
	},
	{ "#": "------ End Toolbar ------", "type" : "endLayout" },



	{ "#": "------ Canvas ------", "type" : "horizontalLayout", "padding" : 0, "colScaling" : [ 0, 1, 0 ], "childHorizontal" : "fill", "childVertical" : "fill" },
	{
		"type" : "dummyToolPalette",
		"text" : "Tools",
		"width" : 60,
		"height" : 60,
	},
	{
		"type" : "dummyCanvas",
		"text" : "Canvas",
		"width" : 600,
		"height" : 400,
	},
	{
		"type" : "dummyLayers",
		"text" : "Tree View",
		"width" : 200,
		"height" : 60,
	},
	{ "#": "------ End Canvas ------", "type" : "endLayout" },



	{
		"type" : "dummyStatusBar",
		"id" : "stats",
		"text" : "Status Bar",
		"width" : 100,
		"height" : 20,
	}
]

}

Both of these are valid, and both produce the same result:

-Mike


#23

I don’t know what editor you were using to edit your JSON, but the statement that keeping track of matching braces is difficult is kind of moot if you use any modern text editor. Whatever you were using in the video wasn’t automatically creating matching braces, which is dumb. You should use a better text editor, like VSCode that automatically matches braces/brackets/parentheses and see if you still feel the same way.


#24

The editor in the video is not my normal editor, but I thought it might display clearer for a video recording. But thanks anyway for calling me dumb.

It’s not that brackets are inherently hard to deal with, but in my experience it’s useful to reduce punctuation, nesting, and excessive structure if it’s not actually buying you anything.


#25

I didn’t call you dumb. I called the editor you used in the video dumb.


#26

Hey nice work! I’ve been down this road myself (fairly similar system: xml layout, component-factory, css-like properties, data model mappings) but ran into some hurdles. Mainly to do with supporting complex behaviours which meant bringing logic into the game. I first tried lua, which was ok, but the more I used it the more I needed to add support for stuff and was just too much work in the end. I then went for the happy medium and moved my xml layout into a C++ hierarchical syntax using lambdas, something like so:

//button setup... or nested containers, etc..
}); ```

I generally liked it for awhile but coming back to the code after some time always felt hard to parse, and whenever a component needed some outside knowledge I'd then be passing all these vars through the captures and at a few nested levels it got pretty unruly.  Also, using lots of lambdas seems to add lots of build time overhead.  Anyway, at this point I've still got the css properties but I'm otherwise mostly back at square one.

My general thoughts going forward are that a dedicated prototyping tool that generates the desired "regular" UI C++ code might work best.  I guess something like what the Projucer (Or old Introjucer, not too familiar with it these days) does, but just taken a number of steps further.  Maybe it already does a lot, but my point is that it seems some of these dynamic aspects perhaps are suited better for a design phase which is then compiled into a lean and mean version for running your released application.

Anyway, my 2 cents fwiw, thanks for sharing and good luck with your efforts going forward!

#27

I would also prefer nested json objects/arrays for children instead of the flatten hierarchy, so I’m glad that both are supported and create the same output.

I also agree with json vs. xml that XML is too verbose. For Nexus I used XML (only to create groups and re-use them for tabs etc.) but it was still with absolute coordinates and no automatic lay-outing to speak of.

I have a few ideas for your layout engine. e.g. an expression evaluator, so certain properties could be expressed mathematically.

Your system would save me a lot of trouble designing the next GUI.

So what are your current plans for it? Maybe share it via GitHub, so you can get bug fixes, new features etc. via pull requests?


#28

Hey Mike. Would you be willing to share this system?

  • E

#29

I don’t think @Lost_Marble got any notifications or anything. He also didn’t reply to a PM I’ve send him a few days ago. :sob:


#30

Yeah I also DM:ed him a few days ago.


#31

@jules Please do continue exploring this :wink: I’ve worked with the Intro/Projucer’s editor a lot over the past years, and I’m glad to hear you’re considering building a dynamic layout system document format that can also be manipulated via code.

I’ve worked with XAML years ago (when WPF was still slow as hell) and have recently been working with React (with TypeScript), and I remember when I first came to Juce that this was one of things I would have loved to use instead of having to write everything in code.

The difficult part IMHO with these kinds of layout systems, is getting the model bindings right. XAML/WPF bindings can be very powerful, but in my opinion, it also made things rather complex for non-trivial GUIs. I guess that’s also why you’re looking at React with the single state model.

By the way: I fully agree on using flexbox where possible and XML as the better format for this kind of thing.

So: yes! Please do continue exploring this path! It would be great!


#32

Yep, it’s one of the more exciting things I want to explore this year!


#33

If you want any help, coding, testing, whatever, please let me know !!

I love this concept !

I’m currently working on an open source Reaper control surface integrator project, and the UI is going to be very challenging, this wold help a lot !


#34

I have to pipe in at this time - that since moving over entirely to C++ and JUCE I’ve been sorely missing my old prototyping and fast RAD cross-platform coding environment and framework - XOJO.

There are so many ways that IDE made development productive, far less frustrating - and also - fun - yet was in no way a “toy”.

I loved the XOJO GUI editor, and the way that one dealt with classes, methods etc - as objects in themselves - rather than this constant harking back to text-file approaches - which in the case of C++ with its necessary but tiresome need to create method declarations in TWO places - two files often - is just a pain.

Of course tools out there like CLion and VS can make this easier. Its a real shame that Xcode clearly has an anti C++. bias and provides nothing for us to ease the drudgery of coding on C++. All of Xcode’s refactoring tools as we know - get reserved for Swift and Objective C. :frowning:

Which reminds me to again - prompt the JUCE team to consider adding some sort of CMake creation feature into PROJUCER - so we can use CLion effortlessly.

Better CLion and JUCE integration and interoperation would be uber-cool !

Until…maybe - ROLI consider just making PROJUCER/JUCE into something more complete like the QT IDE ?
just my tuppeneth… probably all been said before :slight_smile:


#35

Just to clarify - I’m in no way paid by or connected to XOJO - but its IDE is available entirely free - to play around with - providing one only launches and plays around with the app being worked on in its debugger.

Here’s a link where you can download it. I provide this only as something - food for thought - for ROLI to consider when thinking of ideas for making the whole JUCE toolset more productive in the future…

For the time being and most likely the future - ( since XOJO can’t build dynamic libraries and doesnt have a pre-emptive-thread-safe framework ) I’m sticking with C++ and JUCE…


#36

to download and try out the IDE …

http://www.xojo.com/download/


#37

That’s very cool. I may adopt some of your ideas to expand on what we do in 8th. 8th has used a JSON GUI-description forever; but we rely heavily on “bounds”, which can be difficult to get right.