Which architecture serves as a model for Juce GUI code?

gui
#1

For a general question: Which known UI framework was Juce GUI code originally inspired by? I wonder whether there’s a mature architecture/model serving as a guide, or if it grows organically on its own device.

If there’s a proven architecture behind it, that would greatly encourage me to invest the time and wrap my head around it. Otherwise, I might need to write a wrapper around Juce, in order to not get trapped in an exotic code base.

#2

It was mainly based on the Java model… That’s much hated now, but was cool back in 1999 when I started :slight_smile:

Modern declarative approaches like react are really interesting, but essentially most actual OSes are all based on the same kind of architecture as JUCE uses - windows, messages, invalidate/repaint events, mouse-events etc. The fancy modern stuff is just layers on top of this.

2 Likes
#3

That’s not a bad foundation to build on.

I think of doing a Component/Layout editor that saves to C++ source as literal constants to a file *.specs.cpp alongside the component source.

A custom component then inherits from RuntimeComponent, which reads those constants to build and manage the component at runtime. All communication with child components is handled by RuntimeComponent, which dispatches their callbacks to the custom component using a unified method signature (basically merges all ::Listener protocols into one).

Why? Because it

  • Spares us the effort of adding individual widgets to the component source at different places manually.
  • Separates the construction, ownership, layout and destruction of child components from the “business” code by making it opaque
  • Allows for callbacks on a per-widget basis, e.g. okButtonClicked(), cancelButtonClicked(), etc. enabling derived classes to selectively override these.
  • Allows for cool UI tools based on *.specs.cpp code

The heavy part is how to link child component callbacks to methods in the custom component. There is no reflection in C++ that would support calling members by name, so RuntimeComponent needs to register these somehow, preferably at compile time.

Do you think such a concept makes sense?

#4

Yeah, I think that breaking up the implementation of a GUI into different aspects is the way to go, but not sure about the particular design you’re describing there.

We’ve brainstormed future GUI directions here many times and never really came to a good conclusion about what a perfect design would look like, however I’d probably recommend something more declarative/functional like the React paradigm. It’s tricky in C++ though, without reflection.

#5

Do we really need reflection at runtime? The links between names (component identifiers) and member functions are known at compile time. I think it boils down to a few points:

  1. The representation of a layout needs to be read/written by the editor and read & interpreted at runtime. Literal constants of modern C++ should do the job. JSON would also do, but compiling JSON as string literals into a binary seems odd to me.

  2. The names in those “specs” need to be linked to member functions in user code. Since the link is known at compile time (entered by the user into the editor), the editor can generate code that accomplishes this (e.g. code that populates a HashMap with function pointers).

  3. Some utility class implements all those *::Listener callbacks of the various components, and merges them into a unified method signature, which user code can respond to. (the unified signature also simplifies the mapping by name).

All generated code resides in *.specs.cpp, nicely separated from user code. So a component implementation would consist of the files, for example:

MegaComponent.h
MegaComponent.cpp
MegaComponent.specs.cpp

Coming up with an editor that scans for these files and fires up a selection menu should be trivial.

It is important to note that all this is not about the GUI to become a totally scripted and dynamic thing. It is about easing the development and maintenance workflow.

#6

I have something evolving already, but got stuck with how to store function pointers to virtual methods (independent of the physically implementing class, that is). Indeed tricky. If I get it working, I’ll share it.

1 Like
#7

Thanks to some template magic, I managed to link components to member functions dynamically. Amazing stuff is possible with modern C++ (looking forward to C++20 with some reflection finally coming)

Whatever, this opens up a great perspective on declarative UI architectures. I have a rudimentary prototype running already, but it will take me some time to come up with a comprehensive example. I’ll be happy to share it then.

Reading some discussions on declarative UI frameworks on this forum, I want to point out that this is way more than just about building a component hierarchy and handling design and layout. I’d say that’s just 50%

The other half is effective communication between user code and UI, which also benefits a lot from being as declarative and lean (non-verbose) as possible. I believe I’ve got something evolving here.

During the past 12 years I have worked with (and extended) UI frameworks for very large applications in other languages and am in the process of porting some of the most successful concepts to C++ and Juce. It won’t be easy, but I’m quite positive. A framework that helped me code and maintain several thousand classes in only a few years can’t be too bad after all.

Will be back with some examples, hopefully soon.

3 Likes
#8

Here’s a follow up on this: