From 5572000f1a02ed1107d3757d7ca06727c1217813 Mon Sep 17 00:00:00 2001 From: Yeongjong Lee Date: Wed, 27 Feb 2019 14:45:27 -0500 Subject: [PATCH] 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 --- src/Makefile_Elementary.am | 3 + src/bin/elementary/test_ui_table.c | 26 +- src/lib/elementary/efl_ui_box_layout.c | 346 ++++++------------- src/lib/elementary/efl_ui_container_layout.c | 158 +++++++++ src/lib/elementary/efl_ui_container_layout.h | 42 +++ src/lib/elementary/efl_ui_table.c | 44 ++- src/lib/elementary/efl_ui_table.eo | 1 + src/lib/elementary/efl_ui_table_layout.c | 294 ++++++++++++++++ src/lib/elementary/efl_ui_table_private.h | 5 + src/lib/elementary/meson.build | 3 + 10 files changed, 655 insertions(+), 267 deletions(-) create mode 100644 src/lib/elementary/efl_ui_container_layout.c create mode 100644 src/lib/elementary/efl_ui_container_layout.h create mode 100644 src/lib/elementary/efl_ui_table_layout.c diff --git a/src/Makefile_Elementary.am b/src/Makefile_Elementary.am index c020b343d4..98d953df9c 100644 --- a/src/Makefile_Elementary.am +++ b/src/Makefile_Elementary.am @@ -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 \ diff --git a/src/bin/elementary/test_ui_table.c b/src/bin/elementary/test_ui_table.c index 098fe139b7..2f698f103c 100644 --- a/src/bin/elementary/test_ui_table.c +++ b/src/bin/elementary/test_ui_table.c @@ -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); diff --git a/src/lib/elementary/efl_ui_box_layout.c b/src/lib/elementary/efl_ui_box_layout.c index 90c5d1b42f..bc9ea63c86 100644 --- a/src/lib/elementary/efl_ui_box_layout.c +++ b/src/lib/elementary/efl_ui_box_layout.c @@ -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])); } diff --git a/src/lib/elementary/efl_ui_container_layout.c b/src/lib/elementary/efl_ui_container_layout.c new file mode 100644 index 0000000000..e8c5979dd0 --- /dev/null +++ b/src/lib/elementary/efl_ui_container_layout.c @@ -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]; +} diff --git a/src/lib/elementary/efl_ui_container_layout.h b/src/lib/elementary/efl_ui_container_layout.h new file mode 100644 index 0000000000..097abee13b --- /dev/null +++ b/src/lib/elementary/efl_ui_container_layout.h @@ -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 +#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 diff --git a/src/lib/elementary/efl_ui_table.c b/src/lib/elementary/efl_ui_table.c index 889dc423db..5f42043b16 100644 --- a/src/lib/elementary/efl_ui_table.c +++ b/src/lib/elementary/efl_ui_table.c @@ -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) { diff --git a/src/lib/elementary/efl_ui_table.eo b/src/lib/elementary/efl_ui_table.eo index c62612a02a..f3c067f98b 100644 --- a/src/lib/elementary/efl_ui_table.eo +++ b/src/lib/elementary/efl_ui_table.eo @@ -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; diff --git a/src/lib/elementary/efl_ui_table_layout.c b/src/lib/elementary/efl_ui_table_layout.c new file mode 100644 index 0000000000..0a21384a8f --- /dev/null +++ b/src/lib/elementary/efl_ui_table_layout.c @@ -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])); +} diff --git a/src/lib/elementary/efl_ui_table_private.h b/src/lib/elementary/efl_ui_table_private.h index e7c3821ee9..0ab08e5109 100644 --- a/src/lib/elementary/efl_ui_table_private.h +++ b/src/lib/elementary/efl_ui_table_private.h @@ -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; }; diff --git a/src/lib/elementary/meson.build b/src/lib/elementary/meson.build index 38ee28fe9a..a3200855c9 100644 --- a/src/lib/elementary/meson.build +++ b/src/lib/elementary/meson.build @@ -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',