This source file includes following definitions.
- layout_reset_lines
- new_line_info
- get_justify
- get_lines_info
- layout_justify
- layout_setup_scroll_bounds
- layout_scroll
- TraverseLayout
- OnLayout
- layout_is_enabled
- compositor_init_layout
- compositor_layout_modified
- compositor_mpeg4_layout_get_sensor_handler
#include "nodes_stacks.h"
#include "mpeg4_grouping.h"
#include "visual_manager.h"
#ifndef GPAC_DISABLE_VRML
typedef struct
{
PARENT_MPEG4_STACK_2D
Bool is_scrolling;
u32 start_scroll_type;
Double start_time, pause_time;
GF_List *lines;
GF_Rect clip;
Fixed last_scroll, prev_rate, scroll_rate, scale_scroll, scroll_len, scroll_min, scroll_max;
GF_SensorHandler hdl;
s32 key_scroll;
Bool keys_active;
} LayoutStack;
typedef struct
{
Fixed width, height, ascent, descent;
u32 first_child, nb_children;
Bool line_break;
} LineInfo;
static void layout_reset_lines(LayoutStack *st)
{
while (gf_list_count(st->lines)) {
LineInfo *li = (LineInfo *)gf_list_get(st->lines, 0);
gf_list_rem(st->lines, 0);
gf_free(li);
}
}
static LineInfo *new_line_info(LayoutStack *st)
{
LineInfo *li;
GF_SAFEALLOC(li, LineInfo);
gf_list_add(st->lines, li);
return li;
}
enum
{
L_FIRST,
L_BEGIN,
L_MIDDLE,
L_END,
L_JUSTIFY,
};
static u32 get_justify(M_Layout *l, u32 i)
{
if (l->justify.count <= i) return L_BEGIN;
if (!strcmp(l->justify.vals[i], "END")) return L_END;
if (!strcmp(l->justify.vals[i], "MIDDLE")) return L_MIDDLE;
if (!strcmp(l->justify.vals[i], "FIRST")) return L_FIRST;
if (!strcmp(l->justify.vals[i], "SPREAD")) return L_JUSTIFY;
if (!strcmp(l->justify.vals[i], "JUSTIFY")) return L_JUSTIFY;
return L_BEGIN;
}
static void get_lines_info(LayoutStack *st, M_Layout *l)
{
Fixed prev_discard_width;
u32 i, count;
LineInfo *li;
Fixed max_w, max_h;
max_w = st->clip.width;
max_h = st->clip.height;
layout_reset_lines(st);
count = gf_list_count(st->groups);
if (!count) return;
li = new_line_info(st);
li->first_child = 0;
prev_discard_width = 0;
for (i=0; i<count; i++) {
ChildGroup *cg = (ChildGroup *)gf_list_get(st->groups, i);
if (!l->horizontal) {
if (cg->final.height + li->height > max_h) {
if (l->wrap) {
li = new_line_info(st);
li->first_child = i;
}
}
if (cg->final.width > li->width) li->width = cg->final.width;
li->height += cg->final.height;
li->nb_children ++;
} else {
if ((cg->text_type==2) || (i && (cg->final.width + li->width> max_w))) {
if (cg->text_type==2) li->line_break = 1;
if (l->wrap) {
if (!li->ascent) {
li->ascent = li->height;
li->descent = 0;
}
if (prev_discard_width) {
li->width -= prev_discard_width;
li->nb_children--;
}
if ((cg->text_type==1) && (i+1==count)) break;
li = new_line_info(st);
li->first_child = i;
if (cg->text_type) {
li->first_child++;
continue;
}
}
}
if (cg->ascent) {
if (li->ascent < cg->ascent) li->ascent = cg->ascent;
if (li->descent < cg->descent) li->descent = cg->descent;
if (li->height < li->ascent + li->descent) li->height = li->ascent + li->descent;
} else if (cg->final.height > li->height) {
li->height = cg->final.height;
}
li->width += cg->final.width;
li->nb_children ++;
prev_discard_width = (cg->text_type==1) ? cg->final.width : 0;
}
}
}
static void layout_justify(LayoutStack *st, M_Layout *l)
{
u32 first, minor, major, i, k, nbLines;
Fixed current_top, current_left, h;
LineInfo *li;
ChildGroup *cg, *prev;
get_lines_info(st, l);
major = get_justify(l, 0);
minor = get_justify(l, 1);
st->scroll_len = 0;
nbLines = gf_list_count(st->lines);
if (l->horizontal) {
if (l->wrap && !l->topToBottom) {
li = (LineInfo*)gf_list_get(st->lines, 0);
current_top = st->clip.y - st->clip.height;
if (li) current_top += li->height;
} else {
current_top = st->clip.y;
}
for (k=0; k<nbLines; k++) {
Fixed spacing = 0;
li = (LineInfo*)gf_list_get(st->lines, k);
first = li->first_child;
if (!l->leftToRight) first += li->nb_children - 1;
if (!l->topToBottom && k) current_top += li->height;
cg = (ChildGroup *)gf_list_get(st->groups, first);
if (!cg) continue;
switch (major) {
case L_END:
cg->final.x = st->clip.x + st->clip.width - li->width;
break;
case L_MIDDLE:
cg->final.x = st->clip.x + (st->clip.width - li->width)/2;
break;
case L_FIRST:
case L_BEGIN:
cg->final.x = st->clip.x;
break;
case L_JUSTIFY:
cg->final.x = st->clip.x;
if (li->nb_children>1) {
cg = (ChildGroup *)gf_list_get(st->groups, li->nb_children-1);
spacing = (st->clip.width - li->width) / (li->nb_children-1) ;
if (spacing<0) spacing = 0;
else if (cg->ascent) {
if ( (k+1==nbLines) || li->line_break) {
spacing = 0;
}
}
}
break;
}
i = first;
while (1) {
cg = (ChildGroup *)gf_list_get(st->groups, i);
if (!cg) break;
h = MAX(li->ascent, li->height);
switch (minor) {
case L_FIRST:
cg->final.y = current_top - h;
if (cg->ascent) {
cg->final.y += cg->ascent;
} else {
cg->final.y += cg->final.height;
}
break;
case L_MIDDLE:
cg->final.y = current_top - (h - cg->final.height)/2;
break;
case L_END:
cg->final.y = current_top;
break;
case L_BEGIN:
default:
cg->final.y = current_top - h + cg->final.height;
break;
}
if (i != first) {
if (l->leftToRight) {
prev = (ChildGroup *)gf_list_get(st->groups, i-1);
} else {
prev = (ChildGroup *)gf_list_get(st->groups, i+1);
}
cg->final.x = prev->final.x + prev->final.width + spacing;
}
i += l->leftToRight ? +1 : -1;
if (l->leftToRight && (i==li->first_child + li->nb_children))
break;
else if (!l->leftToRight && (i==li->first_child - 1))
break;
}
if (l->topToBottom) {
current_top -= gf_mulfix(l->spacing, li->height);
} else {
current_top += gf_mulfix(l->spacing - FIX_ONE, li->height);
}
if (l->scrollVertical) {
st->scroll_len += li->height;
} else {
if (st->scroll_len < li->width) st->scroll_len = li->width;
}
}
return;
}
li = (LineInfo*)gf_list_get(st->lines, 0);
if (l->wrap && !l->leftToRight) {
current_left = st->clip.x + st->clip.width;
if (li) current_left -= li->width;
} else {
current_left = st->clip.x;
}
for (k=0; k<nbLines; k++) {
Fixed spacing = 0;
li = (LineInfo*)gf_list_get(st->lines, k);
first = li->first_child;
if (!l->topToBottom) first += li->nb_children - 1;
cg = (ChildGroup *)gf_list_get(st->groups, first);
switch (major) {
case L_END:
cg->final.y = st->clip.y - st->clip.height + li->height;
break;
case L_MIDDLE:
cg->final.y = st->clip.y - st->clip.height/2 + li->height/2;
break;
case L_FIRST:
case L_BEGIN:
cg->final.y = st->clip.y;
break;
case L_JUSTIFY:
cg->final.y = st->clip.y;
if (li->nb_children>1) {
spacing = (st->clip.height - li->height) / (li->nb_children-1) ;
if (spacing<0) spacing = 0;
}
break;
}
i = first;
while (1) {
cg = (ChildGroup *)gf_list_get(st->groups, i);
switch (minor) {
case L_MIDDLE:
cg->final.x = current_left + li->width/2 - cg->final.width/2;
break;
case L_END:
cg->final.x = current_left + li->width - cg->final.width;
break;
case L_BEGIN:
case L_FIRST:
default:
cg->final.x = current_left;
break;
}
if (i != first) {
if (l->topToBottom) {
prev = (ChildGroup *)gf_list_get(st->groups, i-1);
} else {
prev = (ChildGroup *)gf_list_get(st->groups, i+1);
}
cg->final.y = prev->final.y - prev->final.height + spacing;
}
i += l->topToBottom ? +1 : -1;
if (l->topToBottom && (i==li->first_child + li->nb_children))
break;
else if (!l->topToBottom && (i==li->first_child - 1))
break;
}
if (l->leftToRight) {
current_left += gf_mulfix(l->spacing, li->width);
} else if (k < nbLines - 1) {
li = (LineInfo*)gf_list_get(st->lines, k+1);
current_left -= gf_mulfix(l->spacing, li->width);
}
if (l->scrollVertical) {
if (st->scroll_len < li->height) st->scroll_len = li->height;
} else {
st->scroll_len += li->width;
}
}
}
static void layout_setup_scroll_bounds(LayoutStack *st, M_Layout *l)
{
u32 minor_justify = 0;
st->scroll_min = st->scroll_max = 0;
if (l->horizontal) minor_justify = l->scrollVertical ? 1 : 0;
else minor_justify = l->scrollVertical ? 0 : 1;
if (l->scrollMode != -1) {
switch( get_justify(l, minor_justify)) {
case L_END:
if (l->scrollVertical) {
if (st->scale_scroll<0) st->scroll_max = - st->scroll_len;
else st->scroll_max = st->clip.height;
} else {
if (st->scale_scroll<0) st->scroll_max = - st->clip.width;
else st->scroll_max = st->scroll_len;
}
break;
case L_MIDDLE:
if (l->scrollVertical) {
if (st->scale_scroll<0) st->scroll_max = - (st->clip.height + st->scroll_len)/2;
else st->scroll_max = (st->clip.height + st->scroll_len)/2;
} else {
if (st->scale_scroll<0) st->scroll_max = - (st->clip.width + st->scroll_len)/2;
else st->scroll_max = (st->clip.width + st->scroll_len)/2;
}
break;
default:
if (l->scrollVertical) {
if (st->scale_scroll<0) st->scroll_max = - st->clip.height;
else st->scroll_max = st->scroll_len;
} else {
if (st->scale_scroll<0) st->scroll_max = - st->scroll_len;
else st->scroll_max = st->clip.width;
}
break;
}
}
else {
st->scroll_max = 0;
}
if (l->scrollMode==1) {
st->scroll_min = 0;
return;
}
if (l->horizontal && l->scrollVertical) {
if (st->scale_scroll<0) {
st->scroll_min = st->scroll_len;
} else {
st->scroll_min = - st->clip.height;
}
return;
}
switch( get_justify(l, minor_justify)) {
case L_END:
if (l->scrollVertical) {
if (st->scale_scroll<0) st->scroll_min = st->clip.height;
else st->scroll_min = - st->scroll_len;
} else {
if (st->scale_scroll<0) st->scroll_min = st->scroll_len;
else st->scroll_min = -st->clip.width;
}
break;
case L_MIDDLE:
if (l->scrollVertical) {
if (st->scale_scroll<0) st->scroll_min = (st->clip.height + st->scroll_len)/2;
else st->scroll_min = - (st->clip.height + st->scroll_len)/2;
} else {
if (st->scale_scroll<0) st->scroll_min = (st->clip.width + st->scroll_len)/2;
else st->scroll_min = - (st->clip.width + st->scroll_len)/2;
}
break;
default:
if (l->scrollVertical) {
if (st->scale_scroll<0) st->scroll_min = st->scroll_len;
else st->scroll_min = - st->clip.height;
} else {
if (st->scale_scroll<0) st->scroll_min = st->clip.width;
else st->scroll_min = - st->scroll_len;
}
break;
}
}
static void layout_scroll(GF_TraverseState *tr_state, LayoutStack *st, M_Layout *l)
{
u32 i, nb_lines;
Fixed scrolled, rate, elapsed, scroll_diff;
Bool smooth, do_scroll, stop_anim;
Double time = 0;
ChildGroup *cg;
if (!st->scale_scroll && !st->is_scrolling && !st->key_scroll) return;
if (st->key_scroll) {
if (!st->is_scrolling) {
layout_setup_scroll_bounds(st, l);
st->is_scrolling = 1;
}
scrolled = st->last_scroll + INT2FIX(st->key_scroll);
} else {
time = gf_node_get_scene_time((GF_Node *)l);
if ((st->start_scroll_type==2) && (st->prev_rate==st->scale_scroll)) st->start_scroll_type = 0;
if (st->start_scroll_type) {
st->start_time = time;
st->is_scrolling = 1;
st->prev_rate = st->scale_scroll;
if ((st->start_scroll_type==2) && st->scale_scroll) {
Fixed cur_pos = st->scroll_min + st->last_scroll;
layout_setup_scroll_bounds(st, l);
cur_pos -= st->scroll_min;
st->start_time = time - FIX2FLT(gf_divfix(cur_pos, st->scale_scroll));
} else {
layout_setup_scroll_bounds(st, l);
}
st->last_scroll = 0;
st->start_scroll_type = 0;
}
rate = st->scale_scroll;
if (!rate) {
if (!st->pause_time) {
st->pause_time = time;
} else {
time = st->pause_time;
}
rate = st->prev_rate;
} else if (st->pause_time) {
st->start_time += (time - st->pause_time);
st->pause_time = 0;
}
elapsed = FLT2FIX((Float) (time - st->start_time));
scrolled = gf_mulfix(elapsed, rate);
}
smooth = l->smoothScroll;
if (!l->horizontal && l->scrollVertical) smooth = 1;
else if (l->horizontal && !l->scrollVertical) smooth = 1;
stop_anim = 0;
if (smooth) {
do_scroll = 1;
} else {
Fixed dim;
scroll_diff = scrolled - st->last_scroll;
do_scroll = 0;
nb_lines = gf_list_count(st->lines);
for (i=0; i < nb_lines; i++) {
LineInfo *li = (LineInfo*)gf_list_get(st->lines, i);
dim = l->scrollVertical ? li->height : li->width;
if (st->key_scroll) {
if (st->key_scroll<0) dim = -dim;
scrolled = dim + st->last_scroll;
if (st->scroll_min > st->scroll_len + scrolled) {
scrolled = st->scroll_min - st->scroll_len;
} else {
do_scroll = 1;
}
break;
} else if (ABS(scroll_diff) >= dim) {
do_scroll = 1;
break;
}
}
}
scroll_diff = st->scroll_max - st->scroll_min;
if ((scroll_diff<0) && (scrolled<=scroll_diff)) {
stop_anim = 1;
scrolled = scroll_diff;
}
else if ((scroll_diff>0) && (scrolled>=scroll_diff)) {
stop_anim = 1;
scrolled = scroll_diff;
}
if (do_scroll)
st->last_scroll = scrolled;
else
scrolled = st->last_scroll;
i=0;
while ((cg = (ChildGroup *)gf_list_enum(st->groups, &i))) {
if (l->scrollVertical) {
cg->scroll_y = st->scroll_min + scrolled;
cg->scroll_x = 0;
} else {
cg->scroll_x = st->scroll_min + scrolled;
cg->scroll_y = 0;
}
}
if (st->key_scroll) {
st->key_scroll = 0;
return;
}
if (!stop_anim) {
tr_state->visual->compositor->force_next_frame_redraw = 1;
return;
}
if (!l->loop) return;
st->start_time = time;
gf_sc_invalidate(tr_state->visual->compositor, NULL);
}
static void TraverseLayout(GF_Node *node, void *rs, Bool is_destroy)
{
Bool recompute_layout;
u32 i;
ChildGroup *cg;
GF_IRect prev_clip;
Bool mode_bckup, had_clip=GF_FALSE;
ParentNode2D *parent_bck;
GF_Rect prev_clipper;
M_Layout *l = (M_Layout *)node;
LayoutStack *st = (LayoutStack *) gf_node_get_private(node);
GF_TraverseState *tr_state = (GF_TraverseState *)rs;
if (is_destroy) {
layout_reset_lines(st);
parent_node_predestroy((ParentNode2D *)st);
gf_list_del(st->lines);
gf_free(st);
return;
}
if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) {
st->scale_scroll = st->scroll_rate = l->scrollRate;
if (visual_get_size_info(tr_state, &st->clip.width, &st->clip.height)) {
st->scale_scroll = gf_mulfix(st->scale_scroll, l->scrollVertical ? st->clip.height : st->clip.width);
}
if (l->size.x>=0) st->clip.width = l->size.x;
if (l->size.y>=0) st->clip.height = l->size.y;
st->bounds = st->clip = gf_rect_center(st->clip.width, st->clip.height);
if (st->scale_scroll && !st->start_scroll_type) st->start_scroll_type = 1;
drawable_reset_group_highlight(tr_state, node);
}
if ((tr_state->traversing_mode==TRAVERSE_PICK) && !gf_sc_pick_in_clipper(tr_state, &st->clip))
goto layout_exit;
if ((tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) && !tr_state->for_node) {
tr_state->bounds = st->clip;
#ifndef GPAC_DISABLE_3D
gf_bbox_from_rect(&tr_state->bbox, &st->clip);
#endif
goto layout_exit;
}
recompute_layout = 0;
if (gf_node_dirty_get(node))
recompute_layout = 1;
prev_clip = tr_state->visual->top_clipper;
if (tr_state->traversing_mode==TRAVERSE_SORT) {
compositor_2d_update_clipper(tr_state, st->clip, &had_clip, &prev_clipper, 0);
if (tr_state->has_clip) {
tr_state->visual->top_clipper = gf_rect_pixelize(&tr_state->clipper);
gf_irect_intersect(&tr_state->visual->top_clipper, &prev_clip);
}
}
if (recompute_layout) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Layout] recomputing positions\n"));
parent_node_reset((ParentNode2D*)st);
parent_bck = tr_state->parent;
mode_bckup = tr_state->traversing_mode;
tr_state->traversing_mode = TRAVERSE_GET_BOUNDS;
tr_state->parent = (ParentNode2D *) st;
if (l->wrap) tr_state->text_split_mode = 1;
parent_node_traverse(node, (ParentNode2D *)st, tr_state);
tr_state->parent = parent_bck;
tr_state->traversing_mode = mode_bckup;
if (l->wrap) tr_state->text_split_mode = 0;
i=0;
while ((cg = (ChildGroup *)gf_list_enum(st->groups, &i))) {
cg->final.x = - cg->final.width/2;
cg->final.y = cg->final.height/2;
}
layout_justify(st, l);
if (l->scrollRate && st->is_scrolling) {
layout_setup_scroll_bounds(st, l);
}
}
if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) {
tr_state->bounds = st->bounds;
if (l->scrollVertical) {
tr_state->bounds.height = st->scroll_len;
} else {
tr_state->bounds.width = st->scroll_len;
}
#ifndef GPAC_DISABLE_3D
gf_bbox_from_rect(&tr_state->bbox, &tr_state->bounds);
#endif
goto layout_exit;
}
layout_scroll(tr_state, st, l);
i=0;
while ((cg = (ChildGroup *)gf_list_enum(st->groups, &i))) {
parent_node_child_traverse(cg, tr_state);
}
tr_state->visual->top_clipper = prev_clip;
if (tr_state->traversing_mode==TRAVERSE_SORT) {
if (had_clip) tr_state->clipper = prev_clipper;
tr_state->has_clip = had_clip;
drawable_check_focus_highlight(node, tr_state, &st->clip);
}
layout_exit:
tr_state->text_split_mode = 0;
}
static Bool OnLayout(GF_SensorHandler *sh, Bool is_over, Bool is_cancel, GF_Event *ev, GF_Compositor *compositor)
{
Bool vertical = ((M_Layout *)sh->sensor)->scrollVertical;
LayoutStack *st = (LayoutStack *) gf_node_get_private(sh->sensor);
if (!is_over) {
st->is_scrolling = 0;
st->key_scroll = 0;
return 0;
}
if (ev->type!=GF_EVENT_KEYDOWN) {
st->is_scrolling = 0;
st->key_scroll = 0;
return 0;
}
switch (ev->key.key_code) {
case GF_KEY_LEFT:
if (!st->keys_active) return 0;
if (vertical) return 0;
st->key_scroll = -1;
break;
case GF_KEY_RIGHT:
if (!st->keys_active) return 0;
if (vertical) return 0;
st->key_scroll = +1;
break;
case GF_KEY_UP:
if (!st->keys_active) return 0;
if (!vertical) return 0;
st->key_scroll = +1;
break;
case GF_KEY_DOWN:
if (!st->keys_active) return 0;
if (!vertical) return 0;
st->key_scroll = -1;
break;
case GF_KEY_ENTER:
st->keys_active = !st->keys_active;
break;
default:
st->key_scroll = 0;
return 0;
}
gf_sc_invalidate(compositor, NULL);
return 1;
}
static Bool layout_is_enabled(GF_Node *node)
{
M_Layout *l = (M_Layout *)node;
if (l->scrollRate != 0) return 0;
return 1;
}
void compositor_init_layout(GF_Compositor *compositor, GF_Node *node)
{
LayoutStack *stack;
GF_SAFEALLOC(stack, LayoutStack);
if (!stack) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate layout stack\n"));
return;
}
parent_node_setup((ParentNode2D*)stack);
stack->lines = gf_list_new();
gf_node_set_private(node, stack);
gf_node_set_callback_function(node, TraverseLayout);
stack->hdl.sensor = node;
stack->hdl.IsEnabled = layout_is_enabled;
stack->hdl.OnUserEvent = OnLayout;
}
void compositor_layout_modified(GF_Compositor *compositor, GF_Node *node)
{
LayoutStack *st = (LayoutStack *) gf_node_get_private(node);
if (st->scroll_rate == ((M_Layout*)node)->scrollRate) {
st->start_scroll_type = 1;
}
else if (((M_Layout*)node)->scrollRate) {
st->start_scroll_type = 2;
}
gf_node_dirty_set(node, GF_SG_NODE_DIRTY, 0);
gf_sc_invalidate(compositor, NULL);
}
GF_SensorHandler *compositor_mpeg4_layout_get_sensor_handler(GF_Node *node)
{
LayoutStack *st = (LayoutStack *) gf_node_get_private(node);
return &st->hdl;
}
#endif