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',