Skip to content

Commit e239453

Browse files
committed
Unify widget callback mechanism with closure
Previous design had widget-specific callback mechanisms (button signals, scroll signals) with duplicated closure management code. This commit introduces two complementary callback patterns: 1. Widget-level dispatch (for custom widgets and complex event handling) - Added dispatch_ex with (widget, event, closure) signature - Unified closure storage in twin_widget_t - Used by custom widgets and applications needing full event access 2. Button-specific API (for simple click handling) - Added twin_button_on_clicked(button, callback, data) - Simplified signature: void (*)(twin_button_t*, void*) - Direct callback invocation on button click - Used by calc, image, spline demos Button signals promoted to standard events (TwinEventButtonSignal*). Removed obsolete scroll signal typedefs and button-specific fields.
1 parent a5d0b01 commit e239453

File tree

14 files changed

+238
-151
lines changed

14 files changed

+238
-151
lines changed

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ __pycache__/
2929
*.pyc
3030

3131
# Configuration
32-
.config
33-
.config.old
32+
.config*
3433
config.h
3534

3635
# CI pipeline

apps/animation.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,11 @@ static twin_time_t _apps_animation_timeout(twin_time_t now, void *closure)
5656
}
5757

5858
static twin_dispatch_result_t _apps_animation_dispatch(twin_widget_t *widget,
59-
twin_event_t *event)
59+
twin_event_t *event,
60+
void *closure)
6061
{
62+
(void) closure; /* unused parameter */
63+
6164
twin_custom_widget_t *custom = twin_widget_get_custom(widget);
6265
if (!custom)
6366
return TwinDispatchContinue;

apps/calc.c

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,11 @@ static void _apps_calc_digit(apps_calc_t *calc, int digit)
129129
_apps_calc_update_value(calc);
130130
}
131131

132-
static void _apps_calc_button_signal(twin_button_t *button,
133-
twin_button_signal_t signal,
134-
void *closure)
132+
static void _apps_calc_button_clicked(twin_button_t *button, void *data)
135133
{
136-
apps_calc_t *calc = closure;
134+
apps_calc_t *calc = data;
137135
int a, b;
138136

139-
if (signal != TwinButtonSignalDown)
140-
return;
141137
int i = _apps_calc_button_to_id(calc, button);
142138
if (i < 0)
143139
return;
@@ -220,8 +216,8 @@ void apps_calc_start(twin_screen_t *screen,
220216
APPS_CALC_BUTTON_SIZE, APPS_CALC_BUTTON_STYLE);
221217
twin_widget_set(&calc->buttons[b]->label.widget,
222218
APPS_CALC_BUTTON_BG);
223-
calc->buttons[b]->signal = _apps_calc_button_signal;
224-
calc->buttons[b]->closure = calc;
219+
twin_button_on_clicked(calc->buttons[b], _apps_calc_button_clicked,
220+
calc);
225221
calc->buttons[b]->label.widget.shape = TwinShapeEllipse;
226222
if (i || j)
227223
calc->buttons[b]->label.widget.copy_geom =

apps/clock.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,11 @@ static twin_time_t _apps_clock_timeout(twin_time_t now, void *closure)
204204
}
205205

206206
static twin_dispatch_result_t _apps_clock_dispatch(twin_widget_t *widget,
207-
twin_event_t *event)
207+
twin_event_t *event,
208+
void *closure)
208209
{
210+
(void) closure; /* unused parameter */
211+
209212
twin_custom_widget_t *custom = twin_widget_get_custom(widget);
210213
if (!custom)
211214
return TwinDispatchContinue;

apps/image.c

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,11 @@ static void _apps_image_paint(twin_custom_widget_t *custom)
5454
}
5555

