It seems like you're trying to invent a GUI-widgets API at the same time as a language feature. I recommend sticking to just the API or just the language feature. Design the API the way you want it, and then see if any new language feature could really improve it or not.
For example, take
and write that API using standard C++; you'd get something like this:
struct Window : std::enable_shared_from_this<Window> {
template<class F> requires std::invocable<F, Window&>
explicit Window(const F& setup) {
setup(*this);
}
};
auto window = std::make_shared<Window>([](auto& w) {
w.width = 100;
w.height = 10;
w.children = {
std::make_shared<Label>([](auto& label) {
label.y = 10;
label.text = "Some label";
},
std::make_shared<Button>([&](auto& label) {
label.text = "Quit";
label.clicked = [parent = w.shared_from_this()]{ parent->close(); };
},
};
};
Is that where we're starting from? Personally,
- I don't do much GUI-widgets stuff,
- I think that's a "clever" API,
- but I'm not sure I'd want to use it.
It seems to make too much stuff public, and permit too many things to go uninitialized — see how the first child sets its `y` coordinate but fails to set its `x` coordinate. We can kinda deal with this via the Builder pattern — we say that the `const F& setup` lambda actually receives a WindowBuilder object, not a Window directly, and then the Window itself is able to sanity-check the (all-public) state of the WindowBuilder before actually "committing" its changes (into private members of Window). But still, this is very runtime-oriented — very far from what I personally mean when I say "declarative." To me, "declarative" means that there's no runtime code — it's just a bunch of declarations, type-checked at compile time, and then if it compiles it's guaranteed to work correctly. Any solution that lets you set `label.y` without also setting `label.x` is the opposite of what I'd call "declarative."
So, I think you (having seen how one'd write that API in standard C++) should identify some concrete problems with the standard C++ solution, and then look for solutions to those problems. For example, it sounds like "too much stuff being public" is not one of the problems you're concerned with.
The ideal outcome is that you say "hmm, actually, I don't see any problems with the present-day-C++ API," and then we're done here. ;)
–Arthur