summaryrefslogtreecommitdiff
path: root/FL
diff options
context:
space:
mode:
authorMatthias Melcher <github@matthiasm.com>2023-08-15 11:36:58 +0200
committerGitHub <noreply@github.com>2023-08-15 11:36:58 +0200
commit10d9010ed9a7624bebebcb0f86fc86d362672027 (patch)
tree4303c6f36947046d2baab01f02defd0d4bdb9ab2 /FL
parente6440ca0a89874e2593b2dc9cf5a3b0e87df94a2 (diff)
Improved, yet compatible, widget callback system using macros (#729)
* adds FL/fl_callback.macros.H * adds FL_FUNCTION_CALLBACK_n(widget, function, [type, data]) * adds FL_METHOD_CALLBACK_n(widget, class, instance, method, [type, data]) * adds FL_INLINE_CALLBACK_n(widget, [type, name, data], callback_body) * adds `examples/callback` * full documentation
Diffstat (limited to 'FL')
-rw-r--r--FL/Fl_Widget.H54
-rw-r--r--FL/fl_callback_macros.H607
2 files changed, 651 insertions, 10 deletions
diff --git a/FL/Fl_Widget.H b/FL/Fl_Widget.H
index cf3a16b4f..fc7091225 100644
--- a/FL/Fl_Widget.H
+++ b/FL/Fl_Widget.H
@@ -70,6 +70,24 @@ struct FL_EXPORT Fl_Label {
};
+/** A class prototype that allows for additional data in callbacks.
+
+ Users can extend this class and pass it to widget callbacks. Widgets can
+ take ownership of the callback data, deleting the data when the widget
+ itself is deleted.
+
+ The destructor of this class is virtual, allowing for additional code to
+ deallocate resources when the user data is deleted.
+
+ \see FL_FUNCTION_CALLBACK, FL_METHOD_CALLBACK, FL_INLINE_CALLBACK
+ \see Fl_Widget::callback(Fl_Callback*, Fl_Callback_User_Data*, bool)
+ \see Fl_Widget::user_data(Fl_Callback_User_Data*, bool)
+ */
+class Fl_Callback_User_Data {
+public:
+ virtual ~Fl_Callback_User_Data() { }
+};
+
/** Fl_Widget is the base class for all widgets in FLTK.
@@ -152,7 +170,7 @@ protected:
CLIP_CHILDREN = 1<<11, ///< all drawing within this widget will be clipped (Fl_Group)
MENU_WINDOW = 1<<12, ///< a temporary popup window, dismissed by clicking outside (Fl_Window)
TOOLTIP_WINDOW = 1<<13, ///< a temporary popup, transparent to events, and dismissed easily (Fl_Window)
- MODAL = 1<<14, ///< a window blocking input to all other winows (Fl_Window)
+ MODAL = 1<<14, ///< a window blocking input to all other windows (Fl_Window)
NO_OVERLAY = 1<<15, ///< window not using a hardware overlay plane (Fl_Menu_Window)
GROUP_RELATIVE = 1<<16, ///< Reserved, not implemented. DO NOT USE.
COPIED_TOOLTIP = 1<<17, ///< the widget tooltip is internally copied, its destruction is handled by the widget
@@ -161,6 +179,7 @@ protected:
NEEDS_KEYBOARD = 1<<20, ///< set on touch screen devices if a widget needs a keyboard when it gets the focus. Reserved, not yet used in 1.4.0. \see Fl_Widget::needs_keyboard()
IMAGE_BOUND = 1<<21, ///< binding the image to the widget will transfer ownership, so that the widget will delete the image when it is no longer needed
DEIMAGE_BOUND = 1<<22, ///< bind the inactive image to the widget, so the widget deletes the image when it is no longer needed
+ AUTO_DELETE_USER_DATA = 1<<23, ///< automatically call `delete` on the user_data pointer when destroying this widget; if set, user_data must point to a class derived from the class Fl_Callback_User_Data
// Note to devs: add new FLTK core flags above this line (up to 1<<28).
@@ -667,12 +686,27 @@ public:
*/
Fl_Callback_p callback() const {return callback_;}
- /** Sets the current callback function for the widget.
+ /** Sets the current callback function and data for the widget.
Each widget has a single callback.
\param[in] cb new callback
\param[in] p user data
*/
- void callback(Fl_Callback* cb, void* p) {callback_ = cb; user_data_ = p;}
+ void callback(Fl_Callback* cb, void* p) {
+ callback_ = cb;
+ user_data(p);
+ }
+
+ /** Sets the current callback function and managed user data for the widget.
+ Setting auto_free will transfer ownership of the callback user data to the
+ widget. Deleting the widget will then also delete the user data.
+ \param[in] cb new callback
+ \param[in] p user data
+ \param[in] auto_free if set, the widget will free user data when destroyed
+ */
+ void callback(Fl_Callback* cb, Fl_Callback_User_Data* p, bool auto_free) {
+ callback_ = cb;
+ user_data(p, auto_free);
+ }
/** Sets the current callback function for the widget.
Each widget has a single callback.
@@ -695,7 +729,7 @@ public:
*/
void callback(Fl_Callback1* cb, long p = 0) {
callback_ = (Fl_Callback*)(fl_intptr_t)(cb);
- user_data_ = (void*)(fl_intptr_t)p;
+ user_data((void*)(fl_intptr_t)p);
}
/** Gets the user data for this widget.
@@ -704,11 +738,11 @@ public:
*/
void* user_data() const {return user_data_;}
- /** Sets the user data for this widget.
- Sets the new user data (void *) argument that is passed to the callback function.
- \param[in] v new user data
- */
- void user_data(void* v) {user_data_ = v;}
+ /** \brief Sets the user data for this widget. */
+ void user_data(void* v);
+
+ /** \brief Sets the user data for this widget. */
+ void user_data(Fl_Callback_User_Data* v, bool auto_free);
/** Gets the current user data (long) argument that is passed to the callback function.
@@ -727,7 +761,7 @@ public:
\see argument()
*/
- void argument(long v) {user_data_ = (void*)(fl_intptr_t)v;}
+ void argument(long v) {user_data((void*)(fl_intptr_t)v);}
/** Returns the conditions under which the callback is called.
diff --git a/FL/fl_callback_macros.H b/FL/fl_callback_macros.H
new file mode 100644
index 000000000..589606377
--- /dev/null
+++ b/FL/fl_callback_macros.H
@@ -0,0 +1,607 @@
+/*
+ * Macros for easy callbacks for the Fast Light Tool Kit (FLTK).
+ *
+ * Copyright 2023 by Bill Spitzak and others.
+ *
+ * This library is free software. Distribution and use rights are outlined in
+ * the file "COPYING" which should have been included with this file. If this
+ * file is missing or damaged, see the license at:
+ *
+ * https://www.fltk.org/COPYING.php
+ *
+ * Please see the following page on how to report bugs and issues:
+ *
+ * https://www.fltk.org/bugs.php
+ */
+
+#ifndef _FL_FL_CALLBACK_MACROS_H_
+#define _FL_FL_CALLBACK_MACROS_H_
+
+#include <stdlib.h>
+
+/**
+ \file fl_callback_macros.H
+ This file provides macros for easy function and method callbacks
+ with multiple type safe arguments.
+*/
+
+#ifdef FL_DOXYGEN
+
+/**
+ \brief Declare a C function callback with custom parameters.
+
+ You can declare a plain C function callback or a static method callback with
+ custom parameters using this macro. It simplifies the process of calling
+ arbitrary functions with up to five custom parameters. The macro generates
+ code that ensures type safety and expands FLTK's standard callbacks, which
+ are limited to a single `void*` or `long` argument.
+
+ To use the macro, you provide the widget that will handle the callback as the
+ first argument. The second argument can be either a regular function or a
+ static method in any class.
+
+ Following these arguments, you can include up to five pairs, where each
+ pair consists of a type and a value. For example,
+ `int, 3` specifies an integer parameter with a value of 3. If you need to
+ pass two arguments, you can use two pairs, like this:
+ `int, 3, int, 4`. The last digit of the macro name must be the same as
+ the number of pairs (0..5)
+
+ Whenever the code generated by the macro is called, the custom parameters are
+ duplicated and marked for automatic deallocation using `delete` when the
+ callback widget is destroyed.
+
+ \code{.cpp}
+ #include <FL/fl_callback_macros.H>
+ ...
+ Fl_Button *btn1 = new Fl_Button(10, 10, 100, 20, "Beep");
+ FL_FUNCTION_CALLBACK(btn1, fl_beep);
+ ...
+ Fl_Button *btn2 = new Fl_Button(10, 40, 100, 20, "Hello");
+ FL_FUNCTION_CALLBACK(btn2,
+ fl_message,
+ const char *, text, "Hello\n%d %d %d %d",
+ int, a, 1, int, b, 2, int, c, 3, int, d, 4
+ );
+ \endcode
+
+ You can find a small demonstration program showcasing the usage of
+ `FL_*_CALLBACK_*` in the `examples/callbacks.cxx` file.
+
+ \param WIDGET the widget that will call the callback
+ \param FUNC a C/C++ function or a static class method
+ \param TYPE0 VALUE0 TYPE1 VALUE1 TYPE2 VALUE2 a list of zero to five type/value pairs, all separated by commas
+
+ \see FL_METHOD_CALLBACK_1, FL_INLINE_CALLBACK_2
+ */
+#define FL_FUNCTION_CALLBACK_3(WIDGET, FUNC, TYPE0, VALUE0, TYPE1, VALUE1, TYPE2, VALUE2)
+
+/**
+ \brief Declare a non-static class method callback with custom parameters.
+
+ You can declare a callback for a non-static class method with custom parameters
+ using this macro. It provides a convenient way to call arbitrary methods in
+ any class, overcoming FLTK's limitation of passing only a single `void*` or
+ `long` argument. Furthermore, it ensures type safety.
+
+ The first argument of the macro specifies the widget that will handle the
+ callback. The second argument indicates the class type to be called. The
+ third argument must be a pointer to an instance of that class. The
+ fourth argument is the name of the method within the class. That method must be
+ public and should not be static.
+
+ Following these arguments, you can include up to five pairs, where each
+ pair consists of a type and a value. For example,
+ `int, 3` specifies an integer parameter with a value of 3. If you need to
+ pass two arguments, you can use two pairs, like this:
+ `int, 3, int, 4`. The last digit of the macro name must be the same as
+ the number of pairs (0..5)
+
+ Whenever the code generated by the macro is called, the custom parameters are
+ duplicated and marked for automatic deallocation using `delete` when the
+ callback widget is destroyed.
+
+ \code{.cpp}
+ #include <FL/fl_callback_macros.H>
+ ...
+ Fl_Button *btn = new Fl_Button(10, 10, 100, 20, "Test");
+ FL_METHOD_CALLBACK_1(btn, Fl_Button, btn, color, Fl_Color, FL_GREEN);
+ \endcode
+
+ You can find a small demonstration program showcasing the usage of
+ `FL_*_CALLBACK_*` in the `examples/callbacks.cxx` file.
+
+ \param WIDGET the widget that will call the callback
+ \param CLASS the class type
+ \param SELF a pointer to an instance of the class
+ \param METH a C++ class method that must be public and not static
+ \param TYPE0 VALUE0 a list of zero to five type/value pairs, all separated by commas
+
+ \see FL_FUNCTION_CALLBACK_3, FL_INLINE_CALLBACK_2
+ */
+#define FL_METHOD_CALLBACK_1(WIDGET, CLASS, SELF, METH, TYPE0, VALUE0)
+
+/**
+ \brief Creates code to declare a callback function in line with instantiating a widget.
+
+ You can use this macro to create a function as a callback, allowing you to
+ define the callback function right where the widget and callback are declared,
+ similar to a Lambda function.
+
+ The first argument of the macro specifies the widget that will handle the
+ callback. Next, you can include up to five triplets, where each triplet
+ consists of a type, a parameter name, and a value. For example, `int, x, 3`
+ specifies an integer parameter with a value of 3. If you need to pass two
+ arguments, you can use two triplets, such as `int, x, 3, int, y, 4`. The last
+ digit of the macro name must be the same as the number of triplets (0..5).
+
+ The last argument is the actual function body itself.
+
+ The function body is limited to a syntax that the macro preprocessor can
+ handle. It should include the leading '{' and trailing '}' and may contain
+ local variable declarations, use global variables and functions, and use also
+ the variables listed and initialized in the argument triples of the macro.
+ Very large function bodies should be avoided because they may exceed the
+ admissible size of a macro argument.
+
+ Whenever the code generated by the macro is called, the custom parameters are
+ duplicated and marked for automatic deallocation using `delete` when the
+ callback widget is destroyed.
+
+ \code{.cpp}
+ #include <FL/fl_callback_macros.H>
+ ...
+ Fl_Button *btn = new Fl_Button(10, 10, 100, 20, "Test");
+ FL_INLINE_CALLBACK_1(btn,
+ Fl_String, name, btn->label(),
+ {
+ fl_message("Greetings from the %s button", name);
+ }
+ );
+ \endcode
+
+ You can find a small demonstration program showcasing the usage of
+ `FL_*_CALLBACK_*` in the `examples/callbacks.cxx` file.
+
+ \param WIDGET the widget that will call the callback
+ \param TYPE0 the type of the first parameter in the function call
+ \param NAME0 an arbitrary variable name that can be used as a parameter in the function body
+ \param VALUE0 a constant value or a variable; the value of the variable is copied when the callback is created
+ \param TYPE1 NAME1 VALUE1 as above; there are six macros that support 0 to 5 parameters
+ \param LAMBDA_FUNCTION_BODY the function body within the limits of the C macro preprocessor
+
+ \see FL_METHOD_CALLBACK_1, FL_FUNCTION_CALLBACK_3
+ */
+#define FL_INLINE_CALLBACK_2(WIDGET, TYPE0, NAME0, VALUE0, TYPE1, VALUE1, NAME1, LAMBDA_FUNCTION_BODY)
+
+#else // FL_DOXYGEN
+
+/*
+ These two macros make it possible to call macros with names that are created
+ by concatenating the name in x and (in this context) the number in y.
+ */
+#define _FL_CBD_CONCAT_IMPL(x, y) x##y
+#define _FL_CBD_CONCAT(x, y) _FL_CBD_CONCAT_IMPL(x, y)
+
+/*
+ Create a unique name for the derived class based on the current source code
+ line number.
+ */
+#define _FL_CBD_CLASS_NAME _FL_CBD_CONCAT(Fl_Callback_User_Data_,__LINE__)
+
+
+/*
+ These macros create boilerplate code for callbacks to functions and
+ static class methods with up to five arguments.
+
+ This macro invocation for example
+ ```
+ FL_FUNCTION_CALLBACK_2( func_cb_btn_2, hello_2_args_cb,
+ const char *, text, "FLTK",
+ int, number, 2 );
+ ```
+ will generate the following code:
+
+ ```
+ do {
+ class Fl_Callback_User_Data_92 : public Fl_Callback_User_Data {
+ public:
+ const char * p0_;
+ int p1_;
+ static void cb(Fl_Widget *w, void *user_data) {
+ Fl_Callback_User_Data_92 *d = (Fl_Callback_User_Data_92*)user_data;
+ hello_2_args_cb(d->p0_, d->p1_);
+ };
+ Fl_Callback_User_Data_92(const char * p0, int p1)
+ : p0_(p0),
+ p1_(p1)
+ { }
+ };
+ func_cb_btn_2->callback(Fl_Callback_User_Data_92::cb,
+ new Fl_Callback_User_Data_92("FLTK", 2),
+ true);
+ } while(0)
+ ```
+
+ Clicking the Fl_Button `func_cb_btn_2` will call `hello_2_args_cb("FLTK", 2)`.
+ Deleting the button will also delete the data that was created in our
+ boilerplate code.
+ */
+#define FL_FUNCTION_CALLBACK_5(WIDGET, FUNC, TYPE0, VALUE0, TYPE1, VALUE1, TYPE2, VALUE2, TYPE3, VALUE3, TYPE4, VALUE4) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ TYPE0 p0_; TYPE1 p1_; TYPE2 p2_; TYPE3 p3_; TYPE4 p4_; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ FUNC(d->p0_, d->p1_, d->p2_, d->p3_, d->p4_); \
+ }; \
+ _FL_CBD_CLASS_NAME(TYPE0 p0, TYPE1 p1, TYPE2 p2, TYPE3 p3, TYPE4 p4) \
+ : p0_(p0), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(VALUE0, VALUE1, VALUE2, VALUE3, VALUE4), true); \
+ } while(0)
+
+#define FL_FUNCTION_CALLBACK_4(WIDGET, FUNC, TYPE0, VALUE0, TYPE1, VALUE1, TYPE2, VALUE2, TYPE3, VALUE3) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ TYPE0 p0_; TYPE1 p1_; TYPE2 p2_; TYPE3 p3_; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ FUNC(d->p0_, d->p1_, d->p2_, d->p3_); \
+ }; \
+ _FL_CBD_CLASS_NAME(TYPE0 p0, TYPE1 p1, TYPE2 p2, TYPE3 p3) \
+ : p0_(p0), p1_(p1), p2_(p2), p3_(p3) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(VALUE0, VALUE1, VALUE2, VALUE3), true); \
+ } while(0)
+
+#define FL_FUNCTION_CALLBACK_3(WIDGET, FUNC, TYPE0, VALUE0, TYPE1, VALUE1, TYPE2, VALUE2) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ TYPE0 p0_; TYPE1 p1_; TYPE2 p2_; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ FUNC(d->p0_, d->p1_, d->p2_); \
+ }; \
+ _FL_CBD_CLASS_NAME(TYPE0 p0, TYPE1 p1, TYPE2 p2) \
+ : p0_(p0), p1_(p1), p2_(p2) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(VALUE0, VALUE1, VALUE2), true); \
+ } while(0)
+
+#define FL_FUNCTION_CALLBACK_2(WIDGET, FUNC, TYPE0, VALUE0, TYPE1, VALUE1) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ TYPE0 p0_; TYPE1 p1_; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ FUNC(d->p0_, d->p1_); \
+ }; \
+ _FL_CBD_CLASS_NAME(TYPE0 p0, TYPE1 p1) \
+ : p0_(p0), p1_(p1) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(VALUE0, VALUE1), true); \
+ } while(0)
+
+#define FL_FUNCTION_CALLBACK_1(WIDGET, FUNC, TYPE0, VALUE0) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ TYPE0 p0_; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ FUNC(d->p0_); \
+ }; \
+ _FL_CBD_CLASS_NAME(TYPE0 p0) \
+ : p0_(p0) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(VALUE0), true); \
+ } while(0)
+
+#define FL_FUNCTION_CALLBACK_0(WIDGET, FUNC) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ FUNC(); \
+ }; \
+ _FL_CBD_CLASS_NAME() { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(), true); \
+ } while(0)
+
+/*
+ These macros create boilerplate code for callbacks to class methods
+ with up to five arguments.
+
+ This macro invocation for example
+ ```
+ FL_METHOD_CALLBACK_4(btn,
+ MyWindow, win, resize,
+ int, test_x+10,
+ int, test_y+10,
+ int, 320,
+ int, 400);
+ ```
+ will generate the following code:
+
+ ```
+ do {
+ class Fl_Callback_User_Data_73 : public Fl_Callback_User_Data {
+ public:
+ int p0_;
+ int p1_;
+ int p2_;
+ int p3_;
+ MyWindow *self_;
+ static void cb(Fl_Widget *w, void *user_data) {
+ Fl_Callback_User_Data_73 *d = (Fl_Callback_User_Data_73*)user_data;
+ d->self_->resize(d->p0_, d->p1_, d->p2_, d->p3_);
+ };
+ Fl_Callback_User_Data_73(MyWindow *self, int p0, int p1, int p2, int p3)
+ : self_(self), p0_(p0), p1_(p1), p2_(p2), p3_(p3) { }
+ };
+ btn->callback(Fl_Callback_User_Data_73::cb,
+ new Fl_Callback_User_Data_73(win, test_x+10, test_y+10, 320, 400),
+ true);
+ } while(0);
+ ```
+
+ Clicking the Fl_Button `btn` will call
+ `win->resize(test_x+10, test_y+10, 320, 400);`.
+ Deleting the button will also delete the data that was created in our
+ boilerplate code.
+ */
+
+#define FL_METHOD_CALLBACK_5(WIDGET, CLASS, SELF, METHOD, TYPE0, VALUE0, TYPE1, VALUE1, TYPE2, VALUE2, TYPE3, VALUE3, TYPE4, VALUE4) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ CLASS *self_; \
+ TYPE0 p0_; TYPE1 p1_; TYPE2 p2_; TYPE3 p3_; TYPE4 p4_; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ d->self_->METHOD(d->p0_, d->p1_, d->p2_, d->p3_, d->p4_); \
+ }; \
+ _FL_CBD_CLASS_NAME(CLASS *self, TYPE0 p0, TYPE1 p1, TYPE2 p2, TYPE3 p3, TYPE4 p4) \
+ : self_(self), p0_(p0), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(SELF, VALUE0, VALUE1, VALUE2, VALUE3, VALUE4), true); \
+ } while(0)
+
+#define FL_METHOD_CALLBACK_4(WIDGET, CLASS, SELF, METHOD, TYPE0, VALUE0, TYPE1, VALUE1, TYPE2, VALUE2, TYPE3, VALUE3) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ CLASS *self_; \
+ TYPE0 p0_; TYPE1 p1_; TYPE2 p2_; TYPE3 p3_; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ d->self_->METHOD(d->p0_, d->p1_, d->p2_, d->p3_); \
+ }; \
+ _FL_CBD_CLASS_NAME(CLASS *self, TYPE0 p0, TYPE1 p1, TYPE2 p2, TYPE3 p3) \
+ : self_(self), p0_(p0), p1_(p1), p2_(p2), p3_(p3) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(SELF, VALUE0, VALUE1, VALUE2, VALUE3), true); \
+ } while(0)
+
+#define FL_METHOD_CALLBACK_3(WIDGET, CLASS, SELF, METHOD, TYPE0, VALUE0, TYPE1, VALUE1, TYPE2, VALUE2) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ CLASS *self_; \
+ TYPE0 p0_; TYPE1 p1_; TYPE2 p2_; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ d->self_->METHOD(d->p0_, d->p1_, d->p2_); \
+ }; \
+ _FL_CBD_CLASS_NAME(CLASS *self, TYPE0 p0, TYPE1 p1, TYPE2 p2) \
+ : self_(self), p0_(p0), p1_(p1), p2_(p2) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(SELF, VALUE0, VALUE1, VALUE2), true); \
+ } while(0)
+
+#define FL_METHOD_CALLBACK_2(WIDGET, CLASS, SELF, METHOD, TYPE0, VALUE0, TYPE1, VALUE1) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ CLASS *self_; \
+ TYPE0 p0_; TYPE1 p1_; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ d->self_->METHOD(d->p0_, d->p1_); \
+ }; \
+ _FL_CBD_CLASS_NAME(CLASS *self, TYPE0 p0, TYPE1 p1) \
+ : self_(self), p0_(p0), p1_(p1) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(SELF, VALUE0, VALUE1), true); \
+ } while(0)
+
+#define FL_METHOD_CALLBACK_1(WIDGET, CLASS, SELF, METHOD, TYPE0, VALUE0) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ CLASS *self_; \
+ TYPE0 p0_; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ d->self_->METHOD(d->p0_); \
+ }; \
+ _FL_CBD_CLASS_NAME(CLASS *self, TYPE0 p0) \
+ : self_(self), p0_(p0) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(SELF, VALUE0), true); \
+ } while(0)
+
+#define FL_METHOD_CALLBACK_0(WIDGET, CLASS, SELF, METHOD) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ CLASS *self_; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ d->self_->METHOD(); \
+ }; \
+ _FL_CBD_CLASS_NAME(CLASS *self) \
+ : self_(self) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(SELF), true); \
+ } while(0)
+
+/*
+ These macros create boilerplate code for callback functions inlined into
+ the widget creation code (similar to lambda functions in C++11 and up)
+ with up to five arguments.
+
+ This macro invocation for example
+ ```
+ FL_INLINE_CALLBACK_2( // callback has two parameters
+ btn, // attach callback to this button
+ const char *, text, "FLTK", // first parameter (type, name, value)
+ int, number, 2, // second parameter
+ { // function body
+ fl_message("We received the message %s with %d!", text, number);
+ }
+ );
+ ```
+ will generate the following code:
+ ```
+ do {
+ class Fl_Callback_User_Data_133 : public Fl_Callback_User_Data {
+ public:
+ const char * p0_; // store first parameter here
+ int p1_; // store second parameter here
+ // lambda style function
+ static void fn(const char * text, int number ) {
+ fl_message("We received the message %s with %d!", text, number);
+ };
+ // FLTK style callback
+ static void cb(Fl_Widget *w, void *user_data) {
+ Fl_Callback_User_Data_133 *d = (Fl_Callback_User_Data_133*)user_data;
+ fn(d->p0_, d->p1_);
+ };
+ // class constructor
+ Fl_Callback_User_Data_133(const char * p0, int p1)
+ : p0_(p0), // copy parameter 0
+ p1_(p1) // copy parameter 1
+ { } // constructor body
+ };
+ // connect our class to the widget callback
+ btn->callback(Fl_Callback_User_Data_133::cb,
+ new Fl_Callback_User_Data_133("FLTK", 2),
+ true);
+ } while(0); // user code adds semicolon
+ ```
+
+ Clicking the Fl_Button `btn` will call
+ `fl_message("We received the message %s with %d!", "FLTK", 2);`.
+ Deleting the button will also delete the data that was created in our
+ boilerplate code.
+ */
+
+#define FL_INLINE_CALLBACK_5(WIDGET, TYPE0, NAME0, VALUE0, TYPE1, NAME1, VALUE1, TYPE2, NAME2, VALUE2, TYPE3, NAME3, VALUE3, TYPE4, NAME4, VALUE4, LAMBDA) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ TYPE0 p0_; TYPE1 p1_; TYPE2 p2_; TYPE3 p3_; TYPE4 p4_; \
+ static void fn(TYPE0 NAME0, TYPE1 NAME1, TYPE2 NAME2, TYPE3 NAME3, TYPE4 NAME4) \
+ LAMBDA; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ _FL_CBD_CLASS_NAME::fn(d->p0_, d->p1_, d->p2_, d->p3_, d->p4_); \
+ }; \
+ _FL_CBD_CLASS_NAME(TYPE0 p0, TYPE1 p1, TYPE2 p2, TYPE3 p3, TYPE4 p4) \
+ : p0_(p0), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(VALUE0, VALUE1, VALUE2, VALUE3, VALUE4), true); \
+ } while(0)
+
+#define FL_INLINE_CALLBACK_4(WIDGET, TYPE0, NAME0, VALUE0, TYPE1, NAME1, VALUE1, TYPE2, NAME2, VALUE2, TYPE3, NAME3, VALUE3, LAMBDA) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ TYPE0 p0_; TYPE1 p1_; TYPE2 p2_; TYPE3 p3_; \
+ static void fn(TYPE0 NAME0, TYPE1 NAME1, TYPE2 NAME2, TYPE3 NAME3) \
+ LAMBDA; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ _FL_CBD_CLASS_NAME::fn(d->p0_, d->p1_, d->p2_, d->p3_); \
+ }; \
+ _FL_CBD_CLASS_NAME(TYPE0 p0, TYPE1 p1, TYPE2 p2, TYPE3 p3) \
+ : p0_(p0), p1_(p1), p2_(p2), p3_(p3) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(VALUE0, VALUE1, VALUE2, VALUE3), true); \
+ } while(0)
+
+#define FL_INLINE_CALLBACK_3(WIDGET, TYPE0, NAME0, VALUE0, TYPE1, NAME1, VALUE1, TYPE2, NAME2, VALUE2, LAMBDA) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ TYPE0 p0_; TYPE1 p1_; TYPE2 p2_; \
+ static void fn(TYPE0 NAME0, TYPE1 NAME1, TYPE2 NAME2) \
+ LAMBDA; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ _FL_CBD_CLASS_NAME::fn(d->p0_, d->p1_, d->p2_); \
+ }; \
+ _FL_CBD_CLASS_NAME(TYPE0 p0, TYPE1 p1, TYPE2 p2) \
+ : p0_(p0), p1_(p1), p2_(p2) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(VALUE0, VALUE1, VALUE2), true); \
+ } while(0)
+
+#define FL_INLINE_CALLBACK_2(WIDGET, TYPE0, NAME0, VALUE0, TYPE1, NAME1, VALUE1, LAMBDA) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ TYPE0 p0_; TYPE1 p1_; \
+ static void fn(TYPE0 NAME0, TYPE1 NAME1) \
+ LAMBDA; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ _FL_CBD_CLASS_NAME::fn(d->p0_, d->p1_); \
+ }; \
+ _FL_CBD_CLASS_NAME(TYPE0 p0, TYPE1 p1) \
+ : p0_(p0), p1_(p1) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(VALUE0, VALUE1), true); \
+ } while(0)
+
+#define FL_INLINE_CALLBACK_1(WIDGET, TYPE0, NAME0, VALUE0, LAMBDA) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ TYPE0 p0_; \
+ static void fn(TYPE0 NAME0) \
+ LAMBDA; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME *d = (_FL_CBD_CLASS_NAME*)user_data; \
+ _FL_CBD_CLASS_NAME::fn(d->p0_); \
+ }; \
+ _FL_CBD_CLASS_NAME(TYPE0 p0) \
+ : p0_(p0) { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(VALUE0), true); \
+ } while(0)
+
+#define FL_INLINE_CALLBACK_0(WIDGET, LAMBDA) \
+ do { \
+ class _FL_CBD_CLASS_NAME : public Fl_Callback_User_Data { \
+ public: \
+ static void fn() \
+ LAMBDA; \
+ static void cb(Fl_Widget *w, void *user_data) { \
+ _FL_CBD_CLASS_NAME::fn(); \
+ }; \
+ _FL_CBD_CLASS_NAME() { }; \
+ }; \
+ WIDGET->callback(_FL_CBD_CLASS_NAME::cb, new _FL_CBD_CLASS_NAME(), true); \
+ } while(0)
+
+#endif // FL_DOXYGEN
+
+#endif /* !_FL_FL_CALLBACK_MACROS_H_ */