Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions src/paint.c
Original file line number Diff line number Diff line change
Expand Up @@ -498,11 +498,25 @@ static void paint_decoration(painter_t* p, const style_t* st, dmvsi_rect_t r, co
}

/* The element (n or inside it) whose text a script sets that a text is of - NULL: none */
/*
* The text a script sets in an element: its own variable, or - the element
* as it looks in another state (a class a script changes) - the variable of
* the element it is a look of: what the script set shows in every look.
*/
static const dynamic_t* text_dynamic(const node_t* e)
{
if (e->dynamic != NULL && e->dynamic->text != 0)
return e->dynamic;
if (e->like != NULL && e->like->dynamic != NULL && e->like->dynamic->text != 0)
return e->like->dynamic;
return NULL;
}

static const node_t* text_set(const node_t* text, const node_t* n)
{
for (const node_t* e = text; e != NULL; e = e->parent)
{
if (e->kind == NODE_ELEMENT && e->dynamic != NULL && e->dynamic->text != 0)
if (e->kind == NODE_ELEMENT && text_dynamic(e) != NULL)
return e;
if (e == n)
break;
Expand All @@ -521,15 +535,16 @@ static void paint_text_var(painter_t* p, const node_t* block, const node_t* e, c
const style_t* st = e->style;
if (st == NULL || st->hidden || (st->color >> 24) == 0)
return;
const dynamic_t* d = text_dynamic(e);
dmvsi_var_info_t info;
if (dmvsi_var_info(p->c->doc, e->dynamic->text, &info) != 0)
if (d == NULL || dmvsi_var_info(p->c->doc, d->text, &info) != 0)
return;
dmvsi_text_t t;
memset(&t, 0, sizeof(t));
t.var = e->dynamic->text;
t.var = d->text;
t.text = info.text;
t.length = strlen(info.text);
t.chars = e->dynamic->chars;
t.chars = d->chars;
t.font = (f != NULL) ? f->font : style_font(p->c, (style_t*)st);
t.paint.color = p->shadow ? times_alpha(p->shadow_color, st->color >> 24) : st->color;
if (t.font == NULL)
Expand Down Expand Up @@ -804,7 +819,7 @@ static void paint_inner(painter_t* p, node_t* n, uint32_t depth)
for (; layers != NULL && i < layers->count && layers->items[i].z < 0; i++)
paint_context(p, layers->items[i].node, depth + 1U);
paint_lines(p, n);
if (n->dynamic != NULL && n->dynamic->text != 0 && n->box.frags == NULL)
if (text_dynamic(n) != NULL && n->box.frags == NULL)
paint_text_var(p, n, n, NULL); /* A script's text in what has none yet */
paint_flow(p, n, depth + 1U);
for (; layers != NULL && i < layers->count; i++)
Expand Down Expand Up @@ -899,7 +914,7 @@ static void paint_flow_element(painter_t* p, node_t* k, uint32_t depth)
else if (node_is(k, "svg"))
paint_svg(p, k);
paint_lines(p, k);
if (k->dynamic != NULL && k->dynamic->text != 0 && k->box.frags == NULL && k->style->display != DISPLAY_INLINE)
if (text_dynamic(k) != NULL && k->box.frags == NULL && k->style->display != DISPLAY_INLINE)
paint_text_var(p, k, k, NULL); /* A script's text in what has none yet */
}
paint_flow(p, k, depth + 1U);
Expand Down
8 changes: 8 additions & 0 deletions src/private.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ typedef struct
bool placed; /* Its absolute position is known */
} box_t;

/* style_t::visibility: what the element's own style says (inherited: its parent's hidden) */
#define VISIBILITY_INHERITED 0u
#define VISIBILITY_VISIBLE 1u
#define VISIBILITY_HIDDEN 2u

/* One look of an element: the element as laid out in a state of the page, shown on its conditions */
#define MAX_VARIANTS 8u

Expand Down Expand Up @@ -131,6 +136,8 @@ struct node
uint8_t kind;
uint32_t index; /* Of the elements, in document order (as parsed) */
dynamic_t* dynamic; /* NULL: what it is it stays */
const struct node* like; /* A look of an element (its tree in a state): that element -
the text a script sets in it is the element's */
bool active; /* Pressed: :active holds (MOD_ACTIVE) */
uint8_t pseudo;
char* tag; /* Lowercase; NULL for text */
Expand Down Expand Up @@ -361,6 +368,7 @@ struct style
uint8_t overflow_x, overflow_y;
bool border_box; /* box-sizing */
bool hidden; /* visibility */
uint8_t visibility; /* ... as the element's own style gives it: VISIBILITY_* */
len_t inset[4]; /* top, right, bottom, left */
int32_t z_index;
bool z_auto;
Expand Down
66 changes: 52 additions & 14 deletions src/script.c
Original file line number Diff line number Diff line change
Expand Up @@ -1228,10 +1228,16 @@ static uint32_t look_of(const node_t* e)
return inside(e, e, h, 0);
}

