FLUID: Add support for lambda callbacks.

Starting the callback text with a '[' assumes that
the rest of the callback is a lambda and generates
inlined code for it.
This commit is contained in:
Matthias Melcher 2025-12-22 23:12:25 +01:00
parent d0d2e104e9
commit 33199dab78
5 changed files with 55 additions and 19 deletions

View File

@ -552,11 +552,10 @@ int xyz_data;
button->callback(xyz_callback, &xyz_data);
\endcode
\note You cannot delete a widget inside a callback, as the
widget may still be accessed by FLTK after your callback
is completed. Instead, use the Fl::delete_widget()
method to mark your widget for deletion when it is safe
to do so.
It's also possible to use a non-capturing lambda function directly, for example:
```
button->callback( [](Fl_Widget*, void*) { puts("Hello"); } , nullptr );
```
Many programmers new to FLTK or C++ try to use a
non-static class method instead of a static class method

View File

@ -369,13 +369,18 @@
\image html wp_cpp_callback.png
\image latex wp_cpp_callback.png "" width=7cm
The callback field can be interpreted in two ways. If the callback text is only
a single word, FLUID assumes that this is the name of an external callback
function and declares it in the header as
The callback field can be interpreted in three ways. If the callback text is
only a single word, FLUID assumes that this is the name of an external
callback function and declares it in the header as
`extern void my_button_action(Fl_Button*, void*);`.
Otherwise, FLUID assumes that the text is the body of a C++ callback function
and instead creates a local static callback function. The name of the callback
If the first letter of the callback text is a '[', FLUID expects a lambda
function which will be inlined into the widget creation code. The lambda
signature must be `[](Fl_Widget*, void*)->void { ... }`. The widget pointer
can be casted to another type inside the lambda.
Otherwise, FLUID assumes that the text is the body of a C++ function,
and a local static callback function is created. The name of the callback
function is generated by FLUID and guaranteed to be unique within the file.
```
static void cb_input(Fl_Input *o, void *v) {

View File

@ -329,7 +329,7 @@ void Menu_Item_Node::write_static(fld::io::Code_Writer& f) {
if (extra_code(n) && isdeclare(extra_code(n)))
f.write_h_once("%s", extra_code(n));
}
if (callback() && !is_name(callback())) {
if (callback() && !is_name(callback()) && (callback()[0] != '[')) {
// see if 'o' or 'v' used, to prevent unused argument warnings:
int use_o = 0;
int use_v = 0;
@ -520,12 +520,21 @@ void Menu_Item_Node::write_item(fld::io::Code_Writer& f) {
f.write_c(", 0, ");
}
if (callback()) {
if (callback()[0] == '[') {
f.write_c("\n");
f.tag(Mergeback::Tag::GENERIC, Mergeback::Tag::WIDGET_CALLBACK, 0);
f.write_c_indented(callback(), 1, 0);
f.write_c("\n");
f.tag(Mergeback::Tag::WIDGET_CALLBACK, Mergeback::Tag::GENERIC, get_uid());
f.write_c("%s, ", f.indent_plus(1));
} else {
const char* k = is_name(callback()) ? nullptr : class_name(1);
if (k) {
f.write_c(" (Fl_Callback*)%s::%s,", k, callback_name(f));
} else {
f.write_c(" (Fl_Callback*)%s,", callback_name(f));
}
}
} else
f.write_c(" 0,");
if (user_data())
@ -571,7 +580,7 @@ void Menu_Item_Node::write_code1(fld::io::Code_Writer& f) {
}
if (callback()) {
if (!is_name(callback()) && class_name(1)) {
if (!is_name(callback()) && (callback()[0] != '[') && class_name(1)) {
const char* cn = callback_name(f);
const char* ut = user_data_type() ? user_data_type() : "void*";
f.write_public(0);

View File

@ -1499,7 +1499,7 @@ void Widget_Node::write_static(fld::io::Code_Writer& f) {
if (strchr(c, '[') == nullptr) f.write_c("%s *%s=(%s *)0;\n", t, c, t);
else f.write_c("%s *%s={(%s *)0};\n", t, c, t);
}
if (callback() && !is_name(callback())) {
if (callback() && !is_name(callback()) && (callback()[0] != '[')) {
// see if 'o' or 'v' used, to prevent unused argument warnings:
int use_o = 0;
int use_v = 0;
@ -1883,7 +1883,16 @@ void Widget_Node::write_widget_code(fld::io::Code_Writer& f) {
const char* ud = user_data();
if (class_name(1) && !parent->is_widget()) ud = "this";
if (callback()) {
if (callback()[0] == '[') {
f.write_c("%s%s->callback(\n", f.indent(), var);
f.tag(Mergeback::Tag::GENERIC, Mergeback::Tag::WIDGET_CALLBACK, 0);
f.write_c_indented(callback(), 1, 0);
f.write_c("\n");
f.tag(Mergeback::Tag::WIDGET_CALLBACK, Mergeback::Tag::GENERIC, get_uid());
f.write_c("%s", f.indent_plus(1));
} else {
f.write_c("%s%s->callback((Fl_Callback*)%s", f.indent(), var, callback_name(f));
}
if (ud)
f.write_c(", (void*)(%s));\n", ud);
else

View File

@ -7,6 +7,7 @@ i18n_gnu_function gettext
i18n_gnu_static_function gettext_noop
header_name {.h}
code_name {.cxx}
include_guard {}
comment {About test/preferences:
The preferences app shows two features of FLTK and FLUID.
@ -184,6 +185,10 @@ Fl_Input::paste_menu_text = gettext("Paste");} {}
}
MenuItem {} {
label sandals
callback {[](Fl_Widget*, void*)
{
puts("The shoe is the sign!");
}} selected
xywh {0 0 100 20}
}
MenuItem {} {
@ -203,7 +208,7 @@ Fl_Input::paste_menu_text = gettext("Paste");} {}
xywh {35 112 100 24} type Radio down_box ROUND_DOWN_BOX
}
Fl_Round_Button wRight {
label {right side} selected
label {right side}
xywh {35 136 100 24} type Radio down_box ROUND_DOWN_BOX
}
Fl_Box {} {
@ -217,6 +222,15 @@ Fl_Input::paste_menu_text = gettext("Paste");} {}
}
Fl_Check_Button wShave {
label shave
callback {[](Fl_Widget* w, void*)->void {
auto* btn = static_cast<Fl_Check_Button*>(w);
if (btn->value()) {
puts("Shave.");
} else {
puts("Don't shave.");
}
}}
comment {// Testing lambdas for callbacks}
xywh {25 212 105 24} down_box DOWN_BOX
}
Fl_Check_Button wBrush {