#include "efl_ui_box_private.h" #include "efl_ui_container_layout.h" #define MY_CLASS EFL_UI_BOX_FLOW_CLASS typedef struct _Efl_Ui_Box_Flow_Data Efl_Ui_Box_Flow_Data; struct _Efl_Ui_Box_Flow_Data { }; typedef struct _Item_Calc Item_Calc; typedef struct _Row_Calc Row_Calc; struct _Item_Calc { EINA_INLIST; Evas_Object *obj; Row_Calc *row; double weight_factor; Efl_Ui_Container_Item_Hints hints[2]; /* 0 is x-axis, 1 is y-axis */ }; struct _Row_Calc { EINA_INLIST; Evas_Object *obj; int item_count; int min_sum; int hgsize; double weight_sum; double cross_weight; double cross_space; double cur_pos; double weight_factor; Efl_Ui_Container_Item_Hints hint; }; static int _item_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->weight_factor <= it1->weight_factor ? -1 : 1; } static int _row_weight_sort_cb(const void *l1, const void *l2) { Row_Calc *it1, *it2; it1 = EINA_INLIST_CONTAINER_GET(l1, Row_Calc); it2 = EINA_INLIST_CONTAINER_GET(l2, Row_Calc); return it2->weight_factor <= it1->weight_factor ? -1 : 1; } EOLIAN static void _efl_ui_box_flow_efl_pack_layout_layout_update(Eo *obj, Efl_Ui_Box_Flow_Data *pd EINA_UNUSED) { Efl_Ui_Box_Data *bd = efl_data_scope_get(obj, EFL_UI_BOX_CLASS); Eo *child; Eina_List *li; Eina_Inlist *inlist = NULL; Item_Calc *items, *item; Row_Calc *rows, *row; Efl_Ui_Container_Item_Hints *hints, *hint; Eina_Bool axis = !efl_ui_layout_orientation_is_horizontal(bd->dir, EINA_FALSE); Eina_Bool c_axis = !axis; int want[2] = { 0, 0 }; int rc = 0, count, i = 0, id, item_last = 0; double cur_pos, cross_weight_sum = 0, cross_min_sum = 0, min_sum = 0; Efl_Ui_Container_Layout_Calc box_calc[2]; /* 0 is x-axis, 1 is y-axis */ count = eina_list_count(bd->children); if (!count) { efl_gfx_hint_size_restricted_min_set(obj, EINA_SIZE2D(0, 0)); return; } _efl_ui_container_layout_init(obj, box_calc); items = alloca(count * sizeof(*items)); rows = alloca(count * sizeof(*rows)); memset(rows, 0, count * sizeof(*rows)); #ifdef DEBUG memset(items, 0, count * sizeof(*items)); #endif // scan all items, get their properties, calculate total weight & min size EINA_LIST_FOREACH(bd->children, li, child) { item = &items[i++]; item->obj = child; hints = item->hints; _efl_ui_container_layout_item_init(item->obj, hints); if ((bd->homogeneous && !axis) || box_calc[0].fill) hints[0].weight = 1; else if (hints[0].weight < 0) hints[0].weight = 0; if ((bd->homogeneous && axis) || box_calc[1].fill) hints[1].weight = 1; else if (hints[1].weight < 0) hints[1].weight = 0; if (want[axis] < hints[axis].space) want[axis] = hints[axis].space; if (bd->homogeneous) continue; if (i == 1) { min_sum = hints[axis].space; } else if (box_calc[axis].size < (min_sum + hints[axis].space + box_calc[axis].pad)) { min_sum = hints[axis].space; rc++; } else { min_sum += (hints[axis].space + box_calc[axis].pad); } row = &rows[rc]; item->row = row; if (row->cross_weight < hints[c_axis].weight) row->cross_weight = hints[c_axis].weight; if (row->cross_space < hints[c_axis].space) row->cross_space = hints[c_axis].space; row->weight_sum += hints[axis].weight; row->min_sum += hints[axis].space; row->item_count++; } // initialize homogeneous properties if (bd->homogeneous) { min_sum = 0; for (i = 0; i < count; i++) { item = &items[i]; hints = items[i].hints; if (i == 0) { min_sum = want[axis]; } else if (box_calc[axis].size < (min_sum + want[axis] + box_calc[axis].pad)) { min_sum = want[axis]; rc++; } else { min_sum += (want[axis] + box_calc[axis].pad); } row = &rows[rc]; item->row = row; if (row->cross_weight < hints[c_axis].weight) row->cross_weight = hints[c_axis].weight; if (row->cross_space < hints[c_axis].space) row->cross_space = hints[c_axis].space; row->item_count++; } } // calculate item space of each row for (id = 0, i = 0; id <= rc; id++) { int box_size; row = &rows[id]; row->cur_pos = box_calc[axis].pos; box_size = box_calc[axis].size - (box_calc[axis].pad * (row->item_count - 1)); row->hgsize = box_size / row->item_count; cross_min_sum += row->cross_space; cross_weight_sum += row->cross_weight; item_last += row->item_count; if (bd->homogeneous) continue; if (row->weight_sum > 0) { int calc_size; double orig_weight = row->weight_sum; calc_size = box_size; inlist = NULL; for (; i < item_last; i++) { double denom; hint = &items[i].hints[axis]; denom = (hint->weight * box_size) - (orig_weight * hint->space); if (denom > 0) { items[i].weight_factor = (hint->weight * box_size) / denom; inlist = eina_inlist_sorted_insert(inlist, EINA_INLIST_GET(&items[i]), _item_weight_sort_cb); } else { calc_size -= hint->space; row->weight_sum -= hint->weight; } } EINA_INLIST_FOREACH(inlist, item) { double weight_len; hint = &item->hints[axis]; weight_len = (calc_size * hint->weight) / row->weight_sum; if (hint->space < weight_len) { hint->space = weight_len; } else { row->weight_sum -= hint->weight; calc_size -= hint->space; } } } else if (EINA_DBL_EQ(row->weight_sum, 0)) { row->cur_pos += (box_size - row->min_sum) * box_calc[axis].align; i += row->item_count; } } // calculate row space box_calc[c_axis].size -= (box_calc[c_axis].pad * rc); cur_pos = box_calc[c_axis].pos; if ((box_calc[c_axis].size > cross_min_sum)) { if (cross_weight_sum > 0) { int orig_size, calc_size; double orig_weight = cross_weight_sum; calc_size = orig_size = box_calc[c_axis].size; inlist = NULL; for (i = 0; i <= rc; i++) { double denom; row = &rows[i]; denom = (row->cross_weight * orig_size) - (orig_weight * row->cross_space); if (denom > 0) { row->weight_factor = (row->cross_weight * orig_size) / denom; inlist = eina_inlist_sorted_insert(inlist, EINA_INLIST_GET(row), _row_weight_sort_cb); } else { calc_size -= row->cross_space; cross_weight_sum -= row->cross_weight; } } EINA_INLIST_FOREACH(inlist, row) { double weight_len; weight_len = (calc_size * row->cross_weight) / cross_weight_sum; if (row->cross_space < weight_len) { row->cross_space = weight_len; } else { cross_weight_sum -= row->cross_weight; calc_size -= row->cross_space; } } } else if (EINA_DBL_EQ(cross_weight_sum, 0)) { cur_pos += (box_calc[c_axis].size - cross_min_sum) * box_calc[c_axis].align; } } // calculate item geometry int item_size[2], item_pos[2], sw, sh; row = NULL; for (i = 0; i < count; i++) { item = &items[i]; hints = items[i].hints; if (row && (row != item->row)) cur_pos += row->cross_space + box_calc[c_axis].pad; row = item->row; if (bd->homogeneous) hints[axis].space = row->hgsize; hints[c_axis].space = row->cross_space; sw = hints[0].space - (hints[0].margin[0] + hints[0].margin[1]); sh = hints[1].space - (hints[1].margin[0] + hints[1].margin[1]); item_size[0] = ((hints[0].weight > 0) && hints[0].fill) ? sw : 0; item_size[1] = ((hints[1].weight > 0) && hints[1].fill) ? sh : 0; _efl_ui_container_layout_min_max_calc(hints, &item_size[0], &item_size[1], (hints[0].aspect > 0) && (hints[1].aspect > 0)); item_pos[axis] = row->cur_pos + 0.5; item_pos[c_axis] = cur_pos + 0.5; 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)); row->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[axis] += (box_calc[axis].margin[0] + box_calc[axis].margin[1]); want[c_axis] = (box_calc[c_axis].margin[0] + box_calc[c_axis].margin[1]) + (box_calc[c_axis].pad * rc) + cross_min_sum; efl_gfx_hint_size_restricted_min_set(obj, EINA_SIZE2D(want[0], want[1])); efl_event_callback_call(obj, EFL_PACK_EVENT_LAYOUT_UPDATED, NULL); } #include "efl_ui_box_flow.eo.c"