#include "efl_ui_box_private.h"
-// FIXME: Aspect support is not implemented
// FIXME: handle RTL? just invert the horizontal order?
typedef struct _Item_Calc Item_Calc;
struct _Item_Calc
{
+ EINA_INLIST;
+
Evas_Object *obj;
double weight[2];
double align[2];
+ double space[2];
+ double comp_factor;
Eina_Bool fill[2];
- Eina_Size2D max, want, aspect;
+ Eina_Size2D max, min, aspect;
int pad[4];
Efl_Gfx_Size_Hint_Aspect aspect_type;
int id;
};
+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->comp_factor <= it1->comp_factor ? -1 : 1;
+}
+
+static inline void
+_min_max_calc(Item_Calc *item, int *cw, int *ch, Eina_Bool aspect_check)
+{
+ int w = *cw, h = *ch;
+
+ if (aspect_check)
+ {
+ w = h * item->aspect.w / item->aspect.h;
+ if (w > *cw)
+ {
+ w = *cw;
+ h = w * item->aspect.h / item->aspect.w;
+ }
+ }
+
+ if (w > item->max.w)
+ {
+ w = item->max.w;
+ if (aspect_check) h = w * item->aspect.h / item->aspect.w;
+ }
+ if (h > item->max.h)
+ {
+ h = item->max.h;
+ if (aspect_check) w = h * item->aspect.w / item->aspect.h;
+ }
+ if (w < item->min.w)
+ {
+ w = item->min.w;
+ if (aspect_check) h = w * item->aspect.h / item->aspect.w;
+ }
+ if (h < item->min.h)
+ {
+ h = item->min.h;
+ if (aspect_check) w = h * item->aspect.w / item->aspect.h;
+ }
+ *cw = w;
+ *ch = h;
+}
+
void
_efl_ui_box_custom_layout(Efl_Ui_Box *ui_box, Evas_Object_Box_Data *bd)
{
Evas_Object_Box_Option *opt;
Evas_Object *o;
Eina_List *li;
+ Eina_Inlist *inlist = NULL;
int wantw = 0, wanth = 0; // requested size
- int boxx, boxy, boxw, boxh;
+ Eina_Rect boxs;
Item_Calc *items, *item;
Eina_Bool horiz = efl_ui_dir_is_horizontal(pd->dir, EINA_FALSE);
- Eina_Bool zeroweight = EINA_FALSE;
int id = 0, count, boxl = 0, boxr = 0, boxt = 0, boxb = 0;
- int length, want, pad, extra = 0, rounding = 0;
- double cur_pos = 0, weight[2] = { 0, 0 }, scale;
+ int length, want, pad;
+ double cur_pos, weight[2] = { 0, 0 }, scale;
double box_align[2];
Eina_Bool box_fill[2] = { EINA_FALSE, EINA_FALSE };
- evas_object_geometry_get(ui_box, &boxx, &boxy, &boxw, &boxh);
+ boxs = efl_gfx_entity_geometry_get(ui_box);
efl_gfx_size_hint_margin_get(ui_box, &boxl, &boxr, &boxt, &boxb);
- scale = evas_object_scale_get(ui_box);
+ scale = efl_gfx_entity_scale_get(ui_box);
// Box align: used if "item has max size and fill" or "no item has a weight"
// Note: cells always expand on the orthogonal direction
count = eina_list_count(bd->children);
if (!count)
{
- evas_object_size_hint_min_set(ui_box, 0, 0);
+ efl_gfx_size_hint_min_set(ui_box, EINA_SIZE2D(0, 0));
return;
}
memset(items, 0, count * sizeof(*items));
#endif
+ // box outer margin
+ boxs.w -= boxl + boxr;
+ boxs.h -= boxt + boxb;
+ boxs.x += boxl;
+ boxs.y += boxt;
+
// scan all items, get their properties, calculate total weight & min size
EINA_LIST_FOREACH(bd->children, li, opt)
{
efl_gfx_size_hint_margin_get(o, &item->pad[0], &item->pad[1], &item->pad[2], &item->pad[3]);
efl_gfx_size_hint_fill_get(o, &item->fill[0], &item->fill[1]);
item->max = efl_gfx_size_hint_max_get(o);
- item->want = efl_gfx_size_hint_combined_min_get(o);
+ item->min = efl_gfx_size_hint_combined_min_get(o);
efl_gfx_size_hint_aspect_get(o, &item->aspect_type, &item->aspect);
- if (item->weight[0] < 0) item->weight[0] = 0;
- if (item->weight[1] < 0) item->weight[1] = 0;
+ if (horiz && box_fill[0]) item->weight[0] = 1;
+ else if (item->weight[0] < 0) item->weight[0] = 0;
+ if (!horiz && box_fill[1]) item->weight[1] = 1;
+ else if (item->weight[1] < 0) item->weight[1] = 0;
if (EINA_DBL_EQ(item->align[0], -1))
{
item->align[0] = 0.5;
item->fill[0] = EINA_TRUE;
}
- else if (item->align[0] < 0)
- {
- item->align[0] = 0;
- }
+ else if (item->align[0] < 0) item->align[0] = 0;
+ else if (item->align[0] > 1) item->align[0] = 1;
if (EINA_DBL_EQ(item->align[1], -1))
{
item->align[1] = 0.5;
item->fill[1] = EINA_TRUE;
}
- else if (item->align[1] < 0)
- {
- item->align[1] = 0;
- }
- if (item->align[0] > 1) item->align[0] = 1;
- if (item->align[1] > 1) item->align[1] = 1;
+ else if (item->align[1] < 0) item->align[1] = 0;
+ else if (item->align[1] > 1) item->align[1] = 1;
- if (item->want.w < 0) item->want.w = 0;
- if (item->want.h < 0) item->want.h = 0;
+ if (item->min.w < 0) item->min.w = 0;
+ if (item->min.h < 0) item->min.h = 0;
if (item->max.w < 0) item->max.w = INT_MAX;
if (item->max.h < 0) item->max.h = INT_MAX;
- if (item->aspect.w <= 0 || item->aspect.h <= 0)
- {
- if (item->aspect_type >= EFL_GFX_SIZE_HINT_ASPECT_HORIZONTAL)
- ERR("Invalid aspect parameter for obj: %p", item->obj);
- item->aspect.w = item->aspect.h = 0;
- item->aspect_type = EFL_GFX_SIZE_HINT_ASPECT_NONE;
- }
+ weight[0] += item->weight[0];
+ weight[1] += item->weight[1];
- if (item->aspect_type >= EFL_GFX_SIZE_HINT_ASPECT_HORIZONTAL)
+ if ((item->aspect.w <= 0) || (item->aspect.h <= 0))
{
- double w, h;
-
- w = item->want.w;
- h = w * item->aspect.h / item->aspect.w;
- if (h < item->want.h)
+ if ((item->aspect.w <= 0) ^ (item->aspect.h <= 0))
{
- h = item->want.h;
- w = h * item->aspect.w / item->aspect.h;
+ ERR("Invalid aspect parameter for obj: %p", item->obj);
+ item->aspect.w = item->aspect.h = 0;
+ item->aspect_type = EFL_GFX_SIZE_HINT_ASPECT_NONE;
}
-
- if (horiz)
- {
- if (item->fill[1] && (h < boxh))
- {
- double w1, h1;
- h1 = item->max.h > 0 ? MIN(boxh, item->max.h) : boxh;
- h1 = MAX(h, h1);
- w1 = h1 * item->aspect.w / item->aspect.h;
- w = item->max.w > 0 ? MIN(w1, item->max.w) : w1;
- }
- }
- else
- {
- if (item->fill[0] && (w < boxw))
- {
- double w1, h1;
- w1 = item->max.w > 0 ? MIN(boxw, item->max.w) : boxw;
- w1 = MAX(w, w1);
- h1 = w1 * item->aspect.h / item->aspect.w;
- h = item->max.h > 0 ? MIN(h1, item->max.h) : h1;
- }
- }
- item->want.w = w;
- item->want.h = h;
}
- if (item->max.w < item->want.w) item->max.w = item->want.w;
- if (item->max.h < item->want.h) item->max.h = item->want.h;
+ else
+ {
+ _min_max_calc(item, &item->min.w, &item->min.h, EINA_TRUE);
+ }
- item->want.w += item->pad[0] + item->pad[1];
- item->want.h += item->pad[2] + item->pad[3];
+ item->space[0] = item->min.w + item->pad[0] + item->pad[1];
+ item->space[1] = item->min.h + item->pad[2] + item->pad[3];
- weight[0] += item->weight[0];
- weight[1] += item->weight[1];
if (horiz)
{
- wantw += item->want.w;
- if (item->want.h > wanth)
- wanth = item->want.h;
+ if (item->space[1] > wanth)
+ wanth = item->space[1];
+ wantw += item->space[0];
}
else
{
- wanth += item->want.h;
- if (item->want.w > wantw)
- wantw = item->want.w;
+ if (item->space[0] > wantw)
+ wantw = item->space[0];
+ wanth += item->space[1];
}
item->id = id++;
}
- // box outer margin
- boxw -= boxl + boxr;
- boxh -= boxt + boxb;
- boxx += boxl;
- boxy += boxt;
-
// total space & available space
if (horiz)
{
- length = boxw;
want = wantw;
+ length = boxs.w;
pad = pd->pad.scalable ? (pd->pad.h * scale) : pd->pad.h;
+ if (boxs.h < wanth)
+ boxs.h = wanth;
}
else
{
- length = boxh;
want = wanth;
+ length = boxs.h;
pad = pd->pad.scalable ? (pd->pad.v * scale) : pd->pad.v;
+ if (boxs.w < wantw)
+ boxs.w = wantw;
}
// padding can not be squeezed (note: could make it an option)
length -= pad * (count - 1);
+ cur_pos = horiz ? boxs.x : boxs.y;
- // available space. if <0 we overflow
- extra = length - want;
-
- if (horiz)
- {
- evas_object_size_hint_min_set(ui_box,
- wantw + boxl + boxr + pad * (count - 1),
- wanth + boxt + boxb);
- }
- else
+ // calculate weight length
+ if ((length > want) && (weight[!horiz] > 0))
{
- evas_object_size_hint_min_set(ui_box,
- wantw + boxl + boxr,
- wanth + pad * (count - 1) + boxt + boxb);
- }
-
- if (extra < 0) extra = 0;
+ int orig_length = length;
+ double orig_weight = weight[!horiz];
- if (EINA_DBL_EQ(weight[!horiz], 0))
- {
- if (box_fill[!horiz])
+ for (id = 0; id < count; id++)
{
- // box is filled, set all weights to be equal
- zeroweight = EINA_TRUE;
- }
- else
- {
- // move bounding box according to box align
- cur_pos = extra * box_align[!horiz];
- }
- weight[!horiz] = count;
- }
-
- for (id = 0; id < count; id++)
- {
- double cx, cy, cw, ch, x, y, w, h;
- item = &items[id];
+ double denom;
+ item = &items[id];
- // extra rounding up (compensate cumulative error)
- if ((item->id == (count - 1)) && (cur_pos - floor(cur_pos) >= 0.5))
- rounding = 1;
-
- if (horiz)
- {
- cx = boxx + cur_pos;
- cy = boxy;
- cw = item->want.w + rounding + (zeroweight ? 1.0 : item->weight[0]) * extra / weight[0];
- ch = boxh;
- cur_pos += cw + pad;
- }
- else
- {
- cx = boxx;
- cy = boxy + cur_pos;
- cw = boxw;
- ch = item->want.h + rounding + (zeroweight ? 1.0 : item->weight[1]) * extra / weight[1];
- cur_pos += ch + pad;
- }
-
- if (item->aspect_type >= EFL_GFX_SIZE_HINT_ASPECT_HORIZONTAL)
- {
- if (horiz)
+ denom = (item->weight[!horiz] * orig_length) -
+ (orig_weight * item->space[!horiz]);
+ if (denom > 0)
{
- w = item->want.w;
- h = item->want.h;
- if (weight[0] > 0)
- w = item->want.w + extra * item->weight[0] / weight[0];
- h = w * item->aspect.h / item->aspect.w;
-
- if (item->aspect_type == EFL_GFX_SIZE_HINT_ASPECT_BOTH ||
- item->aspect_type == EFL_GFX_SIZE_HINT_ASPECT_VERTICAL)
- {
- if (h > boxh)
- {
- h = boxh;
- w = h * item->aspect.w / item->aspect.h;
- }
- }
+ item->comp_factor = (item->weight[!horiz] * orig_length) / denom;
+ inlist = eina_inlist_sorted_insert(inlist, EINA_INLIST_GET(item),
+ weight_sort_cb);
}
else
{
- w = item->want.w;
- h = item->want.h;
- if (weight[1] > 0)
- h = item->want.h + extra * item->weight[1] / weight[1];
- w = h * item->aspect.w / item->aspect.h;
-
- if (item->aspect_type == EFL_GFX_SIZE_HINT_ASPECT_BOTH ||
- item->aspect_type == EFL_GFX_SIZE_HINT_ASPECT_HORIZONTAL)
- {
- if (w > boxw)
- {
- w = boxw;
- h = w * item->aspect.h / item->aspect.w;
- }
- }
+ length -= item->space[!horiz];
+ weight[!horiz] -= item->weight[!horiz];
}
+ }
- if ((item->max.w != INT_MAX) && (item->max.h != INT_MAX))
- {
- double mar, ar;
- mar = item->max.w / (double)item->max.h;
- ar = item->aspect.w / (double)item->aspect.h;
- if (ar < mar)
- {
- if (h > item->max.h)
- {
- h = item->max.h;
- w = h * item->aspect.w / item->aspect.h;
- }
- }
- else
- {
- if (w > item->max.w)
- {
- w = item->max.w;
- h = w * item->aspect.h / item->aspect.w;
- }
- }
- }
- else if (item->max.w != INT_MAX)
+ EINA_INLIST_FOREACH(inlist, item)
+ {
+ double weight_len;
+
+ weight_len = (length * item->weight[!horiz]) / weight[!horiz];
+ if (item->space[!horiz] < weight_len)
{
- w = MIN(w, MAX(item->want.w, item->max.w));
- h = w * item->aspect.h / item->aspect.w;
+ item->space[!horiz] = weight_len;
}
else
{
- h = MIN(h, MAX(item->want.h, item->max.h));
- w = h * item->aspect.w / item->aspect.h;
+ weight[!horiz] -= item->weight[!horiz];
+ length -= item->space[!horiz];
}
- w = w - item->pad[0] - item->pad[1];
- h = h - item->pad[2] - item->pad[3];
+ }
+ }
- if (item->fill[0])
- x = cx + (cw - w) * 0.5 + item->pad[0];
- else
- x = cx + (cw - w) * item->align[0] + item->pad[0];
+ // calculate item geometry
+ {
+ int x, y, w, h, sw, sh;
- if (item->fill[1])
- y = cy + (ch - h) * 0.5 + item->pad[2];
- else
- y = cy + (ch - h) * item->align[1] + item->pad[2];
- }
- else
+ if ((length > want) && EINA_DBL_EQ(weight[!horiz], 0))
+ cur_pos += (length - want) * box_align[!horiz];
+
+ for (id = 0; id < count; id++)
{
- // horizontally
- if (item->max.w < INT_MAX)
- {
- w = MIN(MAX(item->want.w - item->pad[0] - item->pad[1], item->max.w), cw);
- x = cx + ((cw - w) * item->align[0]) + item->pad[0];
- }
- else if (item->fill[0])
- {
- // fill x
- w = cw - item->pad[0] - item->pad[1];
- x = cx + item->pad[0];
- }
+ item = &items[id];
+ item->space[horiz] = horiz ? boxs.h : boxs.w;
+ sw = item->space[0] - item->pad[0] - item->pad[1];
+ sh = item->space[1] - item->pad[2] - item->pad[3];
+
+ if ((item->weight[0] > 0) && item->fill[0])
+ w = sw;
else
- {
- if (horiz && item->weight[0] > 0)
- w = cw - item->pad[0] - item->pad[1];
- else
- w = item->want.w - item->pad[0] - item->pad[1];
- x = cx + ((cw - w) * item->align[0]) + item->pad[0];
- }
+ w = 0;
- // vertically
- if (item->max.h < INT_MAX)
- {
- h = MIN(MAX(item->want.h - item->pad[2] - item->pad[3], item->max.h), ch);
- y = cy + ((ch - h) * item->align[1]) + item->pad[2];
- }
- else if (item->fill[1])
+ if ((item->weight[1] > 0) && item->fill[1])
+ h = sh;
+ else
+ h = 0;
+
+ _min_max_calc(item, &w, &h, (item->aspect.w > 0) &&
+ (item->aspect.h > 0));
+ if (horiz)
{
- // fill y
- h = ch - item->pad[2] - item->pad[3];
- y = cy + item->pad[2];
+ x = cur_pos + 0.5;
+ y = boxs.y;
}
else
{
- if (!horiz && item->weight[1] > 0)
- h = ch - item->pad[2] - item->pad[3];
- else
- h = item->want.h - item->pad[2] - item->pad[3];
- y = cy + ((ch - h) * item->align[1]) + item->pad[2];
+ x = boxs.x;
+ y = cur_pos + 0.5;
}
+ x += item->pad[0] + ((sw - w) * item->align[0]);
+ y += item->pad[2] + ((sh - h) * item->align[1]);
+
+ cur_pos += item->space[!horiz] + pad;
+
+ efl_gfx_entity_geometry_set(item->obj, EINA_RECT(x, y, w, h));
}
+ }
- //DBG("[%2d/%2d] cell: %.0f,%.0f %.0fx%.0f item: %.0f,%.0f %.0fx%.0f",
- // id, count, cx, cy, cw, ch, x, y, w, h);
- evas_object_geometry_set(item->obj, x, y, w, h);
+ if (horiz)
+ {
+ efl_gfx_size_hint_min_set(ui_box, EINA_SIZE2D(
+ wantw + boxl + boxr + pad * (count - 1),
+ wanth + boxt + boxb));
+ }
+ else
+ {
+ efl_gfx_size_hint_min_set(ui_box, EINA_SIZE2D(
+ wantw + boxl + boxr,
+ wanth + pad * (count - 1) + boxt + boxb));
}
}