5656
static twin_dispatch_result_t _apps_image_dispatch(twin_widget_t *widget,
57-
twin_event_t *event)
57+
twin_event_t *event,
58+
void *closure)
5859
{
60+
(void) closure; /* unused parameter */
61+
5962
twin_custom_widget_t *custom = twin_widget_get_custom(widget);
6063
if (!custom)
6164
return TwinDispatchContinue;
@@ -70,15 +73,11 @@ static twin_dispatch_result_t _apps_image_dispatch(twin_widget_t *widget,
7073
return TwinDispatchContinue;
7174
}
7275

73-
static void _apps_image_button_signal(twin_button_t *button,
74-
twin_button_signal_t signal,
75-
void *closure)
76+
static void _apps_image_button_clicked(twin_button_t *button, void *data)
7677
{
7778
(void) button; /* unused parameter */
78-
if (signal != TwinButtonSignalDown)
79-
return;
8079

81-
twin_custom_widget_t *custom = closure;
80+
twin_custom_widget_t *custom = data;
8281
apps_image_data_t *img =
8382
(apps_image_data_t *) twin_custom_widget_data(custom);
8483
const int n = sizeof(tvg_files) / sizeof(tvg_files[0]);
@@ -113,8 +112,7 @@ static twin_custom_widget_t *_apps_image_init(twin_box_t *parent)
113112
twin_button_create(parent, "Next Image", 0xFF482722, D(10),
114113
TwinStyleBold | TwinStyleOblique);
115114
twin_widget_set(&button->label.widget, 0xFFFEE4CE);
116-
button->signal = _apps_image_button_signal;
117-
button->closure = custom;
115+
twin_button_on_clicked(button, _apps_image_button_clicked, custom);
118116
button->label.widget.shape = TwinShapeRectangle;
119117

120118
return custom;

apps/line.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,11 @@ static int _apps_line_hit(apps_line_data_t *line,
7171
}
7272

7373
static twin_dispatch_result_t _apps_line_dispatch(twin_widget_t *widget,
74-
twin_event_t *event)
74+
twin_event_t *event,
75+
void *closure)
7576
{
77+
(void) closure; /* unused parameter */
78+
7679
twin_custom_widget_t *custom = twin_widget_get_custom(widget);
7780
if (!custom)
7881
return TwinDispatchContinue;

apps/spline.c

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -113,15 +113,11 @@ static void _apps_spline_paint(twin_custom_widget_t *custom)
113113
twin_path_destroy(path);
114114
}
115115

116-
static void _apps_spline_button_signal(twin_button_t *button,
117-
twin_button_signal_t signal,
118-
void *closure)
116+
static void _apps_spline_button_clicked(twin_button_t *button, void *data)
119117
{
120118
(void) button; /* unused parameter */
121-
if (signal != TwinButtonSignalDown)
122-
return;
123119

124-
twin_custom_widget_t *custom = closure;
120+
twin_custom_widget_t *custom = data;
125121
apps_spline_data_t *spline =
126122
(apps_spline_data_t *) twin_custom_widget_data(custom);
127123
spline->n_points = (spline->n_points == 3) ? 4 : 3;
@@ -167,8 +163,11 @@ static int _apps_spline_hit(apps_spline_data_t *spline,
167163
}
168164

169165
static twin_dispatch_result_t _apps_spline_dispatch(twin_widget_t *widget,
170-
twin_event_t *event)
166+
twin_event_t *event,
167+
void *closure)
171168
{
169+
(void) closure; /* unused parameter */
170+
172171
twin_custom_widget_t *custom = twin_widget_get_custom(widget);
173172
if (!custom)
174173
return TwinDispatchContinue;
@@ -227,8 +226,7 @@ static twin_custom_widget_t *_apps_spline_init(twin_box_t *parent, int n_points)
227226
twin_button_create(parent, "Switch curve", 0xffae0000, D(10),
228227
TwinStyleBold | TwinStyleOblique);
229228
twin_widget_set(&button->label.widget, 0xc0808080);
230-
button->signal = _apps_spline_button_signal;
231-
button->closure = custom;
229+
twin_button_on_clicked(button, _apps_spline_button_clicked, custom);
232230
button->label.widget.shape = TwinShapeRectangle;
233231

234232
return custom;

include/twin.h

Lines changed: 100 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ typedef struct _twin_screen twin_screen_t;
116116
typedef struct _twin_pixmap twin_pixmap_t;
117117
typedef struct _twin_animation twin_animation_t;
118118

119+
/** Button signal types (used in unified event system) */
120+
typedef enum _twin_button_signal {
121+
TwinButtonSignalDown /**< Sent when button pressed */,
122+
TwinButtonSignalUp /**< Sent when button released inside widget */
123+
} twin_button_signal_t;
124+
119125
/**
120126
* Event type enumeration for input and system events
121127
*
@@ -147,7 +153,11 @@ typedef enum _twin_event_kind {
147153
TwinEventPaint = 0x1001 /**< Widget needs painting */,
148154
TwinEventQueryGeometry = 0x1002 /**< Widget geometry query */,
149155
TwinEventConfigure = 0x1003 /**< Widget configuration change */,
150-
TwinEventDestroy = 0x1004 /**< Widget destruction */
156+
TwinEventDestroy = 0x1004 /**< Widget destruction */,
157+
158+
/* Button signals (unified with event system) */
159+
TwinEventButtonSignalDown = 0x2001 /**< Button pressed signal */,
160+
TwinEventButtonSignalUp = 0x2002 /**< Button released signal */
151161
} twin_event_kind_t;
152162

153163
/**
@@ -177,7 +187,10 @@ typedef struct _twin_event {
177187
struct {
178188
twin_rect_t extents; /**< New widget geometry */
179189
} configure; /**< Widget configuration event data */
180-
} u; /**< Event-specific data union */
190+
struct {
191+
twin_button_signal_t signal; /**< Button signal type */
192+
} button_signal; /**< Button signal event data */
193+
} u; /**< Event-specific data union */
181194
} twin_event_t;
182195

