efl_ui_table: refactor layout_update

Summary:
There are three reasons to refactor layout_update of Efl.Ui.Table.

=== 1. Inconsistency of hint behavior. ===

Some hint property is often not respected. for example, hint_min is ignored in
Table when it is used with hint_max even if hint_weight is 0. hint_aspect is
always ignored in Table.
The ambiguous behavior make it hard to layout widgets in container. of course,
we documented 'it's just a hint that should be used whenever appropriate.' but i
don't think it means that 'hint API is sometimes respected and we also don't
know when that API is respected.'. at least there is rule for consistent
behavior and we should be able to explain why a widget is located here and
why some hint property is ignored.

So, i'll suggest priority of hint property. this refactoring support following
priority.
1) HintMin
2) HintMin + HintAspect
3) HintMargin
4) HintMax
5) HintAspect
6) HintWeight, HintFill
7) HintAlign

ref T5487
Please check with unit test D7840

=== 2. To Enhance usability. ===

Efl.Ui.Table is using homogeneous mode of evas_table which have same columns,
rows size. but i think a table can generally change columns, rows size and
we can provide homogeneous mode option.(D7892)

In this patch
 - table columns(rows) min size is decided by maximum size among its cells
width(height) min size.
 - table columns(rows) weight is decided by maximum weight among its cells
horizontal(vertical) weight.

Also, pack_align is implemented. it is used if no item has a weight.

=== 3. To remove internal evas_table. ===

This is low priority work. however, i guess is is necessary for lightweight
container widget. there are two size_hint callback to adjust table size and
efl_canvas_group_calculate is called twice when it is resized.
This patch is first step to remove internal evas_table.

Test Plan:
make check
elementary_test -to 'efl.ui.table'

Reviewers: jpeg, Jaehyun_Cho, zmike

Reviewed By: zmike

Subscribers: zmike, cedric, #reviewers, #committers

Tags: #efl

Maniphest Tasks: T5487

Differential Revision: https://phab.enlightenment.org/D7841
This commit is contained in:
Yeongjong Lee 2019-02-27 14:45:27 -05:00 committed by Mike Blumenkrantz
parent 665f8877e9
commit 5572000f1a
10 changed files with 655 additions and 267 deletions

View File

@ -826,6 +826,8 @@ lib_elementary_libelementary_la_SOURCES = \
lib/elementary/els_cursor.c \
lib/elementary/els_tooltip.c \
lib/elementary/elu_ews_wm.c \
lib/elementary/efl_ui_container_layout.c \
lib/elementary/efl_ui_container_layout.h \
lib/elementary/efl_ui_box.c \
lib/elementary/efl_ui_box_flow.c \
lib/elementary/efl_ui_box_stack.c \
@ -838,6 +840,7 @@ lib_elementary_libelementary_la_SOURCES = \
lib/elementary/efl_ui_anchor_popup.c \
lib/elementary/efl_ui_table.c \
lib/elementary/efl_ui_table_static.c \
lib/elementary/efl_ui_table_layout.c \
lib/elementary/efl_ui_table_private.h \
lib/elementary/efl_ui_text.c \
lib/elementary/efl_ui_text_factory_images.c \

View File

