summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Melcher <github@matthiasm.com>2025-12-22 23:12:25 +0100
committerMatthias Melcher <github@matthiasm.com>2025-12-22 23:12:42 +0100
commit33199dab785daaa7c6c5733021ad0cab48e0e1dd (patch)
tree7430470aace60b830659ca4d229727aa1f6f6112
parentd0d2e104e9d5303ac897f5d1faca582e619180b2 (diff)
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.
-rw-r--r--documentation/src/common.dox9
-rw-r--r--fluid/documentation/src/page_widget_panel.dox15
-rw-r--r--fluid/nodes/Menu_Node.cxx21
-rw-r--r--fluid/nodes/Widget_Node.cxx13
-rw-r--r--test/preferences.fl16
5 files changed, 55 insertions, 19 deletions
diff --git a/documentation/src/common.dox b/documentation/src/common.dox
index 5677bb5e5..ba7c0e583 100644
--- a/documentation/src/common.dox
+++ b/documentation/src/common.dox
@@ -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
diff --git a/fluid/documentation/src/page_widget_panel.dox b/fluid/documentation/src/page_widget_panel.dox
index 7a1261d62..d897037e4 100644
--- a/fluid/documentation/src/page_widget_panel.dox
+++ b/fluid/documentation/src/page_widget_panel.dox
@@ -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) {
diff --git a/fluid/nodes/Menu_Node.cxx b/fluid/nodes/Menu_Node.cxx
index 342fcf014..dcaa2fc55 100644
--- a/fluid/nodes/Menu_Node.cxx
+++ b/fluid/nodes/Menu_Node.cxx
@@ -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,11 +520,20 @@ void Menu_Item_Node::write_item(fld::io::Code_Writer& f) {
f.write_c(", 0, ");
}
if (callback()) {
- const char* k = is_name(callback()) ? nullptr : class_name(1);
- if (k) {
- f.write_c(" (Fl_Callback*)%s::%s,", k, callback_name(f));
+ 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 {
- f.write_c(" (Fl_Callback*)%s,", callback_name(f));
+ 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,");
@@ -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);
diff --git a/fluid/nodes/Widget_Node.cxx b/fluid/nodes/Widget_Node.cxx
index e711c03f0..b74560f05 100644
--- a/fluid/nodes/Widget_Node.cxx
+++ b/fluid/nodes/Widget_Node.cxx
@@ -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()) {
- f.write_c("%s%s->callback((Fl_Callback*)%s", f.indent(), var, callback_name(f));
+ 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
diff --git a/test/preferences.fl b/test/preferences.fl
index a46c7faeb..dec03d2b3 100644
--- a/test/preferences.fl
+++ b/test/preferences.fl
@@ -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 {