forked from enlightenment/efl
376 lines
12 KiB
C
376 lines
12 KiB
C
#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 (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];
|
|
}
|
|
|
|
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;
|
|
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;
|
|
|
|
if (!pd->count)
|
|
{
|
|
efl_gfx_hint_size_restricted_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 || 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;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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 < pd->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);
|
|
|
|
efl_gfx_hint_size_restricted_min_set(ui_table,
|
|
EINA_SIZE2D(table_calc.want[0],
|
|
table_calc.want[1]));
|
|
}
|