efl/src/lib/elementary/efl_ui_box_layout.c

244 lines
7.4 KiB
C

#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?
typedef struct _Item_Calc Item_Calc;
struct _Item_Calc
{
EINA_INLIST;
Evas_Object *obj;
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)
{
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;
}
/* this function performs a simplified layout when the box has changed position
* but no other changes have occurred, e.g., when a box is being scrolled
*/
static void
_efl_ui_box_layout_simple(Efl_Ui_Box *ui_box, Efl_Ui_Box_Data *pd)
{
Eo *child;
Eina_List *l;
Eina_Position2D pos = efl_gfx_entity_position_get(ui_box);
EINA_LIST_FOREACH(pd->children, l, child)
{
Eina_Position2D child_pos = efl_gfx_entity_position_get(child);
efl_gfx_entity_position_set(child,
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_box, EFL_PACK_EVENT_LAYOUT_UPDATED, NULL);
}
void
_efl_ui_box_custom_layout(Efl_Ui_Box *ui_box, Efl_Ui_Box_Data *pd)
{
Eo *child;
Eina_List *li;
Eina_Inlist *inlist = NULL;
Item_Calc *items, *item;
Efl_Ui_Container_Item_Hints *hints, *hint;
Eina_Bool axis = !efl_ui_layout_orientation_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 */
count = eina_list_count(pd->children);
if (!count)
{
efl_gfx_hint_size_restricted_min_set(ui_box, EINA_SIZE2D(0, 0));
return;
}
if (!pd->full_recalc)
{
_efl_ui_box_layout_simple(ui_box, pd);
return;
}
_efl_ui_container_layout_init(ui_box, box_calc);
pd->last_pos.x = box_calc[0].pos - box_calc[0].margin[0];
pd->last_pos.y = box_calc[1].pos - box_calc[1].margin[0];
/* 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
*/
if (count >= 500)
{
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
// scan all items, get their properties, calculate total weight & min size
EINA_LIST_FOREACH(pd->children, li, child)
{
item = &items[i++];
item->obj = child;
hints = item->hints;
_efl_ui_container_layout_item_init(item->obj, hints);
if (pd->homogeneous || box_calc[0].fill)
hints[0].weight = 1;
else if (hints[0].weight < 0)
hints[0].weight = 0;
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)
{
if (hints[axis].space > mmin)
mmin = hints[axis].space;
}
else
{
want[axis] += hints[axis].space;
}
}
// total space & available space
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)
box_calc[axis].size -= (box_calc[axis].pad * (count - 1));
box_calc[r_axis].pad = 0;
cur_pos = box_calc[axis].pos;
// calculate weight size
if (!pd->homogeneous && (box_calc[axis].size > want[axis]) && (weight_sum > 0))
{
int orig_size, calc_size;
double orig_weight = weight_sum;
calc_size = orig_size = box_calc[axis].size;
for (i = 0; i < count; i++)
{
double denom;
hint = &items[i].hints[axis];
denom = (hint->weight * orig_size) - (orig_weight * hint->space);
if (denom > 0)
{
items[i].weight_factor = (hint->weight * orig_size) / denom;
inlist = eina_inlist_sorted_insert(inlist, EINA_INLIST_GET(&items[i]),
_weight_sort_cb);
}
else
{
calc_size -= hint->space;
weight_sum -= hint->weight;
}
}
EINA_INLIST_FOREACH(inlist, item)
{
double weight_len;
hint = &item->hints[axis];
weight_len = (calc_size * hint->weight) / weight_sum;
if (hint->space < weight_len)
{
hint->space = weight_len;
}
else
{
weight_sum -= hint->weight;
calc_size -= hint->space;
}
}
}
// calculate item geometry
{
int item_size[2], item_pos[2], sw, sh;
if (box_calc[axis].size > want[axis])
{
if (pd->homogeneous)
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 (i = 0; i < count; i++)
{
hints = items[i].hints;
if (pd->homogeneous)
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]);
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] = cur_pos + 0.5;
item_pos[r_axis] = box_calc[r_axis].pos;
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));
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));
pd->full_recalc = EINA_FALSE;
efl_gfx_hint_size_restricted_min_set(ui_box, EINA_SIZE2D(want[0], want[1]));
efl_event_callback_call(ui_box, EFL_PACK_EVENT_LAYOUT_UPDATED, NULL);
if (count >= 500) free(items);
}