diff options
| author | Matthias Melcher <github@matthiasm.com> | 2023-08-15 11:36:58 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-08-15 11:36:58 +0200 |
| commit | 10d9010ed9a7624bebebcb0f86fc86d362672027 (patch) | |
| tree | 4303c6f36947046d2baab01f02defd0d4bdb9ab2 /FL | |
| parent | e6440ca0a89874e2593b2dc9cf5a3b0e87df94a2 (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.H | 54 | ||||
| -rw-r--r-- | FL/fl_callback_macros.H | 607 |
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_ */ |
