Declarative UI Framework Proposal

In need of a declarative UI framework with data binding, I did a proof-of-concept project that I want to share with the Juce community. It makes use of concepts I have been working with for decades. Some of the concepts originate from Smalltalk, although the ported code is clean Juce-style C++.

To keep this introduction short, I’ll point you directly to a very brief whitepaper (3 pages) and the GitHub project where you can play around with the framework yourself. Be warned though, this is still rough work in progess, with objects leaking on shutdown, etc.

There are a few code snippets in the PDF, although the examples in the project pretty much speak for themselves. Here’s a few highlights as a teaser:

  • Specs are XML plus auto-generated C++ for the data binding
  • Layout is declarative and responsive and supports FlexBox and LayoutFrame (FlexBox not yet fully integrated, but everything is already there to add it in).
  • It is possible (in theory) to include a UI Editor with your development builds that has access to all your UI’s at runtime(!)
  • Cool goodies inside like LayoutFrame, LayoutCursor and Metaclasses for basic reflection.

One night it struck me as fantastic, this possibility to include a UI Editor with development builds! Just imagine a small “Edit” icon on top of your windows. You could edit the UI live, save it and continue :wink:

The framework definitely supports that.

To be clear, I want this to be picked up, used, extended and enjoyed. I will definitely use it for my projects, so if some of it makes it into the Juce Universe, all the more better.

8 Likes

This looks very promising indeed. Just had a quick play, and it certainly works!

One initial comment which is more based on the layout XML style I’m used to (e.g. JSX) is to have all the XML element names in TitleCase rather than lowercase.

Great work, you’ve obviously put a lot of thought into this!

Expect some PR’s and issues on GitHub coming your way soon…

I would like to ask if, instead of XML, you could support JSON? It’s shorter, easier to read and a lot easier to parse (code-wise). It would make maintenance of the specs much easier (IMO).

4 Likes

I don’t agree; I think for declarative markup for UI, XML is more readable than JSON, especially with complex structures.

3 Likes

XML was actually the result of using ValueTree as an intermediate format, purely for convenience. So if someone came up with a ValueTree <-> JSON bridge, you could as well use JSON here.

The only complaint I have is Xcode not getting the “R” syntax (“here docs”) and messing up the coloring. If XML was properly colored, it would be much easier to read and write.

The final goal however is to never hand-code the XML, but to have a UIEditor included with development builds that generates all you need. Except for the occasional hack, and with an interface for custom-built components already included with the framework, there is no need to manually code XML.

Any volunteers for porting the Projucer component editor? :wink:

1 Like

This reminds me of the Adobe FLEX SDK (RIP), where you had some declarative MXML for layouts and could combine it with inline code. It was very dynamic but a nightmare for getting even the simplest things done. Tons of boilerplate for simple button states. Rebuilding tree states for dynamic controls.

IMO declarative layout engines are nice for all kind of editor forms and big applications with many generic modules and controls. But it’s totally over the top for small applications. A bit over engineered at this state, if you ask me. Too abstract for what it does, and it doesn’t even handle every possible case yet and is also hard to debug!

We get it, everyone tries to MVC things. Hard codeds things will get to you someday. It’s nice for later changes and RAD. But please make it easy and minimal if you don’t want to end with an XML junk volcano.

From my experience, building such system bottom-up for every kind of scenario fails every time. Better take a look at the latest JUCE poll results. Most people use it for audio plugins. You may want to build an audio application use-case first. A model that does the tiresome things in an awesome, visually pleasing and easy way. Before coming up with a random model that reads like an example out of a software patterns computer engineering text book.

Sorry for the hard words. Appreciate your effort anyway. Can only get better : )

I agree this may be oversized for many audio plugins. But that’s not what it is intended for.

DAW-like desktop applications don’t count as “small”, I would think. These have to deal with an immense number of application parts and associated UI’s (inspectors, lists, preferences, settings, menu bars, tool bars, you name it), it’s a potential development and maintenance nightmare. Let alone keeping a consistent look, feel and application state, or improving the UI between major releases.

Hard-coded means exactly that: Once it’s done, it’s hard and extremely costly to keep up with the evolution of a product.

As great as Juce is, making desktop apps like that based on Juce in its current state is not something I would recommend anyone. Therefore, “It’s nice for later changes and RAD” exactly hits the nail for this use case.

Instead of having a hard-coded mess, all UI’s are organized formally the same, with issues easy to spot and fix (and edit live). But most importantly, the application logic sits right in front of you, everything in one place, not obscured by twisted UI code.

The boilerplate, which is 100% generated automatically anyway, merely is a consequence of C++ not supporting reflection. It could probably be further reduced. Given the advantages, it should not distract too much.

The idea behind a framework like this is also not to take away or replace anything, e.g. the ability to hard-code stuff where it’s desirable. The motivation is to make Juce more viable for large desktop apps and open it to a wider audience of developers.

3 Likes

I should add that, since UISpecs are embedded with source code, XML, JSON or similar formats (which are primarily used with external files) are not needed at all. Any native format that lends itself to being automatically generated would do as well. Even chained C++ statements.

Key is to have a UISpec isolated from you application behaviour. Everything else is open to whatever works best.

