xparq / sfw

"Super frugal widgets" - experimental simple, self-containing SFML-based GUI lib with no other external deps. [WIP!]
MIT License
2 stars 2 forks source link

Fluent widget tree setup API #307

Open xparq opened 1 year ago

xparq commented 1 year ago

From the experiences gathered so far (below): might be worth introducing, as a "STRICTLY EXPERIMENTAL" (and very dangerous etc.) feature (because it's so satisfying, when works), but it's way too far from replacing the old style, or even from being reasonably complete!...

And definitely REQUIRES strong setup-time diagnostics, too: it'd be tempting to reorder widgets and set them up first offline, just to be able to nicely "stream" them all into the tree afterwards, in a clean & tidy structure -- but even simple things like "named add()" won't work with free-standing containers... (It's an existing problem (#109) for the old style too, but probably less frequent there.)


TODO:


Examples:

This is fine (works already):

demo << sfw::Button("Quit", [&] { demo.close(); });

This is fine, too, but doesn't do what you might think... (indentation doesn't override op precedence! ;) ):

*right_bar // a legacy WidgetContainer ptr
    << sfw::HBox()
        << sfw::Label("Clear background")
        << sfw::CheckBox([&](auto* w) { Theme::clearBackground = w->checked(); }, true)
;

This would do what you wanted -- but crashes (see next issue comment!):

*right_bar
    << ( sfw::HBox()
        << sfw::Label("Clear background")
        << sfw::CheckBox([&](auto* w) { Theme::clearBackground = w->checked(); }, true)
    )
;

This works fine -- but mixing pointers and refs is sick:

    << ( new sfw::HBox
        << sfw::Label("Clear background")
        << sfw::CheckBox([&](auto* w) { Theme::clearBackground = w->checked(); }, true)
    )

Maybe committing to all pointers (like in the "classic" version anyway) and using just global PTR << ... overloads could work? Unfortunately, this is outright rejected by the compiler for bogus arg. types, apparently even before the templates would be considered (at least no mention of those in the -- unusually terse -- error, in either compilers):

    << ( new sfw::HBox
        << new sfw::Label("Clear background")
        << new sfw::CheckBox([&](auto* w) { Theme::clearBackground = w->checked(); }, true)
    );

(NOTE: OK, so this is probably a case of "template shadowing", where some existing << ops (in std::?) take precedence over my templated version. I also suspected it to be an obscure case of namespace resolution with ADL, but using namespace sfw; didn't make a difference, so... Seems to be a lost case. :-/ )

FYI, this (even more mixed-up version) also works:

right_bar->add( new sfw::HBox
        << sfw::Label("Clear background")
        << sfw::CheckBox([&](auto* w) { Theme::clearBackground = w->checked(); }, true)
);
xparq commented 1 year ago

Regarding the crash:

Both GCC and MSVC... Maybe the same double-delete bug I found in the fluent POC: the compiler generates a default copy ctor and selects that instead of the move ctor... But, unfortunately, the fix that worked there, does nothing here:

WidgetContainer(const WidgetContainer&) = delete;
WidgetContainer(WidgetContainer&&) = default;

Sigh. So... Is it the early destruction of the HBox temporary? Or related to the callback copy?

In a similar, but other, crash (due to another migration-related change in the code), this is what GDB saw:

Thread 1 received signal SIGSEGV, Segmentation fault.
0x00007ff641816336 in std::_Function_handler<void (sfw::Widget*), sfw::GUI::themeChanged()::{lambda(sfw::Widget*)#1}>::_M_invoke(std::_Any_data const&, sfw::Widget*&&) ()
(gdb)