@ -34,31 +34,31 @@ weights_cb(void *data, const Efl_Event *event)
{
case NONE:
efl_gfx_hint_align_set(table, 0.5, 0.5);
for (int i = 0; i < 7; i++)
for (int i = 1; i < 7; i++)
efl_gfx_hint_weight_set(objects[i], 0, 0);
break;
case NONE_BUT_FILL:
efl_gfx_hint_fill_set(table, EINA_TRUE, EINA_TRUE);
for (int i = 0; i < 7; i++)
for (int i = 1; i < 7; i++)
efl_gfx_hint_weight_set(objects[i], 0, 0);
break;
case EQUAL:
efl_gfx_hint_align_set(table, 0.5, 0.5);
for (int i = 0; i < 7; i++)
for (int i = 1; i < 7; i++)
efl_gfx_hint_weight_set(objects[i], 1, 1);
break;
case ONE:
efl_gfx_hint_align_set(table, 0.5, 0.5);
for (int i = 0; i < 6; i++)
for (int i = 1; i < 7; i++)
efl_gfx_hint_weight_set(objects[i], 0, 0);
efl_gfx_hint_weight_set(objects[6], 1, 1);
efl_gfx_hint_weight_set(objects[2], 1, 1);
break;
case TWO:
efl_gfx_hint_align_set(table, 0.5, 0.5);
for (int i = 0; i < 5; i++)
for (int i = 1; i < 7; i++)
efl_gfx_hint_weight_set(objects[i], 0, 0);
efl_gfx_hint_weight_set(objects[5], 1, 1);
efl_gfx_hint_weight_set(objects[6], 1, 1);
efl_gfx_hint_weight_set(objects[2], 1, 1);
efl_gfx_hint_weight_set(objects[3], 1, 1);
break;
case CUSTOM:
efl_object_override(table, &custom_layout_ops);
@ -89,11 +89,11 @@ margin_slider_cb(void *data, const Efl_Event *event)
}
static void
btnmargins_slider_cb(void *data, const Efl_Event *event)
btnmargins_slider_cb(void *data EINA_UNUSED, const Efl_Event *event)
{
int val = elm_slider_value_get(event->object);
for (int i = 1; i < 7; i++)
efl_gfx_hint_margin_set(data, val, val, val, val);
efl_gfx_hint_margin_set(objects[i], val, val, val, val);
}
static void
@ -333,7 +333,7 @@ test_ui_table(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_
efl_event_callback_add(o, EFL_UI_SLIDER_EVENT_CHANGED, padding_slider_cb, table);
elm_slider_min_max_set(o, 0, 40);
elm_slider_inverted_set(o, 1);
elm_slider_value_set(o, 10);
elm_slider_value_set(o, 0);
efl_pack(bx, o);
efl_gfx_entity_visible_set(o, 1);
@ -359,7 +359,7 @@ test_ui_table(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_
efl_event_callback_add(o, EFL_UI_SLIDER_EVENT_CHANGED, margin_slider_cb, table);
elm_slider_min_max_set(o, 0, 40);
elm_slider_inverted_set(o, 1);
elm_slider_value_set(o, 10);
elm_slider_value_set(o, 0);
efl_pack(bx, o);
efl_gfx_entity_visible_set(o, 1);
@ -386,7 +386,7 @@ test_ui_table(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_
efl_event_callback_add(o, EFL_UI_SLIDER_EVENT_CHANGED, btnmargins_slider_cb, table);
elm_slider_min_max_set(o, 0, 40);
elm_slider_inverted_set(o, 1);
elm_slider_value_set(o, 10);
elm_slider_value_set(o, 0);
efl_pack(bx, o);
efl_gfx_entity_visible_set(o, 1);

View File

@ -1,6 +1,7 @@
#define EFL_GFX_HINT_PROTECTED
#include "efl_ui_box_private.h"
#include "efl_ui_container_layout.h"
// FIXME: handle RTL? just invert the horizontal order?
@ -11,65 +12,19 @@ struct _Item_Calc
EINA_INLIST;
Evas_Object *obj;
double weight[2];
double align[2];
double space[2];
double comp_factor;
Eina_Bool fill[2];
Eina_Size2D max, min, aspect;
int pad[4];
Efl_Gfx_Hint_Aspect aspect_type;
int id;
double weight_factor;
Efl_Ui_Container_Item_Hints hints[2]; /* 0 is x-axis, 1 is y-axis */
};
static int
weight_sort_cb(const void *l1, const void *l2)
_weight_sort_cb(const void *l1, const void *l2)
{
Item_Calc *it1, *it2;
it1 = EINA_INLIST_CONTAINER_GET(l1, Item_Calc);
it2 = EINA_INLIST_CONTAINER_GET(l2, Item_Calc);
return it2->comp_factor <= it1->comp_factor ? -1 : 1;
}
static inline void
_min_max_calc(Item_Calc *item, int *cw, int *ch, Eina_Bool aspect_check)
{
int w = *cw, h = *ch;
if (aspect_check)
{
w = h * item->aspect.w / item->aspect.h;
if (w > *cw)
{
w = *cw;
h = w * item->aspect.h / item->aspect.w;
}
}
if (w > item->max.w)
{
w = item->max.w;
if (aspect_check) h = w * item->aspect.h / item->aspect.w;
}
if (h > item->max.h)
{
h = item->max.h;
if (aspect_check) w = h * item->aspect.w / item->aspect.h;
}
if (w < item->min.w)
{
w = item->min.w;
if (aspect_check) h = w * item->aspect.h / item->aspect.w;
}
if (h < item->min.h)
{
h = item->min.h;
if (aspect_check) w = h * item->aspect.w / item->aspect.h;
}
*cw = w;
*ch = h;
return it2->weight_factor <= it1->weight_factor ? -1 : 1;
}
void
@ -77,37 +32,17 @@ _efl_ui_box_custom_layout(Efl_Ui_Box *ui_box, Evas_Object_Box_Data *bd)
{
Efl_Ui_Box_Data *pd = efl_data_scope_get(ui_box, EFL_UI_BOX_CLASS);
Evas_Object_Box_Option *opt;
Evas_Object *o;
Eina_List *li;
Eina_Inlist *inlist = NULL;
int wantw = 0, wanth = 0; // requested size
Eina_Rect boxs;
Item_Calc *items, *item;
Eina_Bool horiz = efl_ui_dir_is_horizontal(pd->dir, EINA_FALSE);
int id = 0, count, boxl = 0, boxr = 0, boxt = 0, boxb = 0;
int length, want, pad;
double cur_pos, weight[2] = { 0, 0 }, scale, mmin = 0;
double box_align[2];
Eina_Bool box_fill[2] = { EINA_FALSE, EINA_FALSE };
Efl_Ui_Container_Item_Hints *hints, *hint;
Eina_Bool axis = !efl_ui_dir_is_horizontal(pd->dir, EINA_FALSE);
Eina_Bool r_axis = !axis;
int want[2] = { 0, 0 };
int count, i = 0;
double cur_pos, mmin = 0, weight_sum = 0;
Efl_Ui_Container_Layout_Calc box_calc[2]; /* 0 is x-axis, 1 is y-axis */
boxs = efl_gfx_entity_geometry_get(ui_box);
efl_gfx_hint_margin_get(ui_box, &boxl, &boxr, &boxt, &boxb);
scale = efl_gfx_entity_scale_get(ui_box);
// Box align: used if "item has max size and fill" or "no item has a weight"
// Note: cells always expand on the orthogonal direction
box_align[0] = pd->align.h;
box_align[1] = pd->align.v;
if (box_align[0] < 0)
{
box_fill[0] = EINA_TRUE;
box_align[0] = 0.5;
}
if (box_align[1] < 0)
{
box_fill[1] = EINA_TRUE;
box_align[1] = 0.5;
}
count = eina_list_count(bd->children);
if (!count)
@ -116,240 +51,153 @@ _efl_ui_box_custom_layout(Efl_Ui_Box *ui_box, Evas_Object_Box_Data *bd)
return;
}
_efl_ui_container_layout_init(ui_box, box_calc);
items = alloca(count * sizeof(*items));
#ifdef DEBUG
memset(items, 0, count * sizeof(*items));
#endif
// box outer margin
boxs.w -= boxl + boxr;
boxs.h -= boxt + boxb;
boxs.x += boxl;
boxs.y += boxt;
// scan all items, get their properties, calculate total weight & min size
EINA_LIST_FOREACH(bd->children, li, opt)
{
item = &items[id];
o = item->obj = opt->obj;
item = &items[i++];
item->obj = opt->obj;
hints = item->hints;
efl_gfx_hint_weight_get(o, &item->weight[0], &item->weight[1]);
efl_gfx_hint_align_get(o, &item->align[0], &item->align[1]);
efl_gfx_hint_margin_get(o, &item->pad[0], &item->pad[1], &item->pad[2], &item->pad[3]);
efl_gfx_hint_fill_get(o, &item->fill[0], &item->fill[1]);
item->max = efl_gfx_hint_size_max_get(o);
item->min = efl_gfx_hint_size_combined_min_get(o);
efl_gfx_hint_aspect_get(o, &item->aspect_type, &item->aspect);
_efl_ui_container_layout_item_init(item->obj, hints);
if (horiz && (box_fill[0] || pd->homogeneous)) item->weight[0] = 1;
else if (item->weight[0] < 0) item->weight[0] = 0;
if (!horiz && (box_fill[1] || pd->homogeneous)) item->weight[1] = 1;
else if (item->weight[1] < 0) item->weight[1] = 0;
if (pd->homogeneous || box_calc[0].fill)
hints[0].weight = 1;
else if (hints[0].weight < 0)
hints[0].weight = 0;
if (EINA_DBL_EQ(item->align[0], -1))
if (pd->homogeneous || box_calc[1].fill)
hints[1].weight = 1;
else if (hints[1].weight < 0)
hints[1].weight = 0;
weight_sum += hints[axis].weight;
if (hints[r_axis].space > want[r_axis])
want[r_axis] = hints[r_axis].space;
if (pd->homogeneous)
{
item->align[0] = 0.5;
item->fill[0] = EINA_TRUE;
}
else if (item->align[0] < 0) item->align[0] = 0;
else if (item->align[0] > 1) item->align[0] = 1;
if (EINA_DBL_EQ(item->align[1], -1))
{
item->align[1] = 0.5;
item->fill[1] = EINA_TRUE;
}
else if (item->align[1] < 0) item->align[1] = 0;
else if (item->align[1] > 1) item->align[1] = 1;
if (item->min.w < 0) item->min.w = 0;
if (item->min.h < 0) item->min.h = 0;
if (item->max.w < 0) item->max.w = INT_MAX;
if (item->max.h < 0) item->max.h = INT_MAX;
weight[0] += item->weight[0];
weight[1] += item->weight[1];
if ((item->aspect.w <= 0) || (item->aspect.h <= 0))
{
if ((item->aspect.w <= 0) ^ (item->aspect.h <= 0))
{
ERR("Invalid aspect parameter for obj: %p", item->obj);
item->aspect.w = item->aspect.h = 0;
item->aspect_type = EFL_GFX_HINT_ASPECT_NONE;
}
if (hints[axis].space > mmin)
mmin = hints[axis].space;
}
else
{
_min_max_calc(item, &item->min.w, &item->min.h, EINA_TRUE);
want[axis] += hints[axis].space;
}
item->space[0] = item->min.w + item->pad[0] + item->pad[1];
item->space[1] = item->min.h + item->pad[2] + item->pad[3];
if (horiz)
{
if (item->space[1] > wanth)
wanth = item->space[1];
if (pd->homogeneous)
{
if (item->space[0] > mmin)
mmin = item->space[0];
}
else
{
wantw += item->space[0];
}
}
else
{
if (item->space[0] > wantw)
wantw = item->space[0];
if (pd->homogeneous)
{
if (item->space[1] > mmin)
mmin = item->space[1];
}
else
{
wanth += item->space[1];
}
}
item->id = id++;
}
// total space & available space
if (horiz)
{
if (pd->homogeneous)
wantw = mmin * count;
want = wantw;
length = boxs.w;
pad = pd->pad.scalable ? (pd->pad.h * scale) : pd->pad.h;
if (boxs.h < wanth)
boxs.h = wanth;
}
else
{
if (pd->homogeneous)
wanth = mmin * count;
want = wanth;
length = boxs.h;
pad = pd->pad.scalable ? (pd->pad.v * scale) : pd->pad.v;
if (boxs.w < wantw)
boxs.w = wantw;
}
if (pd->homogeneous)
want[axis] = mmin * count;
if (box_calc[r_axis].size < want[r_axis])
box_calc[r_axis].size = want[r_axis];
// padding can not be squeezed (note: could make it an option)
length -= pad * (count - 1);
cur_pos = horiz ? boxs.x : boxs.y;
box_calc[axis].size -= (box_calc[axis].pad * (count - 1));
box_calc[r_axis].pad = 0;
cur_pos = box_calc[axis].pos;
// calculate weight length
if (!pd->homogeneous && (length > want) && (weight[!horiz] > 0))
// calculate weight size
if (!pd->homogeneous && (box_calc[axis].size > want[axis]) && (weight_sum > 0))
{
int orig_length = length;
double orig_weight = weight[!horiz];
int orig_size, calc_size;
double orig_weight = weight_sum;
for (id = 0; id < count; id++)
calc_size = orig_size = box_calc[axis].size;
for (i = 0; i < count; i++)
{
double denom;
item = &items[id];
hint = &items[i].hints[axis];
denom = (item->weight[!horiz] * orig_length) -
(orig_weight * item->space[!horiz]);
denom = (hint->weight * orig_size) - (orig_weight * hint->space);
if (denom > 0)
{
item->comp_factor = (item->weight[!horiz] * orig_length) / denom;
inlist = eina_inlist_sorted_insert(inlist, EINA_INLIST_GET(item),
weight_sort_cb);
items[i].weight_factor = (hint->weight * orig_size) / denom;
inlist = eina_inlist_sorted_insert(inlist, EINA_INLIST_GET(&items[i]),
_weight_sort_cb);
}
else
{
length -= item->space[!horiz];
weight[!horiz] -= item->weight[!horiz];
calc_size -= hint->space;
weight_sum -= hint->weight;
}
}
EINA_INLIST_FOREACH(inlist, item)
{
double weight_len;
hint = &item->hints[axis];
weight_len = (length * item->weight[!horiz]) / weight[!horiz];
if (item->space[!horiz] < weight_len)
weight_len = (calc_size * hint->weight) / weight_sum;
if (hint->space < weight_len)
{
item->space[!horiz] = weight_len;
hint->space = weight_len;
}
else
{
weight[!horiz] -= item->weight[!horiz];
length -= item->space[!horiz];
weight_sum -= hint->weight;
calc_size -= hint->space;
}
}
}
// calculate item geometry
{
int x, y, w, h, sw, sh;
int item_size[2], item_pos[2], sw, sh;
if (length > want)
if (box_calc[axis].size > want[axis])
{
if (pd->homogeneous)
mmin = (double)length / count;
else if (EINA_DBL_EQ(weight[!horiz], 0))
cur_pos += (length - want) * box_align[!horiz];
mmin = (double)box_calc[axis].size / count;
else if (EINA_DBL_EQ(weight_sum, 0))
cur_pos += (box_calc[axis].size - want[axis]) * box_calc[axis].align;
}
for (id = 0; id < count; id++)
for (i = 0; i < count; i++)
{
item = &items[id];
hints = items[i].hints;
if (pd->homogeneous)
item->space[!horiz] = mmin;
item->space[horiz] = horiz ? boxs.h : boxs.w;
sw = item->space[0] - item->pad[0] - item->pad[1];
sh = item->space[1] - item->pad[2] - item->pad[3];
hints[axis].space = mmin;
hints[r_axis].space = box_calc[r_axis].size;
sw = hints[0].space - (hints[0].margin[0] + hints[0].margin[1]);
sh = hints[1].space - (hints[1].margin[0] + hints[1].margin[1]);
if ((item->weight[0] > 0) && item->fill[0])
w = sw;
else
w = 0;
item_size[0] = ((hints[0].weight > 0) && hints[0].fill) ? sw : 0;
item_size[1] = ((hints[1].weight > 0) && hints[1].fill) ? sh : 0;
if ((item->weight[1] > 0) && item->fill[1])
h = sh;
else
h = 0;
_efl_ui_container_layout_min_max_calc(hints, &item_size[0], &item_size[1],
(hints[0].aspect > 0) && (hints[1].aspect > 0));
_min_max_calc(item, &w, &h, (item->aspect.w > 0) &&
(item->aspect.h > 0));
if (horiz)
{
x = cur_pos + 0.5;
y = boxs.y;
}
else
{
x = boxs.x;
y = cur_pos + 0.5;
}
x += item->pad[0] + ((sw - w) * item->align[0]);
y += item->pad[2] + ((sh - h) * item->align[1]);
item_pos[axis] = cur_pos + 0.5;
item_pos[r_axis] = box_calc[r_axis].pos;
cur_pos += item->space[!horiz] + pad;
item_pos[0] += (hints[0].margin[0] +
((sw - item_size[0]) * hints[0].align));
item_pos[1] += (hints[1].margin[0] +
((sh - item_size[1]) * hints[1].align));
efl_gfx_entity_geometry_set(item->obj, EINA_RECT(x, y, w, h));
cur_pos += hints[axis].space + box_calc[axis].pad;
efl_gfx_entity_geometry_set(items[i].obj,
EINA_RECT(item_pos[0], item_pos[1],
item_size[0], item_size[1]));
}
}
want[0] += (box_calc[0].margin[0] + box_calc[0].margin[1]) +
(box_calc[0].pad * (count - 1));
want[1] += (box_calc[1].margin[0] + box_calc[1].margin[1]) +
(box_calc[1].pad * (count - 1));
if (horiz)
{
efl_gfx_hint_size_min_set(ui_box, EINA_SIZE2D(
wantw + boxl + boxr + pad * (count - 1),
wanth + boxt + boxb));
}
else
{
efl_gfx_hint_size_min_set(ui_box, EINA_SIZE2D(
wantw + boxl + boxr,
wanth + pad * (count - 1) + boxt + boxb));
}
efl_gfx_hint_size_min_set(ui_box, EINA_SIZE2D(want[0], want[1]));
}

View File

@ -0,0 +1,158 @@
#include "efl_ui_container_layout.h"
void
_efl_ui_container_layout_min_max_calc(Efl_Ui_Container_Item_Hints *item, int *cw, int *ch, Eina_Bool aspect_check)
{
int w = *cw, h = *ch;
Eina_Size2D aspect = { item[0].aspect, item[1].aspect };
if (aspect_check)
{
w = h * aspect.w / aspect.h;
if (w > *cw)
{
w = *cw;
h = w * aspect.h / aspect.w;
}
}
if (w > item[0].max)
{
w = item[0].max;
if (aspect_check) h = w * aspect.h / aspect.w;
}
if (h > item[1].max)
{
h = item[1].max;
if (aspect_check) w = h * aspect.w / aspect.h;
}
if (w < item[0].min)
{
w = item[0].min;
if (aspect_check) h = w * aspect.h / aspect.w;
}
if (h < item[1].min)
{
h = item[1].min;
if (aspect_check) w = h * aspect.w / aspect.h;
}
*cw = w;
*ch = h;
}
void
_efl_ui_container_layout_item_init(Eo* o, Efl_Ui_Container_Item_Hints *item)
{
Eina_Size2D max;
Eina_Size2D min;
Eina_Size2D aspect;
Efl_Gfx_Hint_Aspect aspect_type;
efl_gfx_hint_weight_get(o, &item[0].weight, &item[1].weight);
efl_gfx_hint_align_get(o, &item[0].align, &item[1].align);
efl_gfx_hint_margin_get(o, &item[0].margin[0], &item[0].margin[1],
&item[1].margin[0], &item[1].margin[1]);
efl_gfx_hint_fill_get(o, &item[0].fill, &item[1].fill);
max = efl_gfx_hint_size_max_get(o);
min = efl_gfx_hint_size_combined_min_get(o);
efl_gfx_hint_aspect_get(o, &aspect_type, &aspect);
item[0].aspect = aspect.w;
item[1].aspect = aspect.h;
item[0].aspect_type = aspect_type;
item[1].aspect_type = aspect_type;
if (EINA_DBL_EQ(item[0].align, -1))
{
item[0].align = 0.5;
item[0].fill = EINA_TRUE;
}
else if (item[0].align < 0) item[0].align = 0;
else if (item[0].align > 1) item[0].align = 1;
if (EINA_DBL_EQ(item[1].align, -1))
{
item[1].align = 0.5;
item[1].fill = EINA_TRUE;
}
else if (item[1].align < 0) item[1].align = 0;
else if (item[1].align > 1) item[1].align = 1;
if (min.w < 0) min.w = 0;
if (min.h < 0) min.h = 0;
if (max.w < 0) max.w = INT_MAX;
if (max.h < 0) max.h = INT_MAX;
item[0].max = max.w;
item[1].max = max.h;
item[0].min = min.w;
item[1].min = min.h;
if ((item[0].aspect <= 0) || (item[1].aspect_type <= 0))
{
if ((item[0].aspect <= 0) ^ (item[1].aspect_type <= 0))
{
ERR("Invalid aspect parameter for obj(%p)", o);
item[0].aspect = item[1].aspect_type = 0;
item[0].aspect_type = item[1].aspect_type = EFL_GFX_HINT_ASPECT_NONE;
}
}
else
{
_efl_ui_container_layout_min_max_calc(item, &item[0].min, &item[1].min,
EINA_TRUE);
}
item[0].space = item[0].min + item[0].margin[0] + item[0].margin[1];
item[1].space = item[1].min + item[1].margin[0] + item[1].margin[1];
}
void
_efl_ui_container_layout_init(Eo* obj, Efl_Ui_Container_Layout_Calc *calc)
{
Eina_Rect geom;
Eina_Bool pad_scalable;
geom = efl_gfx_entity_geometry_get(obj);
efl_gfx_hint_margin_get(obj, &calc[0].margin[0], &calc[0].margin[1],
&calc[1].margin[0], &calc[1].margin[1]);
calc[0].scale = calc[1].scale = efl_gfx_entity_scale_get(obj);
efl_pack_padding_get(obj, &calc[0].pad, &calc[1].pad, &pad_scalable);
calc[0].pad = pad_scalable ? (calc[0].pad * calc[0].scale) : calc[0].pad;
calc[1].pad = pad_scalable ? (calc[1].pad * calc[1].scale) : calc[1].pad;
// pack align is used if "no item has a weight"
efl_pack_align_get(obj, &calc[0].align, &calc[1].align);
if (calc[0].align < 0)
{
calc[0].fill = EINA_TRUE;
calc[0].align = 0.5;
}
else if (calc[0].align > 1)
{
calc[0].align = 1;
}
else
{
calc[0].fill = EINA_FALSE;
}
if (calc[1].align < 0)
{
calc[1].fill = EINA_TRUE;
calc[1].align = 0.5;
}
else if (calc[1].align > 1)
{
calc[1].align = 1;
}
else
{
calc[1].fill = EINA_FALSE;
}
calc[0].pos = geom.x + calc[0].margin[0];
calc[1].pos = geom.y + calc[1].margin[0];
calc[0].size = geom.w - calc[0].margin[0] - calc[0].margin[1];
calc[1].size = geom.h - calc[1].margin[0] - calc[1].margin[1];
}

View File

@ -0,0 +1,42 @@
#ifndef _EFL_UI_CONTAINER_HELPER_H_
#define _EFL_UI_CONTAINER_HELPER_H_
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#include <Elementary.h>
#include "elm_priv.h"
typedef struct _Efl_Ui_Container_Item_Hints Efl_Ui_Container_Item_Hints;
typedef struct _Efl_Ui_Container_Layout_Calc Efl_Ui_Container_Layout_Calc;
struct _Efl_Ui_Container_Item_Hints
{
int max;
int min;
int aspect;
int margin[2]; // start, end
Efl_Gfx_Hint_Aspect aspect_type;
double weight;
double align;
double space;
Eina_Bool fill;
};
struct _Efl_Ui_Container_Layout_Calc
{
int pos;
int size;
int margin[2];
double align;
double scale;
double pad;
Eina_Bool fill : 1;
};
void _efl_ui_container_layout_min_max_calc(Efl_Ui_Container_Item_Hints *item, int *cw, int *ch, Eina_Bool aspect_check);
void _efl_ui_container_layout_item_init(Eo* o, Efl_Ui_Container_Item_Hints *item);
void _efl_ui_container_layout_init(Eo* obj, Efl_Ui_Container_Layout_Calc *calc);
#endif

View File

@ -74,7 +74,16 @@ _table_size_hints_changed(void *data, Evas *e EINA_UNUSED,
{
Efl_Ui_Table_Data *pd = efl_data_scope_get(data, MY_CLASS);
_sizing_eval(data, pd);
if (table == data)
efl_pack_layout_request(data);
else
_sizing_eval(data, pd);
}
static void
_efl_ui_table_size_hints_changed_cb(void *data EINA_UNUSED, const Efl_Event *ev)
{
efl_pack_layout_request(ev->object);
}
/* Custom table class: overrides smart_calculate. */
@ -116,10 +125,7 @@ _custom_table_calc(Eo *obj, Custom_Table_Data *pd)
EOLIAN static void
_efl_ui_table_efl_pack_layout_layout_update(Eo *obj, Efl_Ui_Table_Data *pd)
{
ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
_sizing_eval(obj, pd);
efl_canvas_group_calculate(efl_super(wd->resize_obj, CUSTOM_TABLE_CLASS));
_efl_ui_table_custom_layout(obj, pd);
}
EOLIAN void
@ -148,6 +154,8 @@ _efl_ui_table_efl_canvas_group_group_add(Eo *obj, Efl_Ui_Table_Data *pd)
evas_object_event_callback_add
(table, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _table_size_hints_changed, obj);
efl_event_callback_add(obj, EFL_GFX_ENTITY_EVENT_HINTS_CHANGED,
_efl_ui_table_size_hints_changed_cb, NULL);
efl_canvas_group_add(efl_super(obj, MY_CLASS));
@ -168,6 +176,8 @@ _efl_ui_table_efl_canvas_group_group_del(Eo *obj, Efl_Ui_Table_Data *pd EINA_UNU
evas_object_event_callback_del_full
(wd->resize_obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
_table_size_hints_changed, obj);
efl_event_callback_del(obj, EFL_GFX_ENTITY_EVENT_HINTS_CHANGED,
_efl_ui_table_size_hints_changed_cb, NULL);
/* let's make our table object the *last* to be processed, since it
* may (smart) parent other sub objects here */
@ -197,6 +207,8 @@ _efl_ui_table_efl_object_constructor(Eo *obj, Efl_Ui_Table_Data *pd)
pd->last_row = -1;
pd->req_cols = 0;
pd->req_rows = 0;
pd->align.h = 0.5;
pd->align.v = 0.5;
return obj;
}
@ -232,6 +244,28 @@ _efl_ui_table_efl_pack_pack_padding_get(const Eo *obj, Efl_Ui_Table_Data *pd EIN
if (v) *v = pd->pad.v;
}
EOLIAN static void
_efl_ui_table_efl_pack_pack_align_set(Eo *obj, Efl_Ui_Table_Data *pd, double h, double v)
{
ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
if (h < 0) h = -1;
if (v < 0) v = -1;
if (h > 1) h = 1;
if (v > 1) v = 1;
pd->align.h = h;
pd->align.v = v;
efl_pack_layout_request(obj);
}
EOLIAN static void
_efl_ui_table_efl_pack_pack_align_get(const Eo *obj EINA_UNUSED, Efl_Ui_Table_Data *pd, double *h, double *v)
{
if (h) *h = pd->align.h;
if (v) *v = pd->align.v;
}
static void
_subobj_del_cb(void *data, const Efl_Event *event)
{

View File

@ -15,6 +15,7 @@ class @beta Efl.Ui.Table extends Efl.Ui.Widget implements Efl.Pack_Table, Efl.Pa
Efl.Pack.unpack;
Efl.Pack.pack;
Efl.Pack.pack_padding { get; set; }
Efl.Pack.pack_align { get; set; }
Efl.Pack_Table.pack_table;
Efl.Pack_Table.table_content_get;
Efl.Pack_Table.table_contents_get;

View File

@ -0,0 +1,294 @@
#include "efl_ui_table_private.h"
#include "efl_ui_container_layout.h"
typedef struct _Item_Calc Item_Calc;
typedef struct _Cell_Calc Cell_Calc;
typedef struct _Table_Calc Table_Calc;
struct _Item_Calc
{
Evas_Object *obj;
int cell_span[2];
int cell_index[2];
Efl_Ui_Container_Item_Hints hints[2]; /* 0 is x-axis, 1 is y-axis */
};
struct _Cell_Calc
{
EINA_INLIST;
int index;
int next;
double acc;
double space;
double weight;
double weight_factor;
Eina_Bool occupied : 1;
};
struct _Table_Calc
{
/* 0 is x-axis, 1 is y-axis */
int rows;
int cols;
int want[2];
double weight_sum[2];
Cell_Calc *cell_calc[2];
Efl_Ui_Container_Layout_Calc layout_calc[2];
};
static int
_weight_sort_cb(const void *l1, const void *l2)
{
Cell_Calc *cc1, *cc2;
cc1 = EINA_INLIST_CONTAINER_GET(l1, Cell_Calc);
cc2 = EINA_INLIST_CONTAINER_GET(l2, Cell_Calc);
return cc2->weight_factor <= cc1->weight_factor ? -1 : 1;
}
static void
_cell_weight_calc(Table_Calc *table_calc, Eina_Bool axis)
{
int i, count, layout_size, calc_size;
double denom, weight_sum, calc_weight;
Eina_Inlist *inlist = NULL;
Cell_Calc *cell_calc, *cc;
layout_size = calc_size = table_calc->layout_calc[axis].size;
weight_sum = calc_weight = table_calc->weight_sum[axis];
cell_calc = table_calc->cell_calc[axis];
count = axis ? table_calc->rows : table_calc->cols;
for (i = 0; i < count; i = cell_calc[i].next)
{
denom = (cell_calc[i].weight * layout_size) -
(weight_sum * cell_calc[i].space);
if (denom > 0)
{
cell_calc[i].weight_factor = (cell_calc[i].weight * layout_size) / denom;
inlist = eina_inlist_sorted_insert(inlist,
EINA_INLIST_GET(&cell_calc[i]),
_weight_sort_cb);
}
else
{
calc_size -= cell_calc[i].space;
calc_weight -= cell_calc[i].weight;
}
}
EINA_INLIST_FOREACH(inlist, cc)
{
double weight_len;
weight_len = (calc_size * cc->weight) / calc_weight;
if (cc->space < weight_len)
{
cc->space = weight_len;
}
else
{
calc_size -= cc->space;
calc_weight -= cc->weight;
}
}
}
static void
_efl_ui_table_regular_cell_init(Table_Calc *table_calc, Eina_Bool axis)
{
int i, index = 0, acc, want = 0, count;
double weight_sum = 0;
Cell_Calc *prev_cell = NULL, *cell_calc;
Efl_Ui_Container_Layout_Calc *layout_calc;
layout_calc = &(table_calc->layout_calc[axis]);
cell_calc = table_calc->cell_calc[axis];
count = axis ? table_calc->rows : table_calc->cols;
for (i = 0; i < count; i++)
{
if (!cell_calc[i].occupied) continue;
cell_calc[i].index = index++;
want += cell_calc[i].space;
weight_sum += cell_calc[i].weight;
if (prev_cell)
prev_cell->next = i;
prev_cell = &cell_calc[i];
}
if (prev_cell)
prev_cell->next = count;
table_calc->want[axis] = want;
table_calc->weight_sum[axis] = weight_sum;
table_calc->layout_calc[axis].size -= (table_calc->layout_calc[axis].pad
* (index - 1));
if ((layout_calc->size > want) && (weight_sum > 0))
_cell_weight_calc(table_calc, axis);
if (EINA_DBL_EQ(weight_sum, 0.0))
layout_calc->pos += (layout_calc->size - want) * layout_calc->align;
for (i = 0, acc = 0; i < count; acc += cell_calc[i].space, i = cell_calc[i].next)
cell_calc[i].acc = acc;
}
static inline int
_efl_ui_table_regular_item_pos_get(Table_Calc *table_calc, Item_Calc *item, Eina_Bool axis)
{
return 0.5 + table_calc->layout_calc[axis].pos
+ table_calc->cell_calc[axis][item->cell_index[axis]].acc
+ (table_calc->cell_calc[axis][item->cell_index[axis]].index *
table_calc->layout_calc[axis].pad);
}
static inline int
_efl_ui_table_regular_item_size_get(Table_Calc *table_calc, Item_Calc *item, Eina_Bool axis)
{
int start, end;
start = item->cell_index[axis];
end = start + item->cell_span[axis] - 1;
return table_calc->cell_calc[axis][end].acc
- table_calc->cell_calc[axis][start].acc
+ table_calc->cell_calc[axis][end].space
+ ((item->cell_span[axis] - 1) * table_calc->layout_calc[axis].pad)
- item->hints[axis].margin[0] - item->hints[axis].margin[1];
}
void
_efl_ui_table_custom_layout(Efl_Ui_Table *ui_table, Efl_Ui_Table_Data *pd)
{
Table_Item *ti;
Item_Calc *items, *item;
Efl_Ui_Container_Item_Hints *hints;
int i = 0, rows, cols;
Table_Calc table_calc;
if (!pd->count)
{
efl_gfx_hint_size_min_set(ui_table, EINA_SIZE2D(0, 0));
return;
}
_efl_ui_container_layout_init(ui_table, table_calc.layout_calc);
table_calc.want[0] = table_calc.want[1] = 0;
table_calc.weight_sum[0] = table_calc.weight_sum[1] = 0;
efl_pack_table_size_get(ui_table, &cols, &rows);
table_calc.cell_calc[0] = alloca(cols * sizeof(Cell_Calc));
table_calc.cell_calc[1] = alloca(rows * sizeof(Cell_Calc));
memset(table_calc.cell_calc[0], 0, cols * sizeof(Cell_Calc));
memset(table_calc.cell_calc[1], 0, rows * sizeof(Cell_Calc));
items = alloca(pd->count * sizeof(*items));
#ifdef DEBUG
memset(items, 0, pd->count * sizeof(*items));
#endif
table_calc.cols = cols;
table_calc.rows = rows;
// scan all items, get their properties, calculate total weight & min size
EINA_INLIST_FOREACH(pd->items, ti)
{
if (((ti->col + ti->col_span) > cols) ||
((ti->row + ti->row_span) > rows))
{
efl_gfx_entity_visible_set(ti->object, EINA_FALSE);
continue;
}
item = &items[i++];
item->obj = ti->object;
hints = item->hints;
_efl_ui_container_layout_item_init(item->obj, hints);
if (table_calc.layout_calc[0].fill)
hints[0].weight = 1;
else if (hints[0].weight < 0)
hints[0].weight = 0;
if (table_calc.layout_calc[1].fill)
hints[1].weight = 1;
else if (hints[1].weight < 0)
hints[1].weight = 0;
item->cell_index[0] = ti->col;
item->cell_index[1] = ti->row;
item->cell_span[0] = ti->col_span;
item->cell_span[1] = ti->row_span;
if (ti->col_span == 1)
{
table_calc.cell_calc[0][ti->col].occupied = EINA_TRUE;
if (table_calc.cell_calc[0][ti->col].space < hints[0].space)
table_calc.cell_calc[0][ti->col].space = hints[0].space;
if (table_calc.cell_calc[0][ti->col].weight < hints[0].weight)
table_calc.cell_calc[0][ti->col].weight = hints[0].weight;
}
if (ti->row_span == 1)
{
table_calc.cell_calc[1][ti->row].occupied = EINA_TRUE;
if (table_calc.cell_calc[1][ti->row].space < hints[1].space)
table_calc.cell_calc[1][ti->row].space = hints[1].space;
if (table_calc.cell_calc[1][ti->row].weight < hints[1].weight)
table_calc.cell_calc[1][ti->row].weight = hints[1].weight;
}
}
_efl_ui_table_regular_cell_init(&table_calc, 0);
_efl_ui_table_regular_cell_init(&table_calc, 1);
for (i = 0; i < pd->count; i++)
{
Eina_Rect space, item_geom;
item = &items[i];
hints = items[i].hints;
space.x = _efl_ui_table_regular_item_pos_get(&table_calc, item, 0);
space.y = _efl_ui_table_regular_item_pos_get(&table_calc, item, 1);
space.w = _efl_ui_table_regular_item_size_get(&table_calc, item, 0);
space.h = _efl_ui_table_regular_item_size_get(&table_calc, item, 1);
item_geom.w = hints[0].fill ? space.w : hints[0].min;
item_geom.h = hints[1].fill ? space.h : hints[1].min;
_efl_ui_container_layout_min_max_calc(hints, &item_geom.w, &item_geom.h,
(hints[0].aspect > 0) && (hints[1].aspect > 0));
item_geom.x = space.x + ((space.w - item_geom.w) * hints[0].align)
+ hints[0].margin[0];
item_geom.y = space.y + ((space.h - item_geom.h) * hints[1].align)
+ hints[1].margin[0];
efl_gfx_entity_geometry_set(item->obj, item_geom);
}
table_calc.want[0] += table_calc.layout_calc[0].margin[0]
+ table_calc.layout_calc[0].margin[1]
+ (table_calc.layout_calc[0].pad *
table_calc.cell_calc[0][cols - 1].index);
table_calc.want[1] += table_calc.layout_calc[1].margin[0]
+ table_calc.layout_calc[1].margin[1]
+ (table_calc.layout_calc[1].pad *
table_calc.cell_calc[1][rows - 1].index);
efl_gfx_hint_size_min_set(ui_table, EINA_SIZE2D(table_calc.want[0],
table_calc.want[1]));
}

View File

@ -14,6 +14,8 @@ typedef struct _Efl_Ui_Table_Data Efl_Ui_Table_Data;
typedef struct _Table_Item_Iterator Table_Item_Iterator;
typedef struct _Table_Item Table_Item;
void _efl_ui_table_custom_layout(Efl_Ui_Table *ui_table, Efl_Ui_Table_Data *pd);
#define TABLE_ITEM_KEY "__table_item"
struct _Table_Item
@ -39,6 +41,9 @@ struct _Efl_Ui_Table_Data
double h, v;
Eina_Bool scalable: 1;
} pad;
struct {
double h, v;
} align;
Eina_Bool linear_recalc : 1;
};

View File

@ -846,6 +846,8 @@ elementary_src = [
'els_cursor.c',
'els_tooltip.c',
'elu_ews_wm.c',
'efl_ui_container_layout.c',
'efl_ui_container_layout.h',
'efl_ui_box.c',
'efl_ui_box_flow.c',
'efl_ui_box_stack.c',
@ -858,6 +860,7 @@ elementary_src = [
'efl_ui_anchor_popup.c',
'efl_ui_table.c',
'efl_ui_table_static.c',
'efl_ui_table_layout.c',
'efl_ui_table_private.h',
'efl_ui_text.c',
'efl_ui_text_factory_images.c',