efl/src/lib/elementary/efl_ui_box_flow.c

360 lines
10 KiB
C

#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"