A UIEditor included with development builds takes UISpecs for input and generates source code as output, so it doesn’t require an external file format to operate.

I’ve tried C++20 designated aggregate initializers with some success, but got stuck with expressions that didn’t compile (bit-wise OR for combining flags). These are certainly looking most sexy and easy to maintain manually, if needed.

Couldn’t resist and finally dropped XML. UISpec is now entirely native C++ generated automatically by the very UISpec itself. Works like a charm.

If you have a look at the resulting code, it doesn’t look much more complex than XML. I’d say it’s even easier to grasp and maintain until there’s eventually a graphical UIEditor. Most importantly, the tedious parsing and conversions are gone and you get nice compiler help and warnings, too.

I’m looking forward to further working on this, although I’ll be busy with other things soon. So if you have any suggestions, let me know.

Looks nice, but for me, the attraction is in being able to live edit an XML file and adjust styles without recompiling. I’m personally not that interested in a graphical UI editor.

2 Likes

Live editing works with any representation. You can still do that. The UISpec, once built, is native global data. Any tool included with a development build can edit it live. Only if you save changes it will write new source code that is compiled on the next run.

The editor need not be much ‘graphical’ either. Something like the Juce live editing demo comes to mind. At least for adjusting optics and layout, that’s pretty sufficient.

But the true advantange of this approach is you can create new UIs from scratch, edit and save them as native code and still have them separated from your application logic.

XML files (or JSON) only work in a development environment. In order to establish data bindings, you still need native code, so there’s not much won for live coding, except tweaking the optics. The embedded XML struck me as merely an illusion of dynamics.

Unless C++ introduces real reflection some day (making classes objects), compiling code is essentailly a one-way ticket. This crass limitation held me back from using it in major projects yet.

The advantage I see in using XML, is a layout engine which watches for changes in XML files in development mode, and prior to a release build saved as binary data with ValueTree::writeToStream, added to the project, and then loaded with ValueTree::readFromStream. So there would be parsing required in development mode but not production mode.

I see, but there’s not much that could be done with XML ‘live’, except adding dummy components (not bound to your model) and changing colors and positioning (a style sheet actually). For everything else you’d have to write code.

For any UI with more than a very few components, editing XML is a chore. I’d rather prefer an editor where I can arrange things visually and simply click a button to generate code that includes the bindings and all.

Not quite… there is the chance of calling functions by string from your frontend, similar to DynamicObject::invokeMethod().

Or a more elaborate system could be invented similar to QT objects, that expose native functions and properties through their meta-object compiler (back in QT 4, don’t know about today)

Just as a side note, I am not sure, if that’s the way I wanted to design my projects.

I can’t really see how a visual arrange editor is useful in anything other than a relatively simple app. For anything more complex than a basic plugin or tool, your UI is probably going to be adaptive in some way. This means creating your UI based on some kind of rules, be it proportionally, by trimming rectangles, juce::Grid, juce::FlexBox or something similar.

For simple UIs, placing some components on the screen isn’t a difficult or time consuming challenge.

That’s my experience anyway.

2 Likes

You wouldn’t edit the entire UI as a whole, of course, but its many parts individually. And yes, responsive layout rules are declarative and handled by the framework. So for example, if a sidebar gets visible, other parts make room for it.

Had a look an Qt qml. It looks slick and everything, but what an overkill. Imagine this boilerplate with a DAW-like app with 100+ inspectors, panels, views, settings, dialogs, tabs …

Any form of ‘scripting’ amounts to embedding a language implementation with your app, plus the performance hit that comes with ‘soft’ bindings of this kind. What’s more, these ‘soft’ bindings (aka invocations) need to be hard-coded somewhere anyway.

No matter how many layers you add, you can’t hide the elephant in the room: C++ can’t call methods by name, unless you hard-code that link somewhere. So why not do just that right there in a native spec?

I am not against your approach at all, nice work.

For me, I have two scenarios:

  1. I am working on my own, then I don’t mind coding C++. The classes available do what I need, and I have all freedom, how close coupled I want the code.

  2. I have a UI/UX person at my disposal, then I want that person being able to finish the job the whole 9 yards. That means, it must be possible to change all Layout, colours and bindings with something, the UI/UX person can edit, without knowing C++. It is fine to have a last step, where these edits are automatically baked in, e.g. using the BinaryBuilder.

But everybody has a different reality to face and different use cases…

1 Like

@adamski, if you install any of the newer Arturia plug-ins, there is a folder in the install product directory “/Library/Arturia/ProductName/resources/gui_xml” that holds xml files that describe each panel of the plug-ins interface. Worth having a look…

2 Likes

Doing simple C++ auto generated interfaces here. Thought I’d share.

On the DSP side everything is based around a module in the signal chain. Modules publish their parameters which are registered with the parameter manager. When the UI is opened the Editor iterates over each module and builds a simple interface for the module based on the spec for each parameter. Great for quick prototyping.

When it comes to the final layout design there’s a mechanism to request the interface component associated with a given parameter so you can move it and style it however you want. That’s the tedious C++ part you are probably trying to avoid but it works for the products it’s designed for - medium sized plugins/synths.

An interface editor could be an option later if needed but might be more effort than it’s worth.

1 Like