From 8d94202057bce76e7bf2792683c1fe603340156b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 27 Dec 2018 14:22:05 +0100 Subject: [PATCH] particle/progress-bar: renders a progrss-bar-like thingy --- CMakeLists.txt | 1 + config.c | 39 ++++++++++ particles/progress_bar.c | 150 +++++++++++++++++++++++++++++++++++++++ particles/progress_bar.h | 8 +++ 4 files changed, 198 insertions(+) create mode 100644 particles/progress_bar.c create mode 100644 particles/progress_bar.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f96ceb..71ef8c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ add_executable(f00bar particles/empty.c particles/empty.h particles/list.c particles/list.h particles/map.c particles/map.h + particles/progress_bar.c particles/progress_bar.h particles/ramp.c particles/ramp.h particles/string.c particles/string.h diff --git a/config.c b/config.c index 3325d9e..faad1cd 100644 --- a/config.c +++ b/config.c @@ -16,6 +16,7 @@ #include "particles/empty.h" #include "particles/list.h" #include "particles/map.h" +#include "particles/progress_bar.h" #include "particles/ramp.h" #include "particles/string.h" @@ -324,6 +325,42 @@ particle_ramp_from_config(const struct yml_node *node, const struct font *parent return particle_ramp_new(yml_value_as_string(tag), parts, count); } +static struct particle * +particle_progress_bar_from_config(const struct yml_node *node, + const struct font *parent_font) +{ + const struct yml_node *tag = yml_get_value(node, "tag"); + const struct yml_node *length = yml_get_value(node, "length"); + const struct yml_node *start = yml_get_value(node, "start"); + const struct yml_node *end = yml_get_value(node, "end"); + const struct yml_node *fill = yml_get_value(node, "fill"); + const struct yml_node *empty = yml_get_value(node, "empty"); + 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"); + + assert(tag != NULL && yml_is_scalar(tag)); + assert(length != NULL && yml_is_scalar(length)); + assert(start != NULL); + assert(end != NULL); + assert(fill != NULL); + assert(empty != NULL); + assert(indicator != NULL); + assert(left_margin == NULL || yml_is_scalar(left_margin)); + assert(right_margin == NULL || yml_is_scalar(right_margin)); + + return particle_progress_bar_new( + yml_value_as_string(tag), + yml_value_as_int(length), + particle_from_config(start, parent_font), + particle_from_config(end, parent_font), + particle_from_config(fill, parent_font), + 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); +} + static struct particle * particle_from_config(const struct yml_node *node, const struct font *parent_font) { @@ -344,6 +381,8 @@ particle_from_config(const struct yml_node *node, const struct font *parent_font ret = particle_map_from_config(pair.value, parent_font); else if (strcmp(type, "ramp") == 0) ret = particle_ramp_from_config(pair.value, parent_font); + else if (strcmp(type, "progress_bar") == 0) + ret = particle_progress_bar_from_config(pair.value, parent_font); else assert(false); diff --git a/particles/progress_bar.c b/particles/progress_bar.c new file mode 100644 index 0000000..ecbe3a2 --- /dev/null +++ b/particles/progress_bar.c @@ -0,0 +1,150 @@ +#include "progress_bar.h" + +#include +#include +#include + +struct private { + char *tag; + int width; + + struct particle *start_marker; + struct particle *end_marker; + struct particle *fill; + struct particle *empty; + struct particle *indicator; +}; + +struct exposable_private { + size_t count; + struct exposable **exposables; +}; + +static void +particle_destroy(struct particle *particle) +{ + struct private *p = particle->private; + + p->start_marker->destroy(p->start_marker); + p->end_marker->destroy(p->end_marker); + p->fill->destroy(p->fill); + p->empty->destroy(p->empty); + p->indicator->destroy(p->indicator); + + free(p->tag); + free(p); + particle_default_destroy(particle); +} + +static void +exposable_destroy(struct exposable *exposable) +{ + struct exposable_private *e = exposable->private; + for (size_t i = 0; i < e->count; i++) + e->exposables[i]->destroy(e->exposables[i]); + free(e->exposables); + free(e); + free(exposable); +} + +static int +begin_expose(struct exposable *exposable, cairo_t *cr) +{ + struct exposable_private *e = exposable->private; + + /* Margins */ + exposable->width = exposable->particle->left_margin + + exposable->particle->right_margin; + + /* Sub-exposables */ + for (size_t i = 0; i < e->count; i++) + exposable->width += e->exposables[i]->begin_expose(e->exposables[i], cr); + + return exposable->width; +} + +static void +expose(const struct exposable *exposable, cairo_t *cr, int x, int y, int height) +{ + const struct exposable_private *e = exposable->private; + + const struct deco *deco = exposable->particle->deco; + if (deco != NULL) + deco->expose(deco, cr, x, y, exposable->width, height); + + x += exposable->particle->left_margin; + for (size_t i = 0; i < e->count; i++) { + e->exposables[i]->expose(e->exposables[i], cr, x, y, height); + x += e->exposables[i]->width; + } +} + +static struct exposable * +instantiate(const struct particle *particle, const struct tag_set *tags) +{ + const struct private *p = particle->private; + const struct tag *tag = tag_for_name(tags, p->tag); + assert(tag != NULL); + + long value = tag->as_int(tag); + long min = tag->min(tag); + long max = tag->max(tag); + + long fill_count = max == min ? 0 : p->width * value / (max - min); + long empty_count = p->width - fill_count; + + struct exposable_private *epriv = malloc(sizeof(*epriv)); + epriv->count = ( + 1 + /* Start marker */ + fill_count + /* Before current position */ + 1 + /* Current position indicator */ + empty_count + /* After current position */ + 1); /* End marker */ + + epriv->exposables = malloc(epriv->count * sizeof(epriv->exposables[0])); + + size_t idx = 0; + epriv->exposables[idx++] = p->start_marker->instantiate(p->start_marker, tags); + for (size_t i = 0; i < fill_count; i++) + epriv->exposables[idx++] = p->fill->instantiate(p->fill, tags); + epriv->exposables[idx++] = p->indicator->instantiate(p->indicator, tags); + for (size_t i = 0; i < empty_count; i++) + epriv->exposables[idx++] = p->empty->instantiate(p->empty, tags); + epriv->exposables[idx++] = p->end_marker->instantiate(p->end_marker, tags); + + assert(idx == epriv->count); + + struct exposable *exposable = malloc(sizeof(*exposable)); + exposable->particle = particle; + exposable->private = epriv; + exposable->destroy = &exposable_destroy; + exposable->begin_expose = &begin_expose; + exposable->expose = &expose; + + return exposable; +} + +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) +{ + struct private *priv = malloc(sizeof(*priv)); + priv->tag = strdup(tag); + priv->width = width; + priv->start_marker = start_marker; + priv->end_marker = end_marker; + priv->fill = fill; + priv->empty = empty; + priv->indicator = indicator; + + struct particle *particle = particle_common_new(left_margin, right_margin); + particle->private = priv; + particle->destroy = &particle_destroy; + particle->instantiate = &instantiate; + + return particle; +} diff --git a/particles/progress_bar.h b/particles/progress_bar.h new file mode 100644 index 0000000..5cfa1c4 --- /dev/null +++ b/particles/progress_bar.h @@ -0,0 +1,8 @@ +#pragma once +#include "../particle.h" + +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);