diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cbbab7..3a83d32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,7 +76,7 @@ dmod_add_library(${DMOD_MODULE_NAME} ${DMOD_MODULE_VERSION} ) dmod_link_modules(${DMOD_MODULE_NAME} - dmvsi@>=0.5 + dmvsi@>=0.6 dmvs_js@>=0.1 ) diff --git a/README.md b/README.md index ab3e939..a929ef0 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,16 @@ What's reported and left out: A class changes only the look of its element; what it moves around it stays. +**Talking to the application.** A page is shown by an application on +dmodOS; `dmod` (dmvs_js) is the channel between them - +`dmod.prop()`, `get()`, `set()`, `onchange()`, `emit()`. One thing more is +said in HTML: `data-dmod-width="name"` makes an element as wide as the +integer property `name` says - its laid out width times the property over +`data-dmod-scale` (1000: per mille; `data-dmod-value` its initial value when +no script makes the property). What is in the element is painted at its +whole width and cut at the property's end: a progress bar the application +moves. In a browser the page's own `dmod.js` does it. + ## Resources Style sheets, fonts and images are files next to the page; a URL is read diff --git a/src/layout.c b/src/layout.c index ae1e9d6..bec9604 100644 --- a/src/layout.c +++ b/src/layout.c @@ -701,10 +701,14 @@ static int32_t place_line(conv_t* c, node_t* block, item_t* items, uint32_t from static int32_t layout_inline(conv_t* c, node_t* block, node_t* first, node_t* stop, int32_t x0, int32_t y0, int32_t avail, int32_t cb_h, bool* first_baseline_set) { - item_t* storage = arena_alloc(&c->arena, MAX_ITEMS * sizeof(item_t)); + /* (on the heap, not the arena: a page lays out its inline content many times - flex measures) */ + item_t* storage = Dmod_Malloc(MAX_ITEMS * sizeof(item_t)); items_t it = { c, storage, 0, true }; if (storage == NULL) + { + c->arena.failed = true; return 0; + } /* The children from `first` up to `stop`, as if they were all of a parent */ node_t holder; memset(&holder, 0, sizeof(holder)); @@ -741,6 +745,7 @@ static int32_t layout_inline(conv_t* c, node_t* block, node_t* first, node_t* st it.items[i].node->box.container = block; } } + Dmod_Free(storage); return 0; } @@ -784,6 +789,7 @@ static int32_t layout_inline(conv_t* c, node_t* block, node_t* first, node_t* st if (end == it.count) break; } + Dmod_Free(storage); return y - y0; } @@ -868,10 +874,13 @@ static bool replaced_size(conv_t* c, node_t* n, int32_t* w, int32_t* h) static int32_t intrinsic_items(conv_t* c, node_t* n, bool max_content) { - item_t* storage = arena_alloc(&c->arena, MAX_ITEMS * sizeof(item_t)); + item_t* storage = Dmod_Malloc(MAX_ITEMS * sizeof(item_t)); /* (on the heap: see layout_inline()) */ items_t it = { c, storage, 0, true }; if (storage == NULL) + { + c->arena.failed = true; return 0; + } inline_items(&it, n, 0, AUTO_SIZE, true, max_content, 0); int32_t best = 0, line = 0, word = 0; for (uint32_t i = 0; i < it.count; i++) @@ -899,6 +908,7 @@ static int32_t intrinsic_items(conv_t* c, node_t* n, bool max_content) word = 0; } } + Dmod_Free(storage); return max32(best, max_content ? line : word); } @@ -1087,10 +1097,13 @@ static int32_t layout_flex(conv_t* c, node_t* n, int32_t x0, int32_t y0, int32_t int32_t main_avail = row ? cw : ch; len_t gap_len = row ? st->column_gap : st->row_gap; int32_t gap = (gap_len.kind == LEN_SET) ? len_resolve(gap_len, max32(main_avail, 0)) : 0; - flex_item_t* items = arena_alloc(&c->arena, MAX_FLEX * sizeof(flex_item_t)); + flex_item_t* items = Dmod_Malloc(MAX_FLEX * sizeof(flex_item_t)); /* (on the heap: see layout_inline()) */ uint32_t count = 0; if (items == NULL) + { + c->arena.failed = true; return 0; + } for (node_t* k = n->first; k != NULL; k = k->next) { @@ -1334,6 +1347,7 @@ static int32_t layout_flex(conv_t* c, node_t* n, int32_t x0, int32_t y0, int32_t baseline_set = true; } } + Dmod_Free(items); if (row) return (ch != AUTO_SIZE) ? ch : line_cross; return (ch != AUTO_SIZE) ? ch : used; diff --git a/src/script.c b/src/script.c index 72b419d..1c9cd0d 100644 --- a/src/script.c +++ b/src/script.c @@ -1125,7 +1125,7 @@ static bool has_class(const node_t* n, const char* name) } /* The states laid out, kept for painting their looks (c->states) */ -#define MAX_STATES 48u +#define MAX_STATES 96u typedef struct { @@ -1831,6 +1831,86 @@ static void texts(script_t* sc, node_t* n, uint32_t depth) } } +/* + * data-dmod-width="name": the element's width follows the property `name` + * of the view (dmod.prop(), or made here) - the width it is laid out at + * times the property over data-dmod-scale (1000: per mille). What is in it + * is painted at its whole width and cut where the property puts its end + * (a progress bar). The application that shows the view sets the property. + */ + +/* The integer property `name`: one the scripts made, else a new one at `initial` */ +static dmvsi_var_t prop_var(conv_t* c, const char* name, int32_t initial) +{ + dmvsi_var_info_t info; + for (dmvsi_var_t v = 1; dmvsi_var_info(c->doc, v, &info) == 0; v++) + { + const char* prop = dmvsi_var_prop(c->doc, v); + if (prop != NULL && strcmp(prop, name) == 0) + return (info.kind == DMVSI_VAR_INT) ? v : 0; + } + dmvsi_var_t v = dmvsi_add_var(c->doc, name, initial); + if (v != 0 && dmvsi_set_prop(c->doc, v, name) != 0) + return 0; + return v; +} + +static int32_t attr_number(const node_t* e, const char* name, int32_t otherwise) +{ + const char* a = node_attr(e, name); + if (a == NULL || *a == '\0') + return otherwise; + int32_t v = 0; + bool negative = *a == '-'; + for (a += negative ? 1 : 0; *a >= '0' && *a <= '9'; a++) + v = v * 10 + (*a - '0'); + return negative ? -v : v; +} + +static int bind_widths(conv_t* c, node_t* n, uint32_t depth) +{ + for (node_t* e = n->first; e != NULL && depth < 200U; e = e->next) + { + if (e->kind != NODE_ELEMENT) + continue; + const char* name = node_attr(e, "data-dmod-width"); + if (name != NULL && *name != '\0' && e->box.placed) + { + int32_t scale = attr_number(e, "data-dmod-scale", 1000); + int32_t w = e->box.w / DMVSI_UNIT; + dmvsi_var_t prop = prop_var(c, name, attr_number(e, "data-dmod-value", 0)); + dmvsi_var_info_t info; + if (prop == 0 || scale <= 0 || dmvsi_var_info(c->doc, prop, &info) != 0) + { + WARN(c, "data-dmod-width of #%s: \"%s\" is no integer property - not converted\n", label_of(e), name); + continue; + } + if (e->dynamic == NULL && (e->dynamic = arena_alloc(&c->arena, sizeof(dynamic_t))) == NULL) + return -ENOMEM; + char label[48]; + Dmod_SnPrintf(label, sizeof(label), "%s_w", label_of(e)); + int64_t now = (int64_t)info.initial * w / scale; + dmvsi_var_t px = dmvsi_add_var(c->doc, label, (int32_t)((now < 0) ? 0 : (now > w) ? w : now)); + /* On a change: px = clamp(prop, 0, scale) * w / scale */ + dmvsi_action_t a[5]; + memset(a, 0, sizeof(a)); + a[0].kind = DMVSI_ACT_SET; a[0].var = px; a[0].operand = prop; + a[1].kind = DMVSI_ACT_MAX; a[1].var = px; a[1].value = 0; + a[2].kind = DMVSI_ACT_MIN; a[2].var = px; a[2].value = scale; + a[3].kind = DMVSI_ACT_MUL; a[3].var = px; a[3].value = w; + a[4].kind = DMVSI_ACT_DIV; a[4].var = px; a[4].value = scale; + dmvsi_handler_t h = (px != 0) ? dmvsi_add_handler(c->doc, a, 5) : 0; + if (h == 0 || dmvsi_on_change(c->doc, prop, h) != 0) + return -ENOMEM; + e->dynamic->bind[DMVSI_BIND_W] = px; + } + int ret = bind_widths(c, e, depth + 1U); + if (ret != 0) + return ret; + } + return 0; +} + int script_compile(conv_t* c) { script_t* sc = Dmod_Malloc(sizeof(*sc)); @@ -1847,7 +1927,10 @@ int script_compile(conv_t* c) find_scripts(sc, c->document, programs, 0); int status = 0; if (programs->count == 0) + { + status = bind_widths(c, c->document, 0); /* (no scripts: only what the application sets) */ goto done; + } dmvs_js_host_t host; memset(&host, 0, sizeof(host)); @@ -1891,6 +1974,8 @@ int script_compile(conv_t* c) status = expand_held(sc); if (status == 0) status = lay_out_changes(sc); + if (status == 0) + status = bind_widths(c, c->document, 0); if (status == 0) status = make_src_variants(sc); if (status == 0) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ecc20bd..1511c6e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,7 +10,7 @@ dmod_add_test(${DMOD_MODULE_NAME} ${DMOD_MODULE_VERSION} # dmvs_js: dmvs_html needs it loaded to convert a page's scripts dmod_link_modules(${DMOD_MODULE_NAME} - dmvsi@>=0.5 + dmvsi@>=0.6 dmvs_js@>=0.1 )