From 1f182b862e36dfe0a5023f2aeae54b85a9dbf79d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 29 Dec 2018 17:36:34 +0100 Subject: [PATCH] particle/progress-bar: allow user to configure an on-click handler Since we're typically interrested in *where* (on the progress-bar) the user clicked, we need a way to pass the clicked position to the handler. Normally, the on-click handler is expanded when a particle instantiates its exposable. At this point, we (obviously) don't have the click position. This is solved by expanding the handler a second time, when the bar is clicked. Thus, the user can use the "{where}" tag in the click handler. "where" will be expanded to a percentage value (0-100). --- config.c | 5 +++- particles/progress_bar.c | 58 +++++++++++++++++++++++++++++++++++++--- particles/progress_bar.h | 2 +- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/config.c b/config.c index 7ebe106..bb6be15 100644 --- a/config.c +++ b/config.c @@ -324,6 +324,7 @@ particle_progress_bar_from_config(const struct yml_node *node, const struct yml_node *indicator = yml_get_value(node, "indicator"); const struct yml_node *left_margin = yml_get_value(node, "left_margin"); const struct yml_node *right_margin = yml_get_value(node, "right_margin"); + const struct yml_node *on_click = yml_get_value(node, "on_click"); assert(tag != NULL && yml_is_scalar(tag)); assert(length != NULL && yml_is_scalar(length)); @@ -334,6 +335,7 @@ particle_progress_bar_from_config(const struct yml_node *node, assert(indicator != NULL); assert(left_margin == NULL || yml_is_scalar(left_margin)); assert(right_margin == NULL || yml_is_scalar(right_margin)); + assert(on_click == NULL || yml_is_scalar(on_click)); return particle_progress_bar_new( yml_value_as_string(tag), @@ -344,7 +346,8 @@ particle_progress_bar_from_config(const struct yml_node *node, particle_from_config(empty, parent_font), particle_from_config(indicator, parent_font), left_margin != NULL ? yml_value_as_int(left_margin) : 0, - right_margin != NULL ? yml_value_as_int(right_margin) : 0); + right_margin != NULL ? yml_value_as_int(right_margin) : 0, + on_click != NULL ? yml_value_as_string(on_click) : NULL); } static struct particle * diff --git a/particles/progress_bar.c b/particles/progress_bar.c index 73c0cbc..717b21e 100644 --- a/particles/progress_bar.c +++ b/particles/progress_bar.c @@ -83,6 +83,51 @@ expose(const struct exposable *exposable, cairo_t *cr, int x, int y, int height) } } +static void +on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, + int x, int y) +{ + if (exposable->on_click == NULL) { + exposable_default_on_mouse(exposable, bar, event, x, y); + return; + } + + /* + * Hack-warning! + * + * In order to pass the *clicked* position to the on_click + * handler, we expand the handler *again* (first time would be + * when the particle instantiated us). + * + * We pass a single tag, "where", which is a percentage value. + * + * Keep a reference to the un-expanded string, to be able to reset + * it after executing the handler. + */ + + char *original = exposable->on_click; + + assert(x >= 0 && x < exposable->width); + long where = exposable->width > 0 + ? 100 * x / exposable->width + : 0; + + struct tag_set tags = { + .tags = (struct tag *[]){tag_new_int(NULL, "where", where)}, + .count = 1, + }; + + exposable->on_click = tags_expand_template(exposable->on_click, &tags); + tag_set_destroy(&tags); + + /* Call default implementation, which will execute our handler */ + exposable_default_on_mouse(exposable, bar, event, x, y); + + /* Reset handler string */ + free(exposable->on_click); + exposable->on_click = original; +} + static struct exposable * instantiate(const struct particle *particle, const struct tag_set *tags) { @@ -120,11 +165,16 @@ instantiate(const struct particle *particle, const struct tag_set *tags) assert(idx == epriv->count); - struct exposable *exposable = exposable_common_new(particle, NULL); + char *on_click = tags_expand_template(particle->on_click_template, tags); + + struct exposable *exposable = exposable_common_new(particle, on_click); + free(on_click); + exposable->private = epriv; exposable->destroy = &exposable_destroy; exposable->begin_expose = &begin_expose; exposable->expose = &expose; + exposable->on_mouse = &on_mouse; enum tag_realtime_unit rt = tag->realtime(tag); @@ -164,7 +214,8 @@ particle_progress_bar_new(const char *tag, int width, struct particle *end_marker, struct particle *fill, struct particle *empty, struct particle *indicator, - int left_margin, int right_margin) + int left_margin, int right_margin, + const char *on_click_template) { struct private *priv = malloc(sizeof(*priv)); priv->tag = strdup(tag); @@ -175,7 +226,8 @@ particle_progress_bar_new(const char *tag, int width, priv->empty = empty; priv->indicator = indicator; - struct particle *particle = particle_common_new(left_margin, right_margin, NULL); + struct particle *particle = particle_common_new( + left_margin, right_margin, on_click_template); particle->private = priv; particle->destroy = &particle_destroy; particle->instantiate = &instantiate; diff --git a/particles/progress_bar.h b/particles/progress_bar.h index 5cfa1c4..a9f94a5 100644 --- a/particles/progress_bar.h +++ b/particles/progress_bar.h @@ -5,4 +5,4 @@ struct particle * particle_progress_bar_new( const char *tag, int width, struct particle *start_marker, struct particle *end_marker, struct particle *fill, struct particle *empty, struct particle *indicator, - int left_margin, int right_margin); + int left_margin, int right_margin, const char *on_click_template);