#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]; int hgsize[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_homogeneous_cell_init(Table_Calc *table_calc, Eina_Bool axis) { int i, index = 0, mmin = 0, count; Cell_Calc *prev_cell = NULL, *cell_calc; 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++; if (cell_calc[i].space > mmin) mmin = cell_calc[i].space; if (prev_cell) prev_cell->next = i; prev_cell = &cell_calc[i]; } if (!index) { memset(table_calc, 0, sizeof(Table_Calc)); return; } if (prev_cell) prev_cell->next = count; table_calc->layout_calc[axis].size -= (table_calc->layout_calc[axis].pad * (index - 1)); table_calc->want[axis] = mmin * index; table_calc->weight_sum[axis] = index; if (table_calc->want[axis] > table_calc->layout_calc[axis].size) table_calc->hgsize[axis] = table_calc->want[axis] / index; else table_calc->hgsize[axis] = table_calc->layout_calc[axis].size / index; table_calc->hgsize[axis] += table_calc->layout_calc[axis].pad; } 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; else if (i && cell_calc[0].next == 0) cell_calc[0].next = i; 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_homogeneous_item_pos_get(Table_Calc *table_calc, Item_Calc *item, Eina_Bool axis) { return 0.5 + table_calc->layout_calc[axis].pos + (table_calc->hgsize[axis] * table_calc->cell_calc[axis][item->cell_index[axis]].index); } static inline int _efl_ui_table_homogeneous_item_size_get(Table_Calc *table_calc, Item_Calc *item, Eina_Bool axis) { return (table_calc->hgsize[axis] * item->cell_span[axis]) - table_calc->layout_calc[axis].pad; } 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]; } /* this function performs a simplified layout when the table has changed position * but no other changes have occurred, e.g., when a table is being scrolled */ static void _efl_ui_table_layout_simple(Efl_Ui_Table *ui_table, Efl_Ui_Table_Data *pd) { Table_Item *ti; Eina_Position2D pos = efl_gfx_entity_position_get(ui_table); EINA_INLIST_FOREACH(EINA_INLIST_GET(pd->items), ti) { Eina_Position2D child_pos = efl_gfx_entity_position_get(ti->object); efl_gfx_entity_position_set(ti->object, EINA_POSITION2D(pos.x - pd->last_pos.x + child_pos.x, pos.y - pd->last_pos.y + child_pos.y)); } pd->last_pos = pos; efl_event_callback_call(ui_table, EFL_PACK_EVENT_LAYOUT_UPDATED, NULL); } 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 id = 0, i = 0, count, rows, cols; int (*_efl_ui_table_item_pos_get[2])(Table_Calc *, Item_Calc *, Eina_Bool); int (*_efl_ui_table_item_size_get[2])(Table_Calc *, Item_Calc *, Eina_Bool); Table_Calc table_calc; Eina_Bool do_free; count = pd->count; if (!count) { efl_gfx_hint_size_restricted_min_set(ui_table, EINA_SIZE2D(0, 0)); return; } if (!pd->full_recalc) { _efl_ui_table_layout_simple(ui_table, pd); return; } _efl_ui_container_layout_init(ui_table, table_calc.layout_calc); pd->last_pos.x = table_calc.layout_calc[0].pos - table_calc.layout_calc[0].margin[0]; pd->last_pos.y = table_calc.layout_calc[1].pos - table_calc.layout_calc[1].margin[0]; 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)); /* Item_Calc struct is currently 152 bytes. * this is pretty big to be allocating a huge number of, and we don't want to explode the stack */ do_free = count >= 500; if (do_free) { items = malloc(count * sizeof(*items)); EINA_SAFETY_ON_NULL_RETURN(items); } else items = alloca(count * sizeof(*items)); #ifdef DEBUG memset(items, 0, 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(EINA_INLIST_GET(pd->items), ti) { if (((ti->col + ti->col_span) > cols) || ((ti->row + ti->row_span) > rows)) { efl_gfx_entity_geometry_set(ti->object, EINA_RECT(9999, 9999, 0, 0)); count--; continue; } item = &items[id++]; item->obj = ti->object; hints = item->hints; _efl_ui_container_layout_item_init(item->obj, hints); if (table_calc.layout_calc[0].fill || pd->homogeneoush) hints[0].weight = 1; else if (hints[0].weight < 0) hints[0].weight = 0; if (table_calc.layout_calc[1].fill || pd->homogeneousv) 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; int end; double ispace, iweight; end = ti->col + ti->col_span; ispace = hints[0].space / ti->col_span; iweight = hints[0].weight / ti->col_span; for (i = ti->col; i < end; i++) { table_calc.cell_calc[0][i].occupied = EINA_TRUE; if (table_calc.cell_calc[0][i].space < ispace) table_calc.cell_calc[0][i].space = ispace; if (table_calc.cell_calc[0][i].weight < iweight) table_calc.cell_calc[0][i].weight = iweight; } end = ti->row + ti->row_span; ispace = hints[1].space / ti->row_span; iweight = hints[1].weight / ti->row_span; for (i = ti->row; i < end; i++) { table_calc.cell_calc[1][i].occupied = EINA_TRUE; if (table_calc.cell_calc[1][i].space < ispace) table_calc.cell_calc[1][i].space = ispace; if (table_calc.cell_calc[1][i].weight < iweight) table_calc.cell_calc[1][i].weight = iweight; } } if (pd->homogeneoush) { _efl_ui_table_homogeneous_cell_init(&table_calc, 0); _efl_ui_table_item_pos_get[0] = _efl_ui_table_homogeneous_item_pos_get; _efl_ui_table_item_size_get[0] = _efl_ui_table_homogeneous_item_size_get; } else { _efl_ui_table_regular_cell_init(&table_calc, 0); _efl_ui_table_item_pos_get[0] = _efl_ui_table_regular_item_pos_get; _efl_ui_table_item_size_get[0] = _efl_ui_table_regular_item_size_get; } if (pd->homogeneousv) { _efl_ui_table_homogeneous_cell_init(&table_calc, 1); _efl_ui_table_item_pos_get[1] = _efl_ui_table_homogeneous_item_pos_get; _efl_ui_table_item_size_get[1] = _efl_ui_table_homogeneous_item_size_get; } else { _efl_ui_table_regular_cell_init(&table_calc, 1); _efl_ui_table_item_pos_get[1] = _efl_ui_table_regular_item_pos_get; _efl_ui_table_item_size_get[1] = _efl_ui_table_regular_item_size_get; } for (i = 0; i < count; i++) { Eina_Rect space, item_geom; item = &items[i]; hints = items[i].hints; space.x = _efl_ui_table_item_pos_get[0](&table_calc, item, 0); space.y = _efl_ui_table_item_pos_get[1](&table_calc, item, 1); space.w = _efl_ui_table_item_size_get[0](&table_calc, item, 0); space.h = _efl_ui_table_item_size_get[1](&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); pd->full_recalc = EINA_FALSE; efl_gfx_hint_size_restricted_min_set(ui_table, EINA_SIZE2D(table_calc.want[0], table_calc.want[1])); efl_event_callback_call(ui_table, EFL_PACK_EVENT_LAYOUT_UPDATED, NULL); if (do_free) free(items); }