/* How opaque an element is seen: not at all while it is hidden (visibility) */
static int32_t seen_opacity(const style_t* st)
/*
* How opaque an element makes itself: hidden by its own visibility, not by
* a box around it - what a change of its classes can change. A row of a
* list on a screen hidden as the page is (its screen shows it later) is
* shown or hidden by its own class all the same.
*/
static int32_t own_opacity(const style_t* st)
{
return st->hidden ? 0 : st->opacity;
bool hidden = (st->visibility == VISIBILITY_INHERITED) ? false : st->visibility == VISIBILITY_HIDDEN;
return hidden ? 0 : st->opacity;
}

/* "+a -b": whether the element has all that already */
Expand Down Expand Up @@ -1279,6 +1285,8 @@ static void seen_like(node_t* look, const node_t* e, uint32_t depth)
return;
if (look->style != NULL && e->style != NULL && !e->style->hidden)
look->style->hidden = false;
if (look != e)
look->like = e;
const node_t* g = e->first;
for (node_t* k = look->first; k != NULL; k = k->next)
{
Expand All @@ -1293,7 +1301,9 @@ static void seen_like(node_t* look, const node_t* e, uint32_t depth)
}

/* An element a change shows that is hidden (visibility) as the page is: shown, its opacity
* what hides it - so that it can fade in. Its inside hidden as it is then */
* what hides it - so that it can fade in. Its inside hidden as it is then: by its own
* visibility, or inheriting it - not by the boxes around the element (a screen hidden as
* the page is shows it later) */
static void reveal(node_t* e, const node_t* f, uint32_t depth)
{
if (e->style != NULL && f->style != NULL && e->kind == NODE_ELEMENT && depth == 0)
Expand All @@ -1305,7 +1315,9 @@ static void reveal(node_t* e, const node_t* f, uint32_t depth)
break;
if (k->kind == NODE_ELEMENT && k->style != NULL && g->style != NULL)
{
if (k->style->hidden && !g->style->hidden)
bool hidden = (g->style->visibility == VISIBILITY_INHERITED) ? e->style->hidden
: g->style->visibility == VISIBILITY_HIDDEN;
if (k->style->hidden && !hidden)
k->style->hidden = false;
reveal(k, g, depth + 1U);
}
Expand All @@ -1319,7 +1331,7 @@ static int lay_out_change(script_t* sc, change_t* ch)
conv_t* c = sc->c;
node_t* e = ch->element;
ch->laid_out = true;
ch->opacity = seen_opacity(e->style);
ch->opacity = own_opacity(e->style);
const style_t* after = e->style; /* The style it has then: its transition */
if ((ch->mod.kind == MOD_CLASS_ADD && has_class(e, ch->mod.name)) ||
(ch->mod.kind == MOD_CLASS_REMOVE && !has_class(e, ch->mod.name)) ||
Expand All @@ -1346,9 +1358,10 @@ static int lay_out_change(script_t* sc, change_t* ch)
view_origin(s, &sx, &sy);
ch->dx = (f->box.ax - sx) - (e->box.ax - ox);
ch->dy = (f->box.ay - sy) - (e->box.ay - oy);
ch->opacity = seen_opacity(f->style);
if (e->style->hidden && !f->style->hidden)
ch->reveals = f;
ch->opacity = own_opacity(f->style);
if (e->style->hidden && (!f->style->hidden || (e->style->visibility == VISIBILITY_HIDDEN &&
f->style->visibility != VISIBILITY_HIDDEN)))
ch->reveals = f; /* (hidden by itself, or hidden no more) */
if (look_of(f) != look_of(e) || !e->box.placed)
{
ch->changed = true; /* Another look */
Expand Down Expand Up @@ -1428,7 +1441,7 @@ static int bind_element(script_t* sc, node_t* e)
continue;
moves_x = moves_x || ch->dx != 0;
moves_y = moves_y || ch->dy != 0;
fades = fades || ch->opacity != seen_opacity(e->style);
fades = fades || ch->opacity != own_opacity(e->style);
}
if (!moves_x && !moves_y && !fades)
return 0;
Expand All @@ -1452,7 +1465,7 @@ static int bind_element(script_t* sc, node_t* e)
if (fades)
{
Dmod_SnPrintf(name, sizeof(name), "%s_alpha", base);
e->dynamic->bind[DMVSI_BIND_OPACITY] = dmvsi_add_var(sc->c->doc, name, seen_opacity(e->style));
e->dynamic->bind[DMVSI_BIND_OPACITY] = dmvsi_add_var(sc->c->doc, name, own_opacity(e->style));
}
return 0;
}
Expand Down Expand Up @@ -1503,6 +1516,28 @@ static void put_to(script_t* sc, dmvsi_var_t var, int32_t value, uint16_t ms, co
}
}

