diff --git a/apps/animation.c b/apps/animation.c index c89a350f..592e34fb 100644 --- a/apps/animation.c +++ b/apps/animation.c @@ -6,20 +6,25 @@ #include -#include "twin_private.h" +#include #include "apps_animation.h" -#define _apps_animation_pixmap(animation) ((animation)->widget.window->pixmap) +static inline twin_pixmap_t *_apps_animation_pixmap( + twin_custom_widget_t *animation) +{ + return twin_custom_widget_pixmap(animation); +} typedef struct { - twin_widget_t widget; twin_pixmap_t *pix; twin_timeout_t *timeout; -} apps_animation_t; +} apps_animation_data_t; -static void _apps_animation_paint(apps_animation_t *anim) +static void _apps_animation_paint(twin_custom_widget_t *custom) { + apps_animation_data_t *anim = + (apps_animation_data_t *) twin_custom_widget_data(custom); twin_pixmap_t *current_frame = NULL; if (twin_pixmap_is_animated(anim->pix)) { @@ -34,15 +39,17 @@ static void _apps_animation_paint(apps_animation_t *anim) .source_kind = TWIN_PIXMAP, .u.pixmap = current_frame, }; - twin_composite(_apps_animation_pixmap(anim), 0, 0, &srcop, 0, 0, NULL, 0, 0, - TWIN_SOURCE, current_frame->width, current_frame->height); + twin_composite(_apps_animation_pixmap(custom), 0, 0, &srcop, 0, 0, NULL, 0, + 0, TWIN_SOURCE, current_frame->width, current_frame->height); } -static twin_time_t _apps_animation_timeout(twin_time_t maybe_unused now, - void *closure) +static twin_time_t _apps_animation_timeout(twin_time_t now, void *closure) { - apps_animation_t *anim = closure; - _twin_widget_queue_paint(&anim->widget); + (void) now; /* unused parameter */ + twin_custom_widget_t *custom = closure; + apps_animation_data_t *anim = + (apps_animation_data_t *) twin_custom_widget_data(custom); + twin_custom_widget_queue_paint(custom); twin_animation_t *a = anim->pix->animation; twin_time_t delay = twin_animation_get_current_delay(a); return delay; @@ -51,12 +58,13 @@ static twin_time_t _apps_animation_timeout(twin_time_t maybe_unused now, static twin_dispatch_result_t _apps_animation_dispatch(twin_widget_t *widget, twin_event_t *event) { - apps_animation_t *anim = (apps_animation_t *) widget; - if (_twin_widget_dispatch(widget, event) == TwinDispatchDone) - return TwinDispatchDone; + twin_custom_widget_t *custom = twin_widget_get_custom(widget); + if (!custom) + return TwinDispatchContinue; + switch (event->kind) { case TwinEventPaint: - _apps_animation_paint(anim); + _apps_animation_paint(custom); break; default: break; @@ -64,29 +72,35 @@ static twin_dispatch_result_t _apps_animation_dispatch(twin_widget_t *widget, return TwinDispatchContinue; } -static void _apps_animation_init(apps_animation_t *anim, - twin_box_t *parent, - twin_dispatch_proc_t dispatch) +static twin_custom_widget_t *_apps_animation_init(twin_box_t *parent, + twin_pixmap_t *pix) { - static const twin_widget_layout_t preferred = {0, 0, 1, 1}; - _twin_widget_init(&anim->widget, parent, 0, preferred, dispatch); + twin_custom_widget_t *custom = twin_custom_widget_create( + parent, 0, 0, 0, 1, 1, _apps_animation_dispatch, + sizeof(apps_animation_data_t)); + if (!custom) + return NULL; + + apps_animation_data_t *anim = + (apps_animation_data_t *) twin_custom_widget_data(custom); + anim->pix = pix; if (twin_pixmap_is_animated(anim->pix)) { twin_animation_t *a = anim->pix->animation; twin_time_t delay = twin_animation_get_current_delay(a); - anim->timeout = twin_set_timeout(_apps_animation_timeout, delay, anim); + anim->timeout = + twin_set_timeout(_apps_animation_timeout, delay, custom); } else { anim->timeout = NULL; } + + return custom; } -static apps_animation_t *apps_animation_create(twin_box_t *parent, - twin_pixmap_t *pix) +static twin_custom_widget_t *apps_animation_create(twin_box_t *parent, + twin_pixmap_t *pix) { - apps_animation_t *anim = malloc(sizeof(apps_animation_t)); - anim->pix = pix; - _apps_animation_init(anim, parent, _apps_animation_dispatch); - return anim; + return _apps_animation_init(parent, pix); } void apps_animation_start(twin_screen_t *screen, @@ -99,7 +113,7 @@ void apps_animation_start(twin_screen_t *screen, twin_toplevel_t *toplevel = twin_toplevel_create(screen, TWIN_ARGB32, TwinWindowApplication, x, y, pix->width, pix->height, name); - apps_animation_t *anim = apps_animation_create(&toplevel->box, pix); + twin_custom_widget_t *anim = apps_animation_create(&toplevel->box, pix); (void) anim; twin_toplevel_show(toplevel); } diff --git a/apps/clock.c b/apps/clock.c index ce121b27..3372b20c 100644 --- a/apps/clock.c +++ b/apps/clock.c @@ -9,7 +9,7 @@ #include #include -#include "twin_private.h" +#include #include "apps_clock.h" @@ -30,20 +30,23 @@ #define APPS_CLOCK_BORDER 0xffbababa #define APPS_CLOCK_BORDER_WIDTH D(0.01) -#define _apps_clock_pixmap(clock) ((clock)->widget.window->pixmap) +static inline twin_pixmap_t *_apps_clock_pixmap(twin_custom_widget_t *clock) +{ + return twin_custom_widget_pixmap(clock); +} typedef struct { - twin_widget_t widget; twin_timeout_t *timeout; -} apps_clock_t; +} apps_clock_data_t; -static void apps_clock_set_transform(apps_clock_t *clock, twin_path_t *path) +static void apps_clock_set_transform(twin_custom_widget_t *clock, + twin_path_t *path) { twin_fixed_t scale; scale = (TWIN_FIXED_ONE - APPS_CLOCK_BORDER_WIDTH * 3) / 2; - twin_path_scale(path, _twin_widget_width(clock) * scale, - _twin_widget_height(clock) * scale); + twin_path_scale(path, twin_custom_widget_width(clock) * scale, + twin_custom_widget_height(clock) * scale); twin_path_translate(path, TWIN_FIXED_ONE + APPS_CLOCK_BORDER_WIDTH * 3, TWIN_FIXED_ONE + APPS_CLOCK_BORDER_WIDTH * 3); @@ -51,7 +54,7 @@ static void apps_clock_set_transform(apps_clock_t *clock, twin_path_t *path) twin_path_rotate(path, -TWIN_ANGLE_90); } -static void apps_clock_hand(apps_clock_t *clock, +static void apps_clock_hand(twin_custom_widget_t *clock, twin_angle_t angle, twin_fixed_t len, twin_fixed_t fill_width, @@ -87,7 +90,7 @@ static void apps_clock_hand(apps_clock_t *clock, twin_path_destroy(stroke); } -static void _apps_clock_date(apps_clock_t *clock, struct tm *t) +static void _apps_clock_date(twin_custom_widget_t *clock, struct tm *t) { char text[7]; twin_text_metrics_t metrics; @@ -118,7 +121,7 @@ static twin_angle_t apps_clock_minute_angle(int min) return min * TWIN_ANGLE_360 / 60; } -static void _apps_clock_face(apps_clock_t *clock) +static void _apps_clock_face(twin_custom_widget_t *clock) { twin_path_t *path = twin_path_create(); int m; @@ -173,7 +176,7 @@ static twin_time_t _apps_clock_interval(void) return 1000 - (tv.tv_usec / 1000); } -static void _apps_clock_paint(apps_clock_t *clock) +static void _apps_clock_paint(twin_custom_widget_t *clock) { struct timeval tv; twin_angle_t second_angle, minute_angle, hour_angle; @@ -199,24 +202,24 @@ static void _apps_clock_paint(apps_clock_t *clock) APPS_CLOCK_SECOND, APPS_CLOCK_SECOND_OUT); } -static twin_time_t _apps_clock_timeout(twin_time_t maybe_unused now, - void *closure) +static twin_time_t _apps_clock_timeout(twin_time_t now, void *closure) { - apps_clock_t *clock = closure; - _twin_widget_queue_paint(&clock->widget); + (void) now; /* unused parameter */ + twin_custom_widget_t *clock = closure; + twin_custom_widget_queue_paint(clock); return _apps_clock_interval(); } static twin_dispatch_result_t _apps_clock_dispatch(twin_widget_t *widget, twin_event_t *event) { - apps_clock_t *clock = (apps_clock_t *) widget; + twin_custom_widget_t *custom = twin_widget_get_custom(widget); + if (!custom) + return TwinDispatchContinue; - if (_twin_widget_dispatch(widget, event) == TwinDispatchDone) - return TwinDispatchDone; switch (event->kind) { case TwinEventPaint: - _apps_clock_paint(clock); + _apps_clock_paint(custom); break; default: break; @@ -224,22 +227,24 @@ static twin_dispatch_result_t _apps_clock_dispatch(twin_widget_t *widget, return TwinDispatchContinue; } -static void _apps_clock_init(apps_clock_t *clock, - twin_box_t *parent, - twin_dispatch_proc_t dispatch) +static twin_custom_widget_t *_apps_clock_init(twin_box_t *parent) { - static const twin_widget_layout_t preferred = {0, 0, 1, 1}; - _twin_widget_init(&clock->widget, parent, 0, preferred, dispatch); - clock->timeout = - twin_set_timeout(_apps_clock_timeout, _apps_clock_interval(), clock); + twin_custom_widget_t *custom = twin_custom_widget_create( + parent, 0, 0, 0, 1, 1, _apps_clock_dispatch, sizeof(apps_clock_data_t)); + if (!custom) + return NULL; + + apps_clock_data_t *data = + (apps_clock_data_t *) twin_custom_widget_data(custom); + data->timeout = + twin_set_timeout(_apps_clock_timeout, _apps_clock_interval(), custom); + + return custom; } -static apps_clock_t *apps_clock_create(twin_box_t *parent) +static twin_custom_widget_t *apps_clock_create(twin_box_t *parent) { - apps_clock_t *clock = malloc(sizeof(apps_clock_t)); - - _apps_clock_init(clock, parent, _apps_clock_dispatch); - return clock; + return _apps_clock_init(parent); } void apps_clock_start(twin_screen_t *screen, @@ -251,7 +256,7 @@ void apps_clock_start(twin_screen_t *screen, { twin_toplevel_t *toplevel = twin_toplevel_create( screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, name); - apps_clock_t *clock = apps_clock_create(&toplevel->box); + twin_custom_widget_t *clock = apps_clock_create(&toplevel->box); (void) clock; twin_toplevel_show(toplevel); } diff --git a/apps/image.c b/apps/image.c index 064dc521..b407e340 100644 --- a/apps/image.c +++ b/apps/image.c @@ -6,20 +6,24 @@ #include -#include "twin_private.h" +#include #include "apps_image.h" -#define _apps_image_pixmap(image) ((image)->widget.window->pixmap) +static inline twin_pixmap_t *_apps_image_pixmap(twin_custom_widget_t *image) +{ + return twin_custom_widget_pixmap(image); +} + #define D(x) twin_double_to_fixed(x) #define ASSET_PATH "assets/" #define APP_WIDTH 400 #define APP_HEIGHT 400 + typedef struct { - twin_widget_t widget; twin_pixmap_t **pixes; int image_idx; -} apps_image_t; +} apps_image_data_t; static const char *tvg_files[] = { /* https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/ */ @@ -36,26 +40,29 @@ static const char *tvg_files[] = { ASSET_PATH "flowchart.tvg", }; -static void _apps_image_paint(apps_image_t *img) +static void _apps_image_paint(twin_custom_widget_t *custom) { + apps_image_data_t *img = + (apps_image_data_t *) twin_custom_widget_data(custom); twin_operand_t srcop = { .source_kind = TWIN_PIXMAP, .u.pixmap = img->pixes[img->image_idx], }; - twin_composite(_apps_image_pixmap(img), 0, 0, &srcop, 0, 0, NULL, 0, 0, + twin_composite(_apps_image_pixmap(custom), 0, 0, &srcop, 0, 0, NULL, 0, 0, TWIN_SOURCE, APP_WIDTH, APP_HEIGHT); } static twin_dispatch_result_t _apps_image_dispatch(twin_widget_t *widget, twin_event_t *event) { - apps_image_t *img = (apps_image_t *) widget; - if (_twin_widget_dispatch(widget, event) == TwinDispatchDone) - return TwinDispatchDone; + twin_custom_widget_t *custom = twin_widget_get_custom(widget); + if (!custom) + return TwinDispatchContinue; + switch (event->kind) { case TwinEventPaint: - _apps_image_paint(img); + _apps_image_paint(custom); break; default: break; @@ -63,14 +70,17 @@ static twin_dispatch_result_t _apps_image_dispatch(twin_widget_t *widget, return TwinDispatchContinue; } -static void _apps_image_button_signal(maybe_unused twin_button_t *button, +static void _apps_image_button_signal(twin_button_t *button, twin_button_signal_t signal, void *closure) { + (void) button; /* unused parameter */ if (signal != TwinButtonSignalDown) return; - apps_image_t *img = closure; + twin_custom_widget_t *custom = closure; + apps_image_data_t *img = + (apps_image_data_t *) twin_custom_widget_data(custom); const int n = sizeof(tvg_files) / sizeof(tvg_files[0]); img->image_idx = img->image_idx == n - 1 ? 0 : img->image_idx + 1; if (!img->pixes[img->image_idx]) { @@ -80,35 +90,39 @@ static void _apps_image_button_signal(maybe_unused twin_button_t *button, return; img->pixes[img->image_idx] = pix; } - _twin_widget_queue_paint(&img->widget); + twin_custom_widget_queue_paint(custom); } -static void _apps_image_init(apps_image_t *img, - twin_box_t *parent, - twin_dispatch_proc_t dispatch) +static twin_custom_widget_t *_apps_image_init(twin_box_t *parent) { - static twin_widget_layout_t preferred = {0, 0, 1, 1}; - preferred.height = parent->widget.window->screen->height * 3.0 / 4.0; - _twin_widget_init(&img->widget, parent, 0, preferred, dispatch); + twin_custom_widget_t *custom = twin_custom_widget_create( + parent, 0, 0, parent->widget.window->screen->height * 3 / 4, 1, 1, + _apps_image_dispatch, sizeof(apps_image_data_t)); + if (!custom) + return NULL; + + apps_image_data_t *img = + (apps_image_data_t *) twin_custom_widget_data(custom); img->image_idx = 0; - img->pixes = calloc(sizeof(tvg_files), sizeof(twin_pixmap_t *)); + img->pixes = calloc(sizeof(tvg_files) / sizeof(tvg_files[0]), + sizeof(twin_pixmap_t *)); img->pixes[0] = twin_tvg_to_pixmap_scale(tvg_files[0], TWIN_ARGB32, APP_WIDTH, APP_HEIGHT); + twin_button_t *button = 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 = img; + button->closure = custom; button->label.widget.shape = TwinShapeRectangle; + + return custom; } -static apps_image_t *apps_image_create(twin_box_t *parent) +static twin_custom_widget_t *apps_image_create(twin_box_t *parent) { - apps_image_t *img = malloc(sizeof(apps_image_t)); - - _apps_image_init(img, parent, _apps_image_dispatch); - return img; + return _apps_image_init(parent); } void apps_image_start(twin_screen_t *screen, const char *name, int x, int y) @@ -116,7 +130,7 @@ void apps_image_start(twin_screen_t *screen, const char *name, int x, int y) twin_toplevel_t *toplevel = twin_toplevel_create(screen, TWIN_ARGB32, TwinWindowApplication, x, y, APP_WIDTH, APP_HEIGHT, name); - apps_image_t *img = apps_image_create(&toplevel->box); + twin_custom_widget_t *img = apps_image_create(&toplevel->box); (void) img; twin_toplevel_show(toplevel); } diff --git a/apps/line.c b/apps/line.c index ce9144ad..e0536a0d 100644 --- a/apps/line.c +++ b/apps/line.c @@ -7,50 +7,59 @@ #include #include -#include "twin_private.h" +#include #include "apps_line.h" #define D(x) twin_double_to_fixed(x) -#define _apps_line_pixmap(line) ((line)->widget.window->pixmap) +static inline twin_pixmap_t *_apps_line_pixmap(twin_custom_widget_t *line) +{ + return twin_custom_widget_pixmap(line); +} -typedef struct _apps_line { - twin_widget_t widget; +typedef struct { twin_point_t points[2]; int which; twin_fixed_t line_width; twin_cap_t cap_style; -} apps_line_t; +} apps_line_data_t; -static void _apps_line_paint(apps_line_t *line) +static void _apps_line_paint(twin_custom_widget_t *custom) { + apps_line_data_t *line = + (apps_line_data_t *) twin_custom_widget_data(custom); twin_path_t *path; path = twin_path_create(); twin_path_set_cap_style(path, line->cap_style); twin_path_move(path, line->points[0].x, line->points[0].y); twin_path_draw(path, line->points[1].x, line->points[1].y); - twin_paint_stroke(_apps_line_pixmap(line), 0xff000000, path, + twin_paint_stroke(_apps_line_pixmap(custom), 0xff000000, path, line->line_width); twin_path_set_cap_style(path, TwinCapButt); - twin_paint_stroke(_apps_line_pixmap(line), 0xffff0000, path, + twin_paint_stroke(_apps_line_pixmap(custom), 0xffff0000, path, twin_int_to_fixed(2)); twin_path_destroy(path); } -static twin_dispatch_result_t _apps_line_update_pos(apps_line_t *line, - twin_event_t *event) +static twin_dispatch_result_t _apps_line_update_pos( + twin_custom_widget_t *custom, + twin_event_t *event) { + apps_line_data_t *line = + (apps_line_data_t *) twin_custom_widget_data(custom); if (line->which < 0) return TwinDispatchContinue; line->points[line->which].x = twin_int_to_fixed(event->u.pointer.x); line->points[line->which].y = twin_int_to_fixed(event->u.pointer.y); - _twin_widget_queue_paint(&line->widget); + twin_custom_widget_queue_paint(custom); return TwinDispatchDone; } -static int _apps_line_hit(apps_line_t *line, twin_fixed_t x, twin_fixed_t y) +static int _apps_line_hit(apps_line_data_t *line, + twin_fixed_t x, + twin_fixed_t y) { int i; @@ -64,27 +73,30 @@ static int _apps_line_hit(apps_line_t *line, twin_fixed_t x, twin_fixed_t y) static twin_dispatch_result_t _apps_line_dispatch(twin_widget_t *widget, twin_event_t *event) { - apps_line_t *line = (apps_line_t *) widget; + twin_custom_widget_t *custom = twin_widget_get_custom(widget); + if (!custom) + return TwinDispatchContinue; + + apps_line_data_t *line = + (apps_line_data_t *) twin_custom_widget_data(custom); - if (_twin_widget_dispatch(widget, event) == TwinDispatchDone) - return TwinDispatchDone; switch (event->kind) { case TwinEventPaint: - _apps_line_paint(line); + _apps_line_paint(custom); break; case TwinEventButtonDown: line->which = _apps_line_hit(line, twin_int_to_fixed(event->u.pointer.x), twin_int_to_fixed(event->u.pointer.y)); - return _apps_line_update_pos(line, event); + return _apps_line_update_pos(custom, event); break; case TwinEventMotion: - return _apps_line_update_pos(line, event); + return _apps_line_update_pos(custom, event); break; case TwinEventButtonUp: if (line->which < 0) return TwinDispatchContinue; - _apps_line_update_pos(line, event); + _apps_line_update_pos(custom, event); line->which = -1; return TwinDispatchDone; break; @@ -94,27 +106,30 @@ static twin_dispatch_result_t _apps_line_dispatch(twin_widget_t *widget, return TwinDispatchContinue; } -static void _apps_line_init(apps_line_t *line, - twin_box_t *parent, - twin_dispatch_proc_t dispatch) +static twin_custom_widget_t *_apps_line_init(twin_box_t *parent) { - static const twin_widget_layout_t preferred = {0, 0, 1, 1}; - _twin_widget_init(&line->widget, parent, 0, preferred, dispatch); - twin_widget_set(&line->widget, 0xffffffff); + twin_custom_widget_t *custom = twin_custom_widget_create( + parent, 0xffffffff, 0, 0, 1, 1, _apps_line_dispatch, + sizeof(apps_line_data_t)); + if (!custom) + return NULL; + + apps_line_data_t *line = + (apps_line_data_t *) twin_custom_widget_data(custom); line->line_width = twin_int_to_fixed(30); line->cap_style = TwinCapProjecting; line->points[0].x = twin_int_to_fixed(50); line->points[0].y = twin_int_to_fixed(50); line->points[1].x = twin_int_to_fixed(100); line->points[1].y = twin_int_to_fixed(100); + line->which = -1; + + return custom; } -static apps_line_t *apps_line_create(twin_box_t *parent) +static twin_custom_widget_t *apps_line_create(twin_box_t *parent) { - apps_line_t *line = malloc(sizeof(apps_line_t)); - - _apps_line_init(line, parent, _apps_line_dispatch); - return line; + return _apps_line_init(parent); } void apps_line_start(twin_screen_t *screen, @@ -126,7 +141,7 @@ void apps_line_start(twin_screen_t *screen, { twin_toplevel_t *toplevel = twin_toplevel_create( screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, name); - apps_line_t *line = apps_line_create(&toplevel->box); + twin_custom_widget_t *line = apps_line_create(&toplevel->box); (void) line; twin_toplevel_show(toplevel); } diff --git a/apps/spline.c b/apps/spline.c index 1e8d4c00..fd3a6bd2 100644 --- a/apps/spline.c +++ b/apps/spline.c @@ -7,7 +7,7 @@ #include #include -#include "twin_private.h" +#include #include "apps_spline.h" @@ -16,10 +16,12 @@ #define BACKBONE_WIDTH 2 #define AUX_LINE_WIDTH 2 -#define _apps_spline_pixmap(spline) ((spline)->widget.window->pixmap) +static inline twin_pixmap_t *_apps_spline_pixmap(twin_custom_widget_t *spline) +{ + return twin_custom_widget_pixmap(spline); +} -typedef struct _apps_spline { - twin_widget_t widget; +typedef struct { int n_points; twin_point_t *points; int which; @@ -27,9 +29,9 @@ typedef struct _apps_spline { twin_cap_t cap_style; twin_matrix_t transition; twin_matrix_t inverse_transition; -} apps_spline_t; +} apps_spline_data_t; -static void _init_control_point(apps_spline_t *spline) +static void _init_control_point(apps_spline_data_t *spline) { const int init_point_quad[3][2] = { {100, 100}, @@ -55,19 +57,22 @@ static void _init_control_point(apps_spline_t *spline) } static void _draw_aux_line(twin_path_t *path, - apps_spline_t *spline, + twin_custom_widget_t *custom, + apps_spline_data_t *spline, int idx1, int idx2) { twin_path_move(path, spline->points[idx1].x, spline->points[idx1].y); twin_path_draw(path, spline->points[idx2].x, spline->points[idx2].y); - twin_paint_stroke(_apps_spline_pixmap(spline), 0xc08000c0, path, + twin_paint_stroke(_apps_spline_pixmap(custom), 0xc08000c0, path, twin_int_to_fixed(AUX_LINE_WIDTH)); twin_path_empty(path); } -static void _apps_spline_paint(apps_spline_t *spline) +static void _apps_spline_paint(twin_custom_widget_t *custom) { + apps_spline_data_t *spline = + (apps_spline_data_t *) twin_custom_widget_data(custom); twin_path_t *path; path = twin_path_create(); twin_path_set_cap_style(path, spline->cap_style); @@ -83,69 +88,75 @@ static void _apps_spline_paint(apps_spline_t *spline) spline->points[1].y, spline->points[2].x, spline->points[2].y); } - twin_paint_stroke(_apps_spline_pixmap(spline), 0xff404040, path, + twin_paint_stroke(_apps_spline_pixmap(custom), 0xff404040, path, spline->line_width); twin_path_set_cap_style(path, TwinCapButt); - twin_paint_stroke(_apps_spline_pixmap(spline), 0xffffff00, path, + twin_paint_stroke(_apps_spline_pixmap(custom), 0xffffff00, path, twin_int_to_fixed(BACKBONE_WIDTH)); twin_path_empty(path); if (spline->n_points == 4) { - _draw_aux_line(path, spline, 0, 1); - _draw_aux_line(path, spline, 3, 2); + _draw_aux_line(path, custom, spline, 0, 1); + _draw_aux_line(path, custom, spline, 3, 2); } else if (spline->n_points == 3) { - _draw_aux_line(path, spline, 0, 1); - _draw_aux_line(path, spline, 1, 2); + _draw_aux_line(path, custom, spline, 0, 1); + _draw_aux_line(path, custom, spline, 1, 2); } for (int i = 0; i < spline->n_points; i++) { twin_path_empty(path); twin_path_circle(path, spline->points[i].x, spline->points[i].y, twin_int_to_fixed(CONTROL_POINT_RADIUS)); - twin_paint_path(_apps_spline_pixmap(spline), 0x40004020, path); + twin_paint_path(_apps_spline_pixmap(custom), 0x40004020, path); } twin_path_destroy(path); } -static void _apps_spline_button_signal(maybe_unused twin_button_t *button, +static void _apps_spline_button_signal(twin_button_t *button, twin_button_signal_t signal, void *closure) { + (void) button; /* unused parameter */ if (signal != TwinButtonSignalDown) return; - apps_spline_t *spline = closure; + twin_custom_widget_t *custom = closure; + 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_widget_queue_paint(&spline->widget); + twin_custom_widget_queue_paint(custom); } -static twin_dispatch_result_t _apps_spline_update_pos(apps_spline_t *spline, - twin_event_t *event) +static twin_dispatch_result_t _apps_spline_update_pos( + twin_custom_widget_t *custom, + twin_event_t *event) { + apps_spline_data_t *spline = + (apps_spline_data_t *) twin_custom_widget_data(custom); if (spline->which < 0) return TwinDispatchContinue; twin_fixed_t x = twin_int_to_fixed(event->u.pointer.x); twin_fixed_t y = twin_int_to_fixed(event->u.pointer.y); - spline->points[spline->which].x = twin_sfixed_to_fixed( - _twin_matrix_x(&(spline->inverse_transition), x, y)); - spline->points[spline->which].y = twin_sfixed_to_fixed( - _twin_matrix_y(&(spline->inverse_transition), x, y)); - _twin_widget_queue_paint(&spline->widget); - twin_widget_children_paint((spline->widget).parent); + spline->points[spline->which].x = + twin_matrix_transform_x(&(spline->inverse_transition), x, y); + spline->points[spline->which].y = + twin_matrix_transform_y(&(spline->inverse_transition), x, y); + twin_custom_widget_queue_paint(custom); + twin_widget_children_paint(twin_custom_widget_base(custom)->parent); return TwinDispatchDone; } -static int _apps_spline_hit(apps_spline_t *spline, +static int _apps_spline_hit(apps_spline_data_t *spline, twin_fixed_t x, twin_fixed_t y) { int i; for (i = 0; i < spline->n_points; i++) { - twin_fixed_t px = twin_sfixed_to_fixed(_twin_matrix_x( - &(spline->transition), spline->points[i].x, spline->points[i].y)); - twin_fixed_t py = twin_sfixed_to_fixed(_twin_matrix_y( - &(spline->transition), spline->points[i].x, spline->points[i].y)); + twin_fixed_t px = twin_matrix_transform_x( + &(spline->transition), spline->points[i].x, spline->points[i].y); + twin_fixed_t py = twin_matrix_transform_y( + &(spline->transition), spline->points[i].x, spline->points[i].y); if (twin_fixed_abs(x - px) < twin_int_to_fixed(CONTROL_POINT_RADIUS) && twin_fixed_abs(y - py) < twin_int_to_fixed(CONTROL_POINT_RADIUS)) return i; @@ -156,27 +167,30 @@ static int _apps_spline_hit(apps_spline_t *spline, static twin_dispatch_result_t _apps_spline_dispatch(twin_widget_t *widget, twin_event_t *event) { - apps_spline_t *spline = (apps_spline_t *) widget; + twin_custom_widget_t *custom = twin_widget_get_custom(widget); + if (!custom) + return TwinDispatchContinue; + + apps_spline_data_t *spline = + (apps_spline_data_t *) twin_custom_widget_data(custom); - if (_twin_widget_dispatch(widget, event) == TwinDispatchDone) - return TwinDispatchDone; switch (event->kind) { case TwinEventPaint: - _apps_spline_paint(spline); + _apps_spline_paint(custom); break; case TwinEventButtonDown: spline->which = _apps_spline_hit(spline, twin_int_to_fixed(event->u.pointer.x), twin_int_to_fixed(event->u.pointer.y)); - return _apps_spline_update_pos(spline, event); + return _apps_spline_update_pos(custom, event); break; case TwinEventMotion: - return _apps_spline_update_pos(spline, event); + return _apps_spline_update_pos(custom, event); break; case TwinEventButtonUp: if (spline->which < 0) return TwinDispatchContinue; - _apps_spline_update_pos(spline, event); + _apps_spline_update_pos(custom, event); spline->which = -1; return TwinDispatchDone; break; @@ -186,15 +200,16 @@ static twin_dispatch_result_t _apps_spline_dispatch(twin_widget_t *widget, return TwinDispatchContinue; } -static void _apps_spline_init(apps_spline_t *spline, - twin_box_t *parent, - twin_dispatch_proc_t dispatch, - int n_points) +static twin_custom_widget_t *_apps_spline_init(twin_box_t *parent, int n_points) { - static twin_widget_layout_t preferred = {0, 0, 1, 1}; - preferred.height = parent->widget.window->screen->height * 2 / 3; - _twin_widget_init(&spline->widget, parent, 0, preferred, dispatch); - twin_widget_set(&spline->widget, 0xffffffff); + twin_custom_widget_t *custom = twin_custom_widget_create( + parent, 0xffffffff, 0, parent->widget.window->screen->height * 2 / 3, 1, + 1, _apps_spline_dispatch, sizeof(apps_spline_data_t)); + if (!custom) + return NULL; + + apps_spline_data_t *spline = + (apps_spline_data_t *) twin_custom_widget_data(custom); spline->line_width = twin_int_to_fixed(100); spline->cap_style = TwinCapRound; twin_matrix_identity(&spline->transition); @@ -203,22 +218,24 @@ static void _apps_spline_init(apps_spline_t *spline, twin_matrix_rotate(&spline->inverse_transition, -TWIN_ANGLE_11_25); spline->points = calloc(n_points, sizeof(twin_point_t)); spline->n_points = n_points; + spline->which = -1; _init_control_point(spline); + twin_button_t *button = 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 = spline; + button->closure = custom; button->label.widget.shape = TwinShapeRectangle; + + return custom; } -static apps_spline_t *apps_spline_create(twin_box_t *parent, int n_points) +static twin_custom_widget_t *apps_spline_create(twin_box_t *parent, + int n_points) { - apps_spline_t *spline = malloc(sizeof(apps_spline_t)); - - _apps_spline_init(spline, parent, _apps_spline_dispatch, n_points); - return spline; + return _apps_spline_init(parent, n_points); } void apps_spline_start(twin_screen_t *screen, @@ -230,7 +247,7 @@ void apps_spline_start(twin_screen_t *screen, { twin_toplevel_t *toplevel = twin_toplevel_create( screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h, name); - apps_spline_t *spline = apps_spline_create(&toplevel->box, 4); + twin_custom_widget_t *spline = apps_spline_create(&toplevel->box, 4); (void) spline; twin_toplevel_show(toplevel); } diff --git a/include/twin.h b/include/twin.h index bca6c586..21b2a8a0 100644 --- a/include/twin.h +++ b/include/twin.h @@ -9,6 +9,7 @@ #define _TWIN_H_ #include +#include #include typedef uint8_t twin_a8_t; @@ -809,6 +810,46 @@ void twin_matrix_multiply(twin_matrix_t *result, const twin_matrix_t *a, const twin_matrix_t *b); +/** + * Transform coordinates using a transformation matrix. + * + * These functions apply a 2D transformation matrix to convert coordinates + * from one coordinate system to another. Commonly used for rotations, + * scaling, and translations in graphics operations. + */ + +/** + * Transform the X coordinate using the given matrix. + * + * @param m Transformation matrix to apply + * @param x Input X coordinate in fixed-point format + * @param y Input Y coordinate in fixed-point format + * @return Transformed X coordinate in fixed-point format + * + * Applies the matrix transformation to compute the new X coordinate. + * Both input coordinates are required as 2D transformations can + * affect both X and Y components. + */ +twin_fixed_t twin_matrix_transform_x(const twin_matrix_t *m, + twin_fixed_t x, + twin_fixed_t y); + +/** + * Transform the Y coordinate using the given matrix. + * + * @param m Transformation matrix to apply + * @param x Input X coordinate in fixed-point format + * @param y Input Y coordinate in fixed-point format + * @return Transformed Y coordinate in fixed-point format + * + * Applies the matrix transformation to compute the new Y coordinate. + * Both input coordinates are required as 2D transformations can + * affect both X and Y components. + */ +twin_fixed_t twin_matrix_transform_y(const twin_matrix_t *m, + twin_fixed_t x, + twin_fixed_t y); + /* * path.c */ @@ -1178,6 +1219,134 @@ twin_widget_t *twin_widget_create(twin_box_t *parent, void twin_widget_set(twin_widget_t *widget, twin_argb32_t background); +/* Get widget dimensions */ +twin_fixed_t twin_widget_width(twin_widget_t *widget); +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); + +/* + * Custom widget support - allows creating widgets without accessing internals + * + * This API provides a clean abstraction for creating custom widgets that + * can store private data and implement custom behavior without requiring + * access to twin_private.h or internal widget structures. + */ + +/** + * Structure representing a custom widget with user data. + * Encapsulates a base widget and allows attaching custom data. + */ +typedef struct { + twin_widget_t *widget; /* Base widget providing standard functionality */ + void *data; /* User-defined data specific to this widget */ +} twin_custom_widget_t; + +/** + * Create a custom widget with user-defined data and dispatch handler. + * + * @param parent Parent box widget to contain this widget + * @param background Background color (ARGB32 format) + * @param width Preferred width in pixels (0 for flexible) + * @param height Preferred height in pixels (0 for flexible) + * @param hstretch Horizontal stretch factor for layout + * @param vstretch Vertical stretch factor for layout + * @param dispatch Custom event dispatch function for this widget + * @param 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. + * Custom data (if requested) is zero-initialized and accessible via + * twin_custom_widget_data(). + */ +twin_custom_widget_t *twin_custom_widget_create(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, + size_t data_size); + +/** + * Destroy a custom widget and free associated resources. + * + * @param custom Custom widget to destroy + * + * Frees both the custom widget structure and any associated user data. + * Note: The base widget destruction should be handled by the parent container. + */ +void twin_custom_widget_destroy(twin_custom_widget_t *custom); + +/** + * Get the drawing pixmap from a widget for rendering operations. + * + * @param widget Widget to get pixmap from + * @return Widget's pixmap for drawing, or NULL if invalid + */ +twin_pixmap_t *twin_widget_pixmap(twin_widget_t *widget); + +/** + * Retrieve the custom widget wrapper from a base widget. + * + * @param widget Base widget to look up + * @return Associated custom widget, or NULL if not found + * + * This function allows event handlers to retrieve their custom widget + * context when receiving events through the base widget dispatch system. + */ +twin_custom_widget_t *twin_widget_get_custom(twin_widget_t *widget); + +/** + * Get the user data pointer from a custom widget. + * + * @param custom Custom widget to get data from + * @return Pointer to user data, or NULL if no data allocated + * + * Returns the user data allocated during twin_custom_widget_create(). + * The returned pointer should be cast to the appropriate data structure type. + */ +void *twin_custom_widget_data(twin_custom_widget_t *custom); + +/** + * Get the base widget from a custom widget. + * + * @param custom Custom widget to get base widget from + * @return Base widget, or NULL if invalid + * + * Provides access to the underlying widget for operations that require + * the base widget interface. + */ +twin_widget_t *twin_custom_widget_base(twin_custom_widget_t *custom); + +/* + * Convenience functions for common custom widget operations. + * These functions provide simplified access to frequently needed operations + * without requiring direct manipulation of the base widget. + */ + +/** Get the current width of a custom widget */ +twin_fixed_t twin_custom_widget_width(twin_custom_widget_t *custom); + +/** Get the current height of a custom widget */ +twin_fixed_t twin_custom_widget_height(twin_custom_widget_t *custom); + +/** Request that a custom widget be repainted */ +void twin_custom_widget_queue_paint(twin_custom_widget_t *custom); + +/** Get the drawing pixmap for a custom widget */ +twin_pixmap_t *twin_custom_widget_pixmap(twin_custom_widget_t *custom); + /* * window.c */ diff --git a/src/matrix.c b/src/matrix.c index bf92579f..87e6a994 100644 --- a/src/matrix.c +++ b/src/matrix.c @@ -165,3 +165,17 @@ twin_sfixed_t _twin_matrix_len(twin_matrix_t *m, twin_fixed_t ds = (twin_fixed_mul(xs, xs) + twin_fixed_mul(ys, ys)); return (twin_fixed_to_sfixed(twin_fixed_sqrt(ds))); } + +twin_fixed_t twin_matrix_transform_x(const twin_matrix_t *m, + twin_fixed_t x, + twin_fixed_t y) +{ + return twin_sfixed_to_fixed(_twin_matrix_x((twin_matrix_t *) m, x, y)); +} + +twin_fixed_t twin_matrix_transform_y(const twin_matrix_t *m, + twin_fixed_t x, + twin_fixed_t y) +{ + return twin_sfixed_to_fixed(_twin_matrix_y((twin_matrix_t *) m, x, y)); +} diff --git a/src/widget.c b/src/widget.c index 9226b36e..a6f7cf9e 100644 --- a/src/widget.c +++ b/src/widget.c @@ -238,3 +238,207 @@ void twin_widget_set(twin_widget_t *widget, twin_argb32_t background) widget->background = background; _twin_widget_queue_paint(widget); } + +twin_fixed_t twin_widget_width(twin_widget_t *widget) +{ + return _twin_widget_width(widget); +} + +twin_fixed_t twin_widget_height(twin_widget_t *widget) +{ + return _twin_widget_height(widget); +} + +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 *widget = malloc(sizeof(twin_widget_t)); + if (!widget) + return NULL; + + twin_widget_layout_t preferred = { + .width = width, + .height = height, + .stretch_width = stretch_width, + .stretch_height = stretch_height, + }; + _twin_widget_init(widget, parent, 0, preferred, dispatch); + widget->background = background; + return widget; +} + +/* Map to store custom widget associations */ +typedef struct _custom_widget_map { + twin_widget_t *widget; + twin_custom_widget_t *custom; + twin_dispatch_proc_t user_dispatch; + struct _custom_widget_map *next; +} custom_widget_map_t; + +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) +{ + custom_widget_map_t *entry = malloc(sizeof(custom_widget_map_t)); + if (!entry) + return; + entry->widget = widget; + entry->custom = custom; + entry->user_dispatch = user_dispatch; + entry->next = custom_widget_map; + custom_widget_map = entry; +} + +static twin_custom_widget_t *find_custom_widget(twin_widget_t *widget) +{ + custom_widget_map_t *entry = custom_widget_map; + while (entry) { + if (entry->widget == widget) + return entry->custom; + entry = entry->next; + } + return NULL; +} + +static void unregister_custom_widget(twin_widget_t *widget) +{ + custom_widget_map_t **prev = &custom_widget_map; + custom_widget_map_t *entry = custom_widget_map; + + while (entry) { + if (entry->widget == widget) { + *prev = entry->next; + free(entry); + return; + } + prev = &entry->next; + entry = entry->next; + } +} + +static twin_dispatch_result_t custom_widget_dispatch(twin_widget_t *widget, + twin_event_t *event) +{ + /* First call the base widget dispatch to handle standard widget behavior */ + twin_dispatch_result_t result = _twin_widget_dispatch(widget, event); + if (result == TwinDispatchDone) + return result; + + /* Then call the user's custom dispatch if any */ + custom_widget_map_t *entry = custom_widget_map; + while (entry) { + if (entry->widget == widget) { + if (entry->user_dispatch) + return entry->user_dispatch(widget, event); + break; + } + entry = entry->next; + } + return TwinDispatchContinue; +} + +twin_custom_widget_t *twin_custom_widget_create(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, + size_t data_size) +{ + twin_custom_widget_t *custom = malloc(sizeof(twin_custom_widget_t)); + if (!custom) + return NULL; + + if (data_size > 0) { + custom->data = malloc(data_size); + if (!custom->data) { + free(custom); + return NULL; + } + memset(custom->data, 0, data_size); + } else { + custom->data = NULL; + } + + custom->widget = twin_widget_create_with_dispatch( + 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); + + return custom; +} + +void twin_custom_widget_destroy(twin_custom_widget_t *custom) +{ + if (!custom) + return; + + if (custom->widget) { + unregister_custom_widget(custom->widget); + /* Note: widget destruction should be handled by the parent/container */ + } + free(custom->data); + free(custom); +} + +twin_pixmap_t *twin_widget_pixmap(twin_widget_t *widget) +{ + if (!widget || !widget->window) + return NULL; + return widget->window->pixmap; +} + +twin_custom_widget_t *twin_widget_get_custom(twin_widget_t *widget) +{ + return find_custom_widget(widget); +} + +void *twin_custom_widget_data(twin_custom_widget_t *custom) +{ + return custom ? custom->data : NULL; +} + +twin_widget_t *twin_custom_widget_base(twin_custom_widget_t *custom) +{ + return custom ? custom->widget : NULL; +} + +twin_fixed_t twin_custom_widget_width(twin_custom_widget_t *custom) +{ + return custom ? twin_widget_width(custom->widget) : 0; +} + +twin_fixed_t twin_custom_widget_height(twin_custom_widget_t *custom) +{ + return custom ? twin_widget_height(custom->widget) : 0; +} + +void twin_custom_widget_queue_paint(twin_custom_widget_t *custom) +{ + if (custom && custom->widget) + twin_widget_queue_paint(custom->widget); +} + +twin_pixmap_t *twin_custom_widget_pixmap(twin_custom_widget_t *custom) +{ + return custom ? twin_widget_pixmap(custom->widget) : NULL; +}