diff --git a/.gitignore b/.gitignore index c339cd6f..3d4aa216 100644 --- a/.gitignore +++ b/.gitignore @@ -29,8 +29,7 @@ __pycache__/ *.pyc # Configuration -.config -.config.old +.config* config.h # CI pipeline diff --git a/apps/animation.c b/apps/animation.c index 592e34fb..ebc98906 100644 --- a/apps/animation.c +++ b/apps/animation.c @@ -56,8 +56,11 @@ static twin_time_t _apps_animation_timeout(twin_time_t now, void *closure) } static twin_dispatch_result_t _apps_animation_dispatch(twin_widget_t *widget, - twin_event_t *event) + twin_event_t *event, + void *closure) { + (void) closure; /* unused parameter */ + twin_custom_widget_t *custom = twin_widget_get_custom(widget); if (!custom) return TwinDispatchContinue; diff --git a/apps/calc.c b/apps/calc.c index 2a06dec9..aa433cf6 100644 --- a/apps/calc.c +++ b/apps/calc.c @@ -129,18 +129,20 @@ static void _apps_calc_digit(apps_calc_t *calc, int digit) _apps_calc_update_value(calc); } -static void _apps_calc_button_signal(twin_button_t *button, - twin_button_signal_t signal, - void *closure) +static twin_dispatch_result_t _apps_calc_button_clicked(twin_widget_t *widget, + twin_event_t *event, + void *data) { - apps_calc_t *calc = closure; + if (event->kind != TwinEventButtonSignalUp) + return TwinDispatchContinue; + + apps_calc_t *calc = data; + twin_button_t *button = (twin_button_t *) widget; int a, b; - if (signal != TwinButtonSignalDown) - return; int i = _apps_calc_button_to_id(calc, button); if (i < 0) - return; + return TwinDispatchContinue; switch (i) { #define _(x) APPS_CALC_##x @@ -191,6 +193,7 @@ static void _apps_calc_button_signal(twin_button_t *button, _apps_calc_digit(calc, i); break; } + return TwinDispatchDone; } void apps_calc_start(twin_screen_t *screen, @@ -220,8 +223,8 @@ void apps_calc_start(twin_screen_t *screen, APPS_CALC_BUTTON_SIZE, APPS_CALC_BUTTON_STYLE); twin_widget_set(&calc->buttons[b]->label.widget, APPS_CALC_BUTTON_BG); - calc->buttons[b]->signal = _apps_calc_button_signal; - calc->buttons[b]->closure = calc; + twin_widget_set_callback(&calc->buttons[b]->label.widget, + _apps_calc_button_clicked, calc); calc->buttons[b]->label.widget.shape = TwinShapeEllipse; if (i || j) calc->buttons[b]->label.widget.copy_geom = diff --git a/apps/clock.c b/apps/clock.c index 591e8ad5..518f4084 100644 --- a/apps/clock.c +++ b/apps/clock.c @@ -204,8 +204,11 @@ static twin_time_t _apps_clock_timeout(twin_time_t now, void *closure) } static twin_dispatch_result_t _apps_clock_dispatch(twin_widget_t *widget, - twin_event_t *event) + twin_event_t *event, + void *closure) { + (void) closure; /* unused parameter */ + twin_custom_widget_t *custom = twin_widget_get_custom(widget); if (!custom) return TwinDispatchContinue; diff --git a/apps/image.c b/apps/image.c index b407e340..4ae5e7a3 100644 --- a/apps/image.c +++ b/apps/image.c @@ -54,8 +54,11 @@ static void _apps_image_paint(twin_custom_widget_t *custom) } static twin_dispatch_result_t _apps_image_dispatch(twin_widget_t *widget, - twin_event_t *event) + twin_event_t *event, + void *closure) { + (void) closure; /* unused parameter */ + twin_custom_widget_t *custom = twin_widget_get_custom(widget); if (!custom) return TwinDispatchContinue; @@ -70,15 +73,16 @@ static twin_dispatch_result_t _apps_image_dispatch(twin_widget_t *widget, return TwinDispatchContinue; } -static void _apps_image_button_signal(twin_button_t *button, - twin_button_signal_t signal, - void *closure) +static twin_dispatch_result_t _apps_image_button_clicked(twin_widget_t *widget, + twin_event_t *event, + void *data) { - (void) button; /* unused parameter */ - if (signal != TwinButtonSignalDown) - return; + (void) widget; /* unused parameter */ + + if (event->kind != TwinEventButtonSignalUp) + return TwinDispatchContinue; - twin_custom_widget_t *custom = closure; + twin_custom_widget_t *custom = data; apps_image_data_t *img = (apps_image_data_t *) twin_custom_widget_data(custom); const int n = sizeof(tvg_files) / sizeof(tvg_files[0]); @@ -87,10 +91,11 @@ static void _apps_image_button_signal(twin_button_t *button, twin_pixmap_t *pix = twin_tvg_to_pixmap_scale( tvg_files[img->image_idx], TWIN_ARGB32, APP_WIDTH, APP_HEIGHT); if (!pix) - return; + return TwinDispatchContinue; img->pixes[img->image_idx] = pix; } twin_custom_widget_queue_paint(custom); + return TwinDispatchDone; } static twin_custom_widget_t *_apps_image_init(twin_box_t *parent) @@ -113,8 +118,8 @@ static twin_custom_widget_t *_apps_image_init(twin_box_t *parent) twin_button_create(parent, "Next Image", 0xFF482722, D(10), TwinStyleBold | TwinStyleOblique); twin_widget_set(&button->label.widget, 0xFFFEE4CE); - button->signal = _apps_image_button_signal; - button->closure = custom; + twin_widget_set_callback(&button->label.widget, _apps_image_button_clicked, + custom); button->label.widget.shape = TwinShapeRectangle; return custom; diff --git a/apps/line.c b/apps/line.c index e0536a0d..4b077289 100644 --- a/apps/line.c +++ b/apps/line.c @@ -71,8 +71,11 @@ static int _apps_line_hit(apps_line_data_t *line, } static twin_dispatch_result_t _apps_line_dispatch(twin_widget_t *widget, - twin_event_t *event) + twin_event_t *event, + void *closure) { + (void) closure; /* unused parameter */ + twin_custom_widget_t *custom = twin_widget_get_custom(widget); if (!custom) return TwinDispatchContinue; diff --git a/apps/spline.c b/apps/spline.c index 68bf2f2c..4d339990 100644 --- a/apps/spline.c +++ b/apps/spline.c @@ -113,20 +113,22 @@ static void _apps_spline_paint(twin_custom_widget_t *custom) twin_path_destroy(path); } -static void _apps_spline_button_signal(twin_button_t *button, - twin_button_signal_t signal, - void *closure) +static twin_dispatch_result_t _apps_spline_button_clicked(twin_widget_t *widget, + twin_event_t *event, + void *data) { - (void) button; /* unused parameter */ - if (signal != TwinButtonSignalDown) - return; + (void) widget; /* unused parameter */ - twin_custom_widget_t *custom = closure; + if (event->kind != TwinEventButtonSignalUp) + return TwinDispatchContinue; + + twin_custom_widget_t *custom = data; apps_spline_data_t *spline = (apps_spline_data_t *) twin_custom_widget_data(custom); spline->n_points = (spline->n_points == 3) ? 4 : 3; _init_control_point(spline); twin_custom_widget_queue_paint(custom); + return TwinDispatchDone; } static twin_dispatch_result_t _apps_spline_update_pos( @@ -167,8 +169,11 @@ static int _apps_spline_hit(apps_spline_data_t *spline, } static twin_dispatch_result_t _apps_spline_dispatch(twin_widget_t *widget, - twin_event_t *event) + twin_event_t *event, + void *closure) { + (void) closure; /* unused parameter */ + twin_custom_widget_t *custom = twin_widget_get_custom(widget); if (!custom) return TwinDispatchContinue; @@ -227,8 +232,8 @@ static twin_custom_widget_t *_apps_spline_init(twin_box_t *parent, int n_points) twin_button_create(parent, "Switch curve", 0xffae0000, D(10), TwinStyleBold | TwinStyleOblique); twin_widget_set(&button->label.widget, 0xc0808080); - button->signal = _apps_spline_button_signal; - button->closure = custom; + twin_widget_set_callback(&button->label.widget, _apps_spline_button_clicked, + custom); button->label.widget.shape = TwinShapeRectangle; return custom; diff --git a/include/twin.h b/include/twin.h index 10285438..1cfd07f7 100644 --- a/include/twin.h +++ b/include/twin.h @@ -116,6 +116,12 @@ typedef struct _twin_screen twin_screen_t; typedef struct _twin_pixmap twin_pixmap_t; typedef struct _twin_animation twin_animation_t; +/** Button signal types (used in unified event system) */ +typedef enum _twin_button_signal { + TwinButtonSignalDown /**< Sent when button pressed */, + TwinButtonSignalUp /**< Sent when button released inside widget */ +} twin_button_signal_t; + /** * Event type enumeration for input and system events * @@ -147,7 +153,11 @@ typedef enum _twin_event_kind { TwinEventPaint = 0x1001 /**< Widget needs painting */, TwinEventQueryGeometry = 0x1002 /**< Widget geometry query */, TwinEventConfigure = 0x1003 /**< Widget configuration change */, - TwinEventDestroy = 0x1004 /**< Widget destruction */ + TwinEventDestroy = 0x1004 /**< Widget destruction */, + + /* Button signals (unified with event system) */ + TwinEventButtonSignalDown = 0x2001 /**< Button pressed signal */, + TwinEventButtonSignalUp = 0x2002 /**< Button released signal */ } twin_event_kind_t; /** @@ -177,7 +187,10 @@ typedef struct _twin_event { struct { twin_rect_t extents; /**< New widget geometry */ } configure; /**< Widget configuration event data */ - } u; /**< Event-specific data union */ + struct { + twin_button_signal_t signal; /**< Button signal type */ + } button_signal; /**< Button signal event data */ + } u; /**< Event-specific data union */ } twin_event_t; /** @@ -588,11 +601,27 @@ typedef enum _twin_box_dir { typedef enum _twin_dispatch_result { TwinDispatchDone /**< Event processing complete */, - TwinDispatchContinue /**< Continue event propagation */ + TwinDispatchContinue /**< Continue event propagation */, + TwinDispatchReject /**< Cannot handle this event */ } twin_dispatch_result_t; -typedef twin_dispatch_result_t (*twin_dispatch_proc_t)(twin_widget_t *widget, - twin_event_t *event); +/** + * Widget event handler function signature + * + * All widget event handlers use this signature with closure support. + * The closure parameter enables stateful event handling without global state. + * + * @widget : Widget receiving the event + * @event : Event to process + * @closure : User data pointer for stateful handling + * @return : Dispatch result (Done, Continue, or Reject) + * + * This type is used for both framework-level handlers and application + * callbacks. + */ +typedef twin_dispatch_result_t (*twin_widget_proc_t)(twin_widget_t *widget, + twin_event_t *event, + void *closure); typedef struct _twin_widget_layout { twin_coord_t width, height; /**< Preferred dimensions */ @@ -614,10 +643,15 @@ struct _twin_widget { twin_widget_t *next; /**< Next sibling widget */ twin_box_t *parent; /**< Parent container */ - /* Widget behavior */ - twin_dispatch_proc_t dispatch; /**< Event dispatch handler */ - twin_rect_t extents; /**< Current geometry */ - twin_widget_t *copy_geom; /**< Geometry source widget */ + /* Event handling: + * - handler: Framework event handler (processes paint, configure, etc.) + * - callback: Application callback (optional, receives button clicks, etc.) + */ + twin_widget_proc_t handler; /**< Widget event handler (framework) */ + twin_widget_proc_t callback; /**< Application callback (optional) */ + void *callback_data; /**< Callback user data */ + twin_rect_t extents; /**< Current geometry */ + twin_widget_t *copy_geom; /**< Geometry source widget */ /* Widget state */ bool paint; /**< Needs painting */ @@ -659,41 +693,13 @@ typedef struct _twin_label { twin_align_t align; /**< Text alignment */ } twin_label_t; -typedef enum _twin_button_signal { - TwinButtonSignalDown /**< Sent when button pressed */, - TwinButtonSignalUp /**< Sent when button released inside widget */ -} twin_button_signal_t; - typedef struct _twin_button twin_button_t; -typedef void (*twin_button_signal_proc_t)(twin_button_t *button, - twin_button_signal_t signal, - void *closure); struct _twin_button { - twin_label_t label; /**< Base label widget */ - bool pressed; /**< Button pressed state */ - bool active; /**< Button active state */ - twin_button_signal_proc_t signal; /**< Signal callback */ - void *closure; /**< Callback closure */ -}; - -typedef enum _twin_scroll_signal { - TwinScrollSignalUpArrow /**< Up arrow clicked */, - TwinScrollSignalDownArrow /**< Down arrow clicked */, - TwinScrollSignalThumb /**< Thumb/slider clicked */, - TwinScrollSignalAboveThumb /**< Area above thumb clicked */, - TwinScrollSignalBelowThumb /**< Area below thumb clicked */ -} twin_scroll_signal_t; - -typedef struct _twin_scroll twin_scroll_t; - -typedef void (*twin_scroll_signal_proc_t)(twin_scroll_t *scroll, - twin_scroll_signal_t signal, - void *closure); - -struct _twin_scroll { - twin_widget_t widget; /**< Base widget */ + twin_label_t label; /**< Base label widget */ + bool pressed; /**< Button pressed state */ + bool active; /**< Button active state */ }; typedef struct _twin_context { @@ -726,6 +732,20 @@ twin_button_t *twin_button_create(twin_box_t *parent, twin_fixed_t font_size, twin_style_t font_style); + +/** + * Set application callback for widget events (low-level API) + * @widget : Widget to configure + * @callback : Application callback function + * @data : User data passed to callback + * + * Allows applications to respond to widget events (e.g., button clicks). + * The callback receives events after the widget's core handler processes them. + */ +void twin_widget_set_callback(twin_widget_t *widget, + twin_widget_proc_t callback, + void *data); + /** * Create default mouse cursor pixmap * @hx : Output hotspot X coordinate @@ -1376,14 +1396,14 @@ twin_fixed_t twin_widget_height(twin_widget_t *widget); /* Request widget repaint */ void twin_widget_queue_paint(twin_widget_t *widget); -/* Create widget with custom dispatch handler */ -twin_widget_t *twin_widget_create_with_dispatch(twin_box_t *parent, - twin_argb32_t background, - twin_coord_t width, - twin_coord_t height, - twin_stretch_t hstretch, - twin_stretch_t vstretch, - twin_dispatch_proc_t dispatch); +/* Create widget with custom event handler */ +twin_widget_t *twin_widget_create_with_handler(twin_box_t *parent, + twin_argb32_t background, + twin_coord_t width, + twin_coord_t height, + twin_stretch_t hstretch, + twin_stretch_t vstretch, + twin_widget_proc_t handler); /* * Custom widget support - allows creating widgets without accessing internals @@ -1403,7 +1423,7 @@ typedef struct { } twin_custom_widget_t; /** - * Create a custom widget with user-defined data and dispatch handler. + * Create a custom widget with user-defined data and event handler. * * @parent : Parent box widget to contain this widget * @background : Background color (ARGB32 format) @@ -1411,11 +1431,11 @@ typedef struct { * @height : Preferred height in pixels (0 for flexible) * @hstretch : Horizontal stretch factor for layout * @vstretch : Vertical stretch factor for layout - * @dispatch : Custom event dispatch function for this widget + * @handler : Custom event handler function for this widget * @data_size : Size of custom data to allocate (0 for no data) * @return : Newly created custom widget, or NULL on failure * - * The dispatch function will be called for all events sent to this widget. + * The handler function will be called for all events sent to this widget. * Custom data (if requested) is zero-initialized and accessible via * twin_custom_widget_data(). */ @@ -1425,7 +1445,7 @@ twin_custom_widget_t *twin_custom_widget_create(twin_box_t *parent, twin_coord_t height, twin_stretch_t hstretch, twin_stretch_t vstretch, - twin_dispatch_proc_t dispatch, + twin_widget_proc_t handler, size_t data_size); /** diff --git a/src/box.c b/src/box.c index 4c5ec6d1..4650c82c 100644 --- a/src/box.c +++ b/src/box.c @@ -12,10 +12,10 @@ void _twin_box_init(twin_box_t *box, twin_box_t *parent, twin_window_t *window, twin_box_dir_t dir, - twin_dispatch_proc_t dispatch) + twin_widget_proc_t handler) { static twin_widget_layout_t preferred = {0, 0, 0, 0}; - _twin_widget_init(&box->widget, parent, window, preferred, dispatch); + _twin_widget_init(&box->widget, parent, window, preferred, handler); box->dir = dir; box->children = NULL; box->button_down = NULL; @@ -41,7 +41,7 @@ static twin_dispatch_result_t _twin_box_query_geometry(twin_box_t *box) for (twin_widget_t *child = box->children; child; child = child->next) { if (child->layout) { ev.kind = TwinEventQueryGeometry; - (*child->dispatch)(child, &ev); + child->handler(child, &ev, child->callback_data); } if (box->dir == TwinBoxHorz) { preferred.width += child->preferred.width; @@ -129,7 +129,7 @@ static twin_dispatch_result_t _twin_box_configure(twin_box_t *box) extents.bottom != ev.u.configure.extents.bottom) { ev.kind = TwinEventConfigure; ev.u.configure.extents = extents; - (*child->dispatch)(child, &ev); + child->handler(child, &ev, child->callback_data); } } return TwinDispatchContinue; @@ -148,14 +148,15 @@ static twin_widget_t *_twin_box_xy_to_widget(twin_box_t *box, } twin_dispatch_result_t _twin_box_dispatch(twin_widget_t *widget, - twin_event_t *event) + twin_event_t *event, + void *closure) { twin_box_t *box = (twin_box_t *) widget; twin_event_t ev; twin_widget_t *child; if (event->kind != TwinEventPaint && - _twin_widget_dispatch(widget, event) == TwinDispatchDone) + _twin_widget_dispatch(widget, event, closure) == TwinDispatchDone) return TwinDispatchDone; switch (event->kind) { case TwinEventDestroy: @@ -166,7 +167,7 @@ twin_dispatch_result_t _twin_box_dispatch(twin_widget_t *widget, /* Send destroy event to child */ ev.kind = TwinEventDestroy; - (*child->dispatch)(child, &ev); + child->handler(child, &ev, child->callback_data); } break; case TwinEventQueryGeometry: @@ -187,14 +188,15 @@ twin_dispatch_result_t _twin_box_dispatch(twin_widget_t *widget, ev = *event; ev.u.pointer.x -= child->extents.left; ev.u.pointer.y -= child->extents.top; - return (*box->button_down->dispatch)(child, &ev); + return child->handler(child, &ev, child->callback_data); } break; case TwinEventKeyDown: case TwinEventKeyUp: case TwinEventUcs4: if (box->focus) - return (*box->focus->dispatch)(box->focus, event); + return box->focus->handler(box->focus, event, + box->focus->callback_data); break; case TwinEventPaint: box->widget.paint = false; @@ -213,7 +215,7 @@ twin_dispatch_result_t _twin_box_dispatch(twin_widget_t *widget, twin_pixmap_set_clip(pixmap, child->extents); twin_pixmap_origin_to_clip(pixmap); child->paint = false; - (*child->dispatch)(child, event); + child->handler(child, event, child->callback_data); twin_pixmap_restore_clip(pixmap, clip); twin_pixmap_set_origin(pixmap, ox, oy); } diff --git a/src/button.c b/src/button.c index c8836564..fd8ee7c7 100644 --- a/src/button.c +++ b/src/button.c @@ -29,11 +29,12 @@ static void _twin_button_set_label_offset(twin_button_t *button) } twin_dispatch_result_t _twin_button_dispatch(twin_widget_t *widget, - twin_event_t *event) + twin_event_t *event, + void *closure) { twin_button_t *button = (twin_button_t *) widget; - if (_twin_label_dispatch(widget, event) == TwinDispatchDone) + if (_twin_label_dispatch(widget, event, closure) == TwinDispatchDone) return TwinDispatchDone; switch (event->kind) { case TwinEventPaint: @@ -43,8 +44,14 @@ twin_dispatch_result_t _twin_button_dispatch(twin_widget_t *widget, button->pressed = true; button->active = true; _twin_button_set_label_offset(button); - if (button->signal) - (*button->signal)(button, TwinButtonSignalDown, button->closure); + + /* Invoke widget callback for button press */ + if (widget->callback) { + twin_event_t press_event = *event; + press_event.kind = TwinEventButtonSignalDown; + press_event.u.button_signal.signal = TwinButtonSignalDown; + (*widget->callback)(widget, &press_event, widget->callback_data); + } return TwinDispatchDone; break; case TwinEventMotion: @@ -63,8 +70,15 @@ twin_dispatch_result_t _twin_button_dispatch(twin_widget_t *widget, if (button->active) { button->active = false; _twin_button_set_label_offset(button); - if (button->signal) - (*button->signal)(button, TwinButtonSignalUp, button->closure); + + /* Invoke widget callback for button release (click) */ + if (widget->callback) { + twin_event_t release_event = *event; + release_event.kind = TwinEventButtonSignalUp; + release_event.u.button_signal.signal = TwinButtonSignalUp; + (*widget->callback)(widget, &release_event, + widget->callback_data); + } } return TwinDispatchDone; break; @@ -80,14 +94,12 @@ void _twin_button_init(twin_button_t *button, twin_argb32_t foreground, twin_fixed_t font_size, twin_style_t font_style, - twin_dispatch_proc_t dispatch) + twin_widget_proc_t handler) { _twin_label_init(&button->label, parent, value, foreground, font_size, - font_style, dispatch); + font_style, handler); button->pressed = false; button->active = false; - button->signal = NULL; - button->closure = NULL; _twin_button_set_label_offset(button); } diff --git a/src/label.c b/src/label.c index 19caf66a..c75764c6 100644 --- a/src/label.c +++ b/src/label.c @@ -60,11 +60,12 @@ static void _twin_label_paint(twin_label_t *label) } twin_dispatch_result_t _twin_label_dispatch(twin_widget_t *widget, - twin_event_t *event) + twin_event_t *event, + void *closure) { twin_label_t *label = (twin_label_t *) widget; - if (_twin_widget_dispatch(widget, event) == TwinDispatchDone) + if (_twin_widget_dispatch(widget, event, closure) == TwinDispatchDone) return TwinDispatchDone; switch (event->kind) { case TwinEventPaint: @@ -112,10 +113,10 @@ void _twin_label_init(twin_label_t *label, twin_argb32_t foreground, twin_fixed_t font_size, twin_style_t font_style, - twin_dispatch_proc_t dispatch) + twin_widget_proc_t handler) { static const twin_widget_layout_t preferred = {0, 0, 1, 1}; - _twin_widget_init(&label->widget, parent, 0, preferred, dispatch); + _twin_widget_init(&label->widget, parent, 0, preferred, handler); label->label = NULL; label->offset.x = 0; label->offset.y = 0; diff --git a/src/toplevel.c b/src/toplevel.c index ef9b641a..2e112a82 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -9,7 +9,8 @@ #include "twin_private.h" twin_dispatch_result_t _twin_toplevel_dispatch(twin_widget_t *widget, - twin_event_t *event) + twin_event_t *event, + void *closure) { twin_toplevel_t *toplevel = (twin_toplevel_t *) widget; twin_event_t ev = *event; @@ -26,15 +27,15 @@ twin_dispatch_result_t _twin_toplevel_dispatch(twin_widget_t *widget, default: break; } - return _twin_box_dispatch(&toplevel->box.widget, &ev); + return _twin_box_dispatch(&toplevel->box.widget, &ev, closure); } static bool _twin_toplevel_event(twin_window_t *window, twin_event_t *event) { twin_toplevel_t *toplevel = window->client_data; - return (*toplevel->box.widget.dispatch)(&toplevel->box.widget, event) == - TwinDispatchDone; + return (*(&toplevel->box.widget)->handler)(&toplevel->box.widget, event, + NULL) == TwinDispatchDone; } static void _twin_toplevel_draw(twin_window_t *window) @@ -44,7 +45,7 @@ static void _twin_toplevel_draw(twin_window_t *window) twin_screen_disable_update(window->screen); event.kind = TwinEventPaint; - (*toplevel->box.widget.dispatch)(&toplevel->box.widget, &event); + (*(&toplevel->box.widget)->handler)(&toplevel->box.widget, &event, NULL); twin_screen_enable_update(window->screen); } @@ -60,11 +61,11 @@ static void _twin_toplevel_destroy(twin_window_t *window) _twin_closure_unregister(toplevel); event.kind = TwinEventDestroy; - (*toplevel->box.widget.dispatch)(&toplevel->box.widget, &event); + (*(&toplevel->box.widget)->handler)(&toplevel->box.widget, &event, NULL); } void _twin_toplevel_init(twin_toplevel_t *toplevel, - twin_dispatch_proc_t dispatch, + twin_widget_proc_t dispatch, twin_window_t *window, const char *name) { @@ -110,7 +111,7 @@ static bool _twin_toplevel_paint(void *closure) twin_screen_disable_update(toplevel->box.widget.window->screen); ev.kind = TwinEventPaint; - (*toplevel->box.widget.dispatch)(&toplevel->box.widget, &ev); + (*(&toplevel->box.widget)->handler)(&toplevel->box.widget, &ev, NULL); twin_screen_enable_update(toplevel->box.widget.window->screen); return false; } @@ -132,13 +133,13 @@ static bool _twin_toplevel_layout(void *closure) twin_window_t *window = toplevel->box.widget.window; ev.kind = TwinEventQueryGeometry; - (*toplevel->box.widget.dispatch)(&toplevel->box.widget, &ev); + (*(&toplevel->box.widget)->handler)(&toplevel->box.widget, &ev, NULL); ev.kind = TwinEventConfigure; ev.u.configure.extents.left = 0; ev.u.configure.extents.top = 0; ev.u.configure.extents.right = window->client.right - window->client.left; ev.u.configure.extents.bottom = window->client.bottom - window->client.top; - (*toplevel->box.widget.dispatch)(&toplevel->box.widget, &ev); + (*(&toplevel->box.widget)->handler)(&toplevel->box.widget, &ev, NULL); return false; } diff --git a/src/twin_private.h b/src/twin_private.h index 7a3b6fc5..4a24f57e 100644 --- a/src/twin_private.h +++ b/src/twin_private.h @@ -544,16 +544,17 @@ void _twin_box_init(twin_box_t *box, twin_box_t *parent, twin_window_t *window, twin_box_dir_t dir, - twin_dispatch_proc_t dispatch); + twin_widget_proc_t dispatch); twin_dispatch_result_t _twin_box_dispatch(twin_widget_t *widget, - twin_event_t *event); + twin_event_t *event, + void *closure); void _twin_widget_init(twin_widget_t *widget, twin_box_t *parent, twin_window_t *window, twin_widget_layout_t preferred, - twin_dispatch_proc_t dispatch); + twin_widget_proc_t dispatch); void _twin_widget_paint_shape(twin_widget_t *widget, twin_shape_t shape, @@ -564,7 +565,9 @@ void _twin_widget_paint_shape(twin_widget_t *widget, twin_fixed_t radius); twin_dispatch_result_t _twin_widget_dispatch(twin_widget_t *widget, - twin_event_t *event); + twin_event_t *event, + void *closure); + void _twin_widget_queue_paint(twin_widget_t *widget); @@ -584,16 +587,18 @@ void _twin_label_init(twin_label_t *label, twin_argb32_t foreground, twin_fixed_t font_size, twin_style_t font_style, - twin_dispatch_proc_t dispatch); + twin_widget_proc_t dispatch); twin_dispatch_result_t _twin_label_dispatch(twin_widget_t *widget, - twin_event_t *event); + twin_event_t *event, + void *closure); twin_dispatch_result_t _twin_toplevel_dispatch(twin_widget_t *widget, - twin_event_t *event); + twin_event_t *event, + void *closure); void _twin_toplevel_init(twin_toplevel_t *toplevel, - twin_dispatch_proc_t dispatch, + twin_widget_proc_t dispatch, twin_window_t *window, const char *name); @@ -602,7 +607,8 @@ void _twin_toplevel_queue_paint(twin_widget_t *widget); void _twin_toplevel_queue_layout(twin_widget_t *widget); twin_dispatch_result_t _twin_button_dispatch(twin_widget_t *widget, - twin_event_t *event); + twin_event_t *event, + void *closure); void _twin_button_init(twin_button_t *button, twin_box_t *parent, @@ -610,7 +616,7 @@ void _twin_button_init(twin_button_t *button, twin_argb32_t foreground, twin_fixed_t font_size, twin_style_t font_style, - twin_dispatch_proc_t dispatch); + twin_widget_proc_t dispatch); typedef struct twin_backend { /* Initialize the backend */ diff --git a/src/widget.c b/src/widget.c index cacb6772..64a38561 100644 --- a/src/widget.c +++ b/src/widget.c @@ -75,16 +75,20 @@ static void _twin_widget_paint(twin_widget_t *widget) _twin_widget_height(widget), widget->radius); } + twin_dispatch_result_t _twin_widget_dispatch(twin_widget_t *widget, - twin_event_t *event) + twin_event_t *event, + void *closure) { + (void) closure; /* Base widget doesn't use closure */ + switch (event->kind) { case TwinEventQueryGeometry: widget->layout = false; if (widget->copy_geom) { twin_widget_t *copy = widget->copy_geom; if (copy->layout) - (*copy->dispatch)(copy, event); + copy->handler(copy, event, copy->callback_data); widget->preferred = copy->preferred; return TwinDispatchDone; } @@ -109,7 +113,7 @@ void _twin_widget_init(twin_widget_t *widget, twin_box_t *parent, twin_window_t *window, twin_widget_layout_t preferred, - twin_dispatch_proc_t dispatch) + twin_widget_proc_t handler) { if (parent) { twin_widget_t **prev; @@ -132,7 +136,9 @@ void _twin_widget_init(twin_widget_t *widget, widget->extents.left = widget->extents.top = 0; widget->extents.right = widget->extents.bottom = 0; widget->preferred = preferred; - widget->dispatch = dispatch; + widget->handler = handler; + widget->callback = NULL; + widget->callback_data = NULL; widget->shape = TwinShapeRectangle; widget->radius = twin_int_to_fixed(12); } @@ -257,13 +263,13 @@ void twin_widget_queue_paint(twin_widget_t *widget) _twin_widget_queue_paint(widget); } -twin_widget_t *twin_widget_create_with_dispatch(twin_box_t *parent, - twin_argb32_t background, - twin_coord_t width, - twin_coord_t height, - twin_stretch_t stretch_width, - twin_stretch_t stretch_height, - twin_dispatch_proc_t dispatch) +twin_widget_t *twin_widget_create_with_handler(twin_box_t *parent, + twin_argb32_t background, + twin_coord_t width, + twin_coord_t height, + twin_stretch_t stretch_width, + twin_stretch_t stretch_height, + twin_widget_proc_t handler) { twin_widget_t *widget = malloc(sizeof(twin_widget_t)); if (!widget) @@ -275,7 +281,7 @@ twin_widget_t *twin_widget_create_with_dispatch(twin_box_t *parent, .stretch_width = stretch_width, .stretch_height = stretch_height, }; - _twin_widget_init(widget, parent, 0, preferred, dispatch); + _twin_widget_init(widget, parent, 0, preferred, handler); widget->background = background; return widget; } @@ -284,7 +290,7 @@ twin_widget_t *twin_widget_create_with_dispatch(twin_box_t *parent, typedef struct _custom_widget_map { twin_widget_t *widget; twin_custom_widget_t *custom; - twin_dispatch_proc_t user_dispatch; + twin_widget_proc_t user_dispatch; struct _custom_widget_map *next; } custom_widget_map_t; @@ -292,7 +298,7 @@ static custom_widget_map_t *custom_widget_map = NULL; static void register_custom_widget(twin_widget_t *widget, twin_custom_widget_t *custom, - twin_dispatch_proc_t user_dispatch) + twin_widget_proc_t user_dispatch) { custom_widget_map_t *entry = malloc(sizeof(custom_widget_map_t)); if (!entry) @@ -335,7 +341,8 @@ static void unregister_custom_widget(twin_widget_t *widget) } static twin_dispatch_result_t custom_widget_dispatch(twin_widget_t *widget, - twin_event_t *event) + twin_event_t *event, + void *closure) { /* Handle destroy events specially to ensure proper cleanup order */ if (event->kind == TwinEventDestroy) { @@ -344,7 +351,7 @@ static twin_dispatch_result_t custom_widget_dispatch(twin_widget_t *widget, while (entry) { if (entry->widget == widget) { if (entry->user_dispatch) - entry->user_dispatch(widget, event); + entry->user_dispatch(widget, event, closure); break; } entry = entry->next; @@ -354,11 +361,12 @@ static twin_dispatch_result_t custom_widget_dispatch(twin_widget_t *widget, unregister_custom_widget(widget); /* Now call base widget dispatch to complete destruction */ - return _twin_widget_dispatch(widget, event); + return _twin_widget_dispatch(widget, event, closure); } /* First call the base widget dispatch to handle standard widget behavior */ - twin_dispatch_result_t result = _twin_widget_dispatch(widget, event); + twin_dispatch_result_t result = + _twin_widget_dispatch(widget, event, closure); if (result == TwinDispatchDone) return result; @@ -367,7 +375,7 @@ static twin_dispatch_result_t custom_widget_dispatch(twin_widget_t *widget, while (entry) { if (entry->widget == widget) { if (entry->user_dispatch) - return entry->user_dispatch(widget, event); + return entry->user_dispatch(widget, event, closure); break; } entry = entry->next; @@ -381,7 +389,7 @@ twin_custom_widget_t *twin_custom_widget_create(twin_box_t *parent, twin_coord_t height, twin_stretch_t hstretch, twin_stretch_t vstretch, - twin_dispatch_proc_t dispatch, + twin_widget_proc_t handler, size_t data_size) { twin_custom_widget_t *custom = malloc(sizeof(twin_custom_widget_t)); @@ -399,16 +407,16 @@ twin_custom_widget_t *twin_custom_widget_create(twin_box_t *parent, custom->data = NULL; } - custom->widget = twin_widget_create_with_dispatch( - parent, background, width, height, hstretch, vstretch, - custom_widget_dispatch); + custom->widget = twin_widget_create_with_handler(parent, background, width, + height, hstretch, vstretch, + custom_widget_dispatch); if (!custom->widget) { free(custom->data); free(custom); return NULL; } - register_custom_widget(custom->widget, custom, dispatch); + register_custom_widget(custom->widget, custom, handler); return custom; } @@ -471,3 +479,13 @@ twin_pixmap_t *twin_custom_widget_pixmap(twin_custom_widget_t *custom) { return custom ? twin_widget_pixmap(custom->widget) : NULL; } + +void twin_widget_set_callback(twin_widget_t *widget, + twin_widget_proc_t callback, + void *data) +{ + if (!widget) + return; + widget->callback = callback; + widget->callback_data = data; +}