183196
/**
@@ -588,11 +601,27 @@ typedef enum _twin_box_dir {
588601

589602
typedef enum _twin_dispatch_result {
590603
TwinDispatchDone /**< Event processing complete */,
591-
TwinDispatchContinue /**< Continue event propagation */
604+
TwinDispatchContinue /**< Continue event propagation */,
605+
TwinDispatchReject /**< Cannot handle this event */
592606
} twin_dispatch_result_t;
593607

594-
typedef twin_dispatch_result_t (*twin_dispatch_proc_t)(twin_widget_t *widget,
595-
twin_event_t *event);
608+
/**
609+
* Widget event handler function signature
610+
*
611+
* All widget event handlers use this signature with closure support.
612+
* The closure parameter enables stateful event handling without global state.
613+
*
614+
* @widget : Widget receiving the event
615+
* @event : Event to process
616+
* @closure : User data pointer for stateful handling
617+
* @return : Dispatch result (Done, Continue, or Reject)
618+
*
619+
* This type is used for both framework-level handlers and application
620+
* callbacks.
621+
*/
622+
typedef twin_dispatch_result_t (*twin_widget_proc_t)(twin_widget_t *widget,
623+
twin_event_t *event,
624+
void *closure);
596625

597626
typedef struct _twin_widget_layout {
598627
twin_coord_t width, height; /**< Preferred dimensions */
@@ -614,10 +643,15 @@ struct _twin_widget {
614643
twin_widget_t *next; /**< Next sibling widget */
615644
twin_box_t *parent; /**< Parent container */
616645

617-
/* Widget behavior */
618-
twin_dispatch_proc_t dispatch; /**< Event dispatch handler */
619-
twin_rect_t extents; /**< Current geometry */
620-
twin_widget_t *copy_geom; /**< Geometry source widget */
646+
/* Event handling:
647+
* - handler: Framework event handler (processes paint, configure, etc.)
648+
* - callback: Application callback (optional, receives button clicks, etc.)
649+
*/
650+
twin_widget_proc_t handler; /**< Widget event handler (framework) */
651+
twin_widget_proc_t callback; /**< Application callback (optional) */
652+
void *callback_data; /**< Callback user data */
653+
twin_rect_t extents; /**< Current geometry */
654+
twin_widget_t *copy_geom; /**< Geometry source widget */
621655

622656
/* Widget state */
623657
bool paint; /**< Needs painting */
@@ -659,41 +693,24 @@ typedef struct _twin_label {
659693
twin_align_t align; /**< Text alignment */
660694
} twin_label_t;
661695

662-
typedef enum _twin_button_signal {
663-
TwinButtonSignalDown /**< Sent when button pressed */,
664-
TwinButtonSignalUp /**< Sent when button released inside widget */
665-
} twin_button_signal_t;
666-
667696
typedef struct _twin_button twin_button_t;
668697

669-
typedef void (*twin_button_signal_proc_t)(twin_button_t *button,
670-
twin_button_signal_t signal,
671-
void *closure);
698+
/**
699+
* Button click callback signature
700+
* @button : Button that was clicked
701+
* @data : User data provided when callback was registered
702+
*
703+
* Similar to GTK's "clicked" signal.
704+
* Fires when the button is pressed and released.
705+
*/
706+
typedef void (*twin_button_callback_t)(twin_button_t *button, void *data);
672707

673708
struct _twin_button {
674-
twin_label_t label; /**< Base label widget */
675-
bool pressed; /**< Button pressed state */
676-
bool active; /**< Button active state */
677-
twin_button_signal_proc_t signal; /**< Signal callback */
678-
void *closure; /**< Callback closure */
679-
};
680-
681-
typedef enum _twin_scroll_signal {
682-
TwinScrollSignalUpArrow /**< Up arrow clicked */,
683-
TwinScrollSignalDownArrow /**< Down arrow clicked */,
684-
TwinScrollSignalThumb /**< Thumb/slider clicked */,
685-
TwinScrollSignalAboveThumb /**< Area above thumb clicked */,
686-
TwinScrollSignalBelowThumb /**< Area below thumb clicked */
687-
} twin_scroll_signal_t;
688-
689-
typedef struct _twin_scroll twin_scroll_t;
690-
691-
typedef void (*twin_scroll_signal_proc_t)(twin_scroll_t *scroll,
692-
twin_scroll_signal_t signal,
693-
void *closure);
694-
695-
struct _twin_scroll {
696-
twin_widget_t widget; /**< Base widget */
709+
twin_label_t label; /**< Base label widget */
710+
bool pressed; /**< Button pressed state */
711+
bool active; /**< Button active state */
712+
twin_button_callback_t click_callback; /**< Modern click callback */
713+
void *click_data; /**< User data for click callback */
697714
};
698715

699716
typedef struct _twin_context {
@@ -726,6 +743,37 @@ twin_button_t *twin_button_create(twin_box_t *parent,
726743
twin_fixed_t font_size,
727744
twin_style_t font_style);
728745

746+
/**
747+
* Register callback for button clicks
748+
* @button : Button widget
749+
* @callback : Function to call when button is clicked
750+
* @data : User data to pass to callback
751+
*
752+
* The callback is invoked when the button is pressed and released (clicked).
753+
*
754+
* Example:
755+
* void on_button_clicked(twin_button_t *btn, void *data) {
756+
* printf("Button clicked!\n");
757+
* }
758+
* twin_button_on_clicked(my_button, on_button_clicked, my_data);
759+
*/
760+
void twin_button_on_clicked(twin_button_t *button,
761+
twin_button_callback_t callback,
762+
void *data);
763+
764+
/**
765+
* Set application callback for widget events (low-level API)
766+
* @widget : Widget to configure
767+
* @callback : Application callback function
768+
* @data : User data passed to callback
769+
*
770+
* Allows applications to respond to widget events (e.g., button clicks).
771+
* The callback receives events after the widget's core handler processes them.
772+
*/
773+
void twin_widget_set_callback(twin_widget_t *widget,
774+
twin_widget_proc_t callback,
775+
void *data);
776+
729777
/**
730778
* Create default mouse cursor pixmap
731779
* @hx : Output hotspot X coordinate
@@ -1376,14 +1424,14 @@ twin_fixed_t twin_widget_height(twin_widget_t *widget);
13761424
/* Request widget repaint */
13771425
void twin_widget_queue_paint(twin_widget_t *widget);
13781426

1379-
/* Create widget with custom dispatch handler */
1380-
twin_widget_t *twin_widget_create_with_dispatch(twin_box_t *parent,
1381-
twin_argb32_t background,
1382-
twin_coord_t width,
1383-
twin_coord_t height,
1384-
twin_stretch_t hstretch,
1385-
twin_stretch_t vstretch,
1386-
twin_dispatch_proc_t dispatch);
1427+
/* Create widget with custom event handler */
1428+
twin_widget_t *twin_widget_create_with_handler(twin_box_t *parent,
1429+
twin_argb32_t background,
1430+
twin_coord_t width,
1431+
twin_coord_t height,
1432+
twin_stretch_t hstretch,
1433+
twin_stretch_t vstretch,
1434+
twin_widget_proc_t handler);
13871435

13881436
/*
13891437
* Custom widget support - allows creating widgets without accessing internals
@@ -1403,19 +1451,19 @@ typedef struct {
14031451
} twin_custom_widget_t;
14041452

14051453
/**
1406-
* Create a custom widget with user-defined data and dispatch handler.
1454+
* Create a custom widget with user-defined data and event handler.
14071455
*
14081456
* @parent : Parent box widget to contain this widget
14091457
* @background : Background color (ARGB32 format)
14101458
* @width : Preferred width in pixels (0 for flexible)
14111459
* @height : Preferred height in pixels (0 for flexible)
14121460
* @hstretch : Horizontal stretch factor for layout
14131461
* @vstretch : Vertical stretch factor for layout
1414-
* @dispatch : Custom event dispatch function for this widget
1462+
* @handler : Custom event handler function for this widget
14151463
* @data_size : Size of custom data to allocate (0 for no data)
14161464
* @return : Newly created custom widget, or NULL on failure
14171465
*
1418-
* The dispatch function will be called for all events sent to this widget.
1466+
* The handler function will be called for all events sent to this widget.
14191467
* Custom data (if requested) is zero-initialized and accessible via
14201468
* twin_custom_widget_data().
14211469
*/
@@ -1425,7 +1473,7 @@ twin_custom_widget_t *twin_custom_widget_create(twin_box_t *parent,
14251473
twin_coord_t height,
14261474
twin_stretch_t hstretch,
14271475
twin_stretch_t vstretch,
1428-
twin_dispatch_proc_t dispatch,
1476+
twin_widget_proc_t handler,
14291477
size_t data_size);
14301478

14311479
/**

0 commit comments

Comments
 (0)