/*
* What a class does to an element: whether adding or removing it (any of
* its changes) moves it or fades it. A change of a class puts only that -
* an element's classes are changed independently (the playing row of a
* list, a row hidden as the list is shorter), and one that does not fade
* it must not undo the opacity another put.
*/
static void class_effects(script_t* sc, const change_t* ch, bool* moves_x, bool* moves_y, bool* fades)
{
*moves_x = *moves_y = *fades = false;
for (uint32_t i = 0; i < sc->change_count; i++)
{
const change_t* k = &sc->changes[i];
if (k->element != ch->element || k->changed || !k->laid_out ||
(k->mod.kind != MOD_CLASS_ADD && k->mod.kind != MOD_CLASS_REMOVE) || strcmp(k->mod.name, ch->mod.name) != 0)
continue;
*moves_x = *moves_x || k->dx != 0;
*moves_y = *moves_y || k->dy != 0;
*fades = *fades || k->opacity != own_opacity(k->element->style);
}
}

/* The actions of a change of one element: its variables to where the change puts it */
static void apply(script_t* sc, node_t* e, uint8_t kind, const char* name, const char* value)
{
Expand All @@ -1528,11 +1563,14 @@ static void apply(script_t* sc, node_t* e, uint8_t kind, const char* name, const
int32_t ox, oy;
view_origin(sc->c, &ox, &oy);
dmvsi_rect_t r = group_rect(sc->c, e, ox, oy);
if (e->dynamic->bind[DMVSI_BIND_X] != 0)
bool moves_x = true, moves_y = true, fades = true;
if (kind == MOD_CLASS_ADD || kind == MOD_CLASS_REMOVE)
class_effects(sc, ch, &moves_x, &moves_y, &fades);
if (e->dynamic->bind[DMVSI_BIND_X] != 0 && moves_x)
put_to(sc, e->dynamic->bind[DMVSI_BIND_X], r.x + ch->dx, ch->move_ms, ch->move_easing);
if (e->dynamic->bind[DMVSI_BIND_Y] != 0)
if (e->dynamic->bind[DMVSI_BIND_Y] != 0 && moves_y)
put_to(sc, e->dynamic->bind[DMVSI_BIND_Y], r.y + ch->dy, ch->move_ms, ch->move_easing);
if (e->dynamic->bind[DMVSI_BIND_OPACITY] != 0)
if (e->dynamic->bind[DMVSI_BIND_OPACITY] != 0 && fades)
put_to(sc, e->dynamic->bind[DMVSI_BIND_OPACITY], ch->opacity, ch->fade_ms, ch->fade_easing);
}

Expand Down
7 changes: 6 additions & 1 deletion src/style.c
Original file line number Diff line number Diff line change
Expand Up @@ -1717,7 +1717,12 @@ static void apply(apply_t* a, const char* name, const char* v)
return;
}
if (strcmp(name, "box-sizing") == 0) { st->border_box = word_is(s, "border-box"); return; }
if (strcmp(name, "visibility") == 0) { st->hidden = inherit ? pa->hidden : !word_is(s, "visible"); return; }
if (strcmp(name, "visibility") == 0)
{
st->hidden = inherit ? pa->hidden : !word_is(s, "visible");
st->visibility = inherit ? VISIBILITY_INHERITED : st->hidden ? VISIBILITY_HIDDEN : VISIBILITY_VISIBLE;
return;
}
if (strcmp(name, "z-index") == 0)
{
int64_t n;
Expand Down
83 changes: 83 additions & 0 deletions tests/dmvs_html_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,89 @@ static uint32_t clicks_setting(dmvsi_doc_t doc, const dmvsi_node_t* n, dmvsi_var
return count;
}

/*
* A list on a screen hidden as the page is: the texts a script sets show in
* every look of a row (a class it changes), and a row hidden by its own class
* is faded in and out by it - not as hidden all the time as its screen is.
*/
DMOD_TEST_STEP(dmvs_html_converts_a_list_on_a_hidden_screen)
{
int status = -1;
DMOD_TEST_EXPECT_TRUE(write_file(TEST_FILE("list.html"),
"<!DOCTYPE html><html><head><style>"
"body { margin: 0 } #screen { width: 200px; height: 100px; position: relative }"
"#nav { width: 20px; height: 20px; background: #333 }"
".view { position: absolute; left: 20px; top: 0; width: 180px; height: 100px; visibility: hidden }"
".view.active { visibility: visible }"
".row { height: 20px; color: #fff } .row-active { color: #f0f } .invisible { visibility: hidden }"
"</style></head><body><div id=\"screen\"><div id=\"nav\"></div><div id=\"list\" class=\"view\">"
"<div id=\"row-0\" class=\"row row-active\"><div id=\"t-0\">Demo A</div></div>"
"<div id=\"row-1\" class=\"row\"><div id=\"t-1\">Demo B</div></div>"
"<div id=\"row-2\" class=\"row\"><div id=\"t-2\">Demo C</div></div>"
"</div></div><script>\n"
"let count = 3;\n"
"const names = ['First', 'Second', 'Third'];\n"
"function show() {\n"
" count = count - 1;\n"
" for (let i = 0; i < 3; i++) {\n"
" document.getElementById('t-' + i).innerText = names[i];\n"
" const row = document.getElementById('row-' + i);\n"
" if (i < count) row.classList.remove('invisible'); else row.classList.add('invisible');\n"
" }\n"
"}\n"
"function pick(t) {\n"
" for (let i = 0; i < 3; i++) {\n"
" const row = document.getElementById('row-' + i);\n"
" if (i === t) row.classList.add('row-active'); else row.classList.remove('row-active');\n"
" }\n"
"}\n"
"document.getElementById('nav').addEventListener('click', () => document.getElementById('list').classList.add('active'));\n"
"document.getElementById('row-0').addEventListener('click', () => show());\n"
"document.getElementById('row-1').addEventListener('click', () => pick(1));\n"
"document.getElementById('row-2').addEventListener('click', () => pick(2));\n"
"</script></body></html>"));
dmvsi_doc_t doc = convert(TEST_FILE("list.html"), "screen", 0, 0, &status);
DMOD_TEST_EXPECT_EQ(status, 0);
if (doc == NULL)
return;
const dmvsi_node_t* root = dmvsi_root(doc);

/* No look of a row shows the text the page had before the script set its own */
DMOD_TEST_EXPECT_EQ(count_texts(root, "Demo A"), 0u);
DMOD_TEST_EXPECT_EQ(count_texts(root, "Demo B"), 0u);
DMOD_TEST_EXPECT_EQ(count_texts(root, "Demo C"), 0u);

/* Each row is faded by its opacity's variable, shown as the page has it */
static const char rows[3][8] = { "row-0", "row-1", "row-2" };
for (uint32_t i = 0; i < 3U; i++)
{
const dmvsi_node_t* r = group_named(root->first, rows[i]);
DMOD_TEST_EXPECT_TRUE(r != NULL && r->bind[DMVSI_BIND_OPACITY] != 0);
DMOD_TEST_EXPECT_TRUE(r != NULL && var_initial(doc, r->bind[DMVSI_BIND_OPACITY]) == 255);
}

/* Picking a row changes no row's opacity: row-active does not fade, invisible does */
const dmvsi_node_t* r1 = group_named(root->first, "row-1");
if (r1 != NULL && r1->click != 0)
{
const dmvsi_action_t* acts = NULL;
uint32_t n = flat_actions(doc, r1->click, &acts);
uint32_t fades = 0;
for (uint32_t i = 0; i < n; i++)
{
for (uint32_t k = 0; k < 3U; k++)
{
const dmvsi_node_t* r = group_named(root->first, rows[k]);
if (r != NULL && acts[i].var == r->bind[DMVSI_BIND_OPACITY] &&
(acts[i].kind == DMVSI_ACT_SET || acts[i].kind == DMVSI_ACT_ANIMATE))
fades++;
}
}
DMOD_TEST_EXPECT_EQ(fades, 0u);
}
dmvsi_free(doc);
}

DMOD_TEST_STEP(dmvs_html_converts_what_scripts_build)
{
int status = -1;
Expand Down
Loading