summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Blumenkrantz <zmike@osg.samsung.com>2015-12-31 15:47:51 -0500
committerMike Blumenkrantz <zmike@osg.samsung.com>2015-12-31 16:08:41 -0500
commitaef9d23550bbcd74829a5d75d5226ba394a9da87 (patch)
tree705ee4cfd30d1ec40e4936036ee4ec5f2961540d
parent0a34a4c6a4767c46baced6940c152363d4836d36 (diff)
downloadelementary-aef9d23550bbcd74829a5d75d5226ba394a9da87.tar.gz
box: implement aspected item layout
tl;dr: if you haven't used aspect hints on objects packed into boxes, this commit has no effect on anything you've done in some cases, using weight+align hints are not sufficient in order to do a simple box packing of objects. one example of this is when a box's size is not explicitly determined, such as if it is packed with EXPAND into another container and contains only objects with no min size hint, such as images. in this case, the box will determine that its min size is extremely small, resulting in nearly-hidden contents inside the box. using aspect hints, this outcome is noticeably different. the expanded/filled potential size of a box item is used to calculate the aspected size, and this size, if it fits within min/max constraints, is then used to calculate the min size of the box. as a more concrete example: * create box with maximum size 100x50 * pack 2x image into box - set VERTICAL aspect, EXPAND, FILL have 2x 50x50 images also I added some docs @feature
-rw-r--r--src/lib/els_box.c387
1 files changed, 300 insertions, 87 deletions
diff --git a/src/lib/els_box.c b/src/lib/els_box.c
index fdcd3bf0d..3c1a47141 100644
--- a/src/lib/els_box.c
+++ b/src/lib/els_box.c
@@ -7,27 +7,259 @@
#include "elm_priv.h"
#include "els_box.h"
+/* calculate an object's aspected size */
+static Eina_Bool
+_box_object_aspect_calc(int *ow, int *oh, int minw, int minh, int maxw, int maxh,
+ int fw /* fill width */, int fh /* fill height */,
+ int ww /* "maximum" width */, int hh /* "maximum" height */,
+ Evas_Aspect_Control aspect, double ratio)
+{
+ if (*ow < minw) *ow = minw;
+ if (*oh < minh) *oh = minh;
+
+ switch (aspect)
+ {
+ case EVAS_ASPECT_CONTROL_HORIZONTAL:
+ /* set height using aspect+width */
+ if (fw) *ow = ww;
+ if ((maxw >= 0) && (maxw < *ow)) *ow = maxw;
+ *oh = ratio / *ow;
+ /* apply min/max */
+ if ((maxh >= 0) && (maxh < *oh)) *oh = maxh;
+ else if ((minh >= 0) && (minh > *oh)) *oh = minh;
+ else return EINA_TRUE;
+ return EINA_FALSE;
+ case EVAS_ASPECT_CONTROL_VERTICAL:
+ /* set width using aspect+height */
+ if (fh) *oh = hh;
+ if ((maxh >= 0) && (maxh < *oh)) *oh = maxh;
+ *ow = ratio * *oh;
+ /* apply min/max */
+ if ((maxw >= 0) && (maxw < *ow)) *ow = maxw;
+ else if ((minw >= 0) && (minw > *ow)) *ow = minw;
+ else return EINA_TRUE;
+ return EINA_FALSE;
+ case EVAS_ASPECT_CONTROL_BOTH:
+ /* try width then height */
+
+ /* set width using aspect+height */
+ if (fh) *oh = hh;
+ if ((maxh >= 0) && (maxh < *oh)) *oh = maxh;
+ *ow = ratio * *oh;
+ /* apply min/max */
+ if ((maxw >= 0) && (maxw < *ow)) *ow = maxw;
+ else if ((minw >= 0) && (minw > *ow)) *ow = minw;
+ else return EINA_TRUE;
+ /* set height using aspect+width */
+ if (fw) *ow = ww;
+ if ((maxw >= 0) && (maxw < *ow)) *ow = maxw;
+ *oh = ratio / *ow;
+ /* apply min/max */
+ if ((maxh >= 0) && (maxh < *oh)) *oh = maxh;
+ else if ((minh >= 0) && (minh > *oh)) *oh = minh;
+ else return EINA_TRUE;
+ /* fallthrough on BOTH failure */
+ default: break;
+ }
+ /* on failure or ASPECT_NONE, use default size calc:
+ * - apply fill
+ * - apply max size constraints
+ */
+ if (fw) *ow = ww;
+ if ((maxw >= 0) && (maxw < *ow)) *ow = maxw;
+ if (fh) *oh = hh;
+ if ((maxh >= 0) && (maxh < *oh)) *oh = maxh;
+ return EINA_FALSE;
+}
+
+/* add box w/h padding to min/max totals */
+static void
+_smart_extents_padding_calc(Evas_Object_Box_Data *priv, int *minw, int *minh, int *maxw, int *maxh, Eina_Bool horizontal)
+{
+ int c;
+
+ if ((*maxw >= 0) && (*minw > *maxw)) *maxw = *minw;
+ if ((*maxh >= 0) && (*minh > *maxh)) *maxh = *minh;
+ c = eina_list_count(priv->children) - 1;
+ if (c > 0)
+ {
+ if (horizontal)
+ {
+ *minw += priv->pad.h * c;
+ if (*maxw != -1) *maxw += priv->pad.h * c;
+ }
+ else
+ {
+ *minh += priv->pad.v * c;
+ if (*maxh != -1) *maxh += priv->pad.v * c;
+ }
+ }
+}
+
+/* calculate extents for non-homogeneous layout;
+ * called twice if aspected items exist
+ */
+static Eina_Bool
+_smart_extents_non_homogeneous_calc(Evas_Object_Box_Data *priv, int w, int h, int *minw, int *minh, int *maxw, int *maxh, double expand, Eina_Bool horizontal, Eina_Bool do_aspect)
+{
+ const Eina_List *l;
+ Evas_Object_Box_Option *opt;
+ int mnw, mnh, mxw, mxh, cminw, cminh;
+ Evas_Coord *rw, *rh, *rxw, *rxh, *rminw, *rminh, *rmaxw, *rmaxh;
+ Eina_Bool max = EINA_TRUE, asp = EINA_FALSE;
+
+ cminw = *minw, cminh = *minh;
+ *minw = *minh = *maxw = *maxh = 0;
+ /* use pointers to values to simplify horizontal vs vertical calculations into
+ * a single algorithm for both orientations
+ */
+ if (!horizontal)
+ {
+ rw = &mnw;
+ rh = &mnh;
+ rxw = &mxw;
+ rxh = &mxh;
+ rminw = minw;
+ rminh = minh;
+ rmaxw = maxw;
+ rmaxh = maxh;
+ }
+ else
+ {
+ rw = &mnh;
+ rh = &mnw;
+ rxw = &mxh;
+ rxh = &mxw;
+ rminw = minh;
+ rminh = minw;
+ rmaxw = maxh;
+ rmaxh = maxw;
+ }
+ EINA_LIST_FOREACH(priv->children, l, opt)
+ {
+ Evas_Aspect_Control aspect = EVAS_ASPECT_CONTROL_NONE;
+ int asx, asy, ow = 0, oh = 0, *rrw, *rrh;
+
+ if (!horizontal)
+ rrw = &ow, rrh = &oh;
+ else
+ rrw = &oh, rrh = &ow;
+
+ evas_object_size_hint_min_get(opt->obj, &mnw, &mnh);
+ if (*rminw < *rw) *rminw = *rw;
+ *rminh += *rh;
+
+ evas_object_size_hint_aspect_get(opt->obj, &aspect, &asx, &asy);
+ if (aspect && ((asx < 1) || (asy < 1)))
+ {
+ aspect = EVAS_ASPECT_CONTROL_NONE;
+ ERR("Invalid aspect specified!");
+ }
+ /* return whether any aspected items exist */
+ asp |= !!aspect;
+
+ evas_object_size_hint_max_get(opt->obj, &mxw, &mxh);
+ if (*rxh < 0)
+ {
+ *rmaxh = -1;
+ max = EINA_FALSE;
+ }
+ if (max) *rmaxh += *rxh;
+
+ if (do_aspect && aspect)
+ {
+ int ww, hh, fw, fh;
+ double wx, wy, ax, ay;
+
+ evas_object_size_hint_weight_get(opt->obj, &wx, &wy);
+
+ if (horizontal)
+ {
+ /* use min size to start */
+ ww = *rw;
+ if ((expand > 0) && (wx > 0.0))
+ {
+ /* add remaining container value after applying weight hint */
+ ow = ((w - cminw) * wx) / expand;
+ ww += ow;
+ }
+ hh = h;
+ }
+ else
+ {
+ hh = *rh;
+ if ((expand > 0) && (wy > 0.0))
+ {
+ oh = ((h - cminh) * wy) / expand;
+ hh += oh;
+ }
+ ww = w;
+ }
+ evas_object_size_hint_align_get(opt->obj, &ax, &ay);
+ if (ax < 0) fw = 1;
+ if (ay < 0) fh = 1;
+
+ /* if aspecting succeeds, use aspected size for min size */
+ if (_box_object_aspect_calc(rrw, rrh, mnw, mnh, mxw, mxh,
+ fw, fh, ww, hh, aspect, asx / (double)asy))
+ *rminh += (*rrh - *rh);
+ }
+ if (*rxw >= 0)
+ {
+ if (*rmaxw == -1) *rmaxw = *rxw;
+ else if (*rmaxw > *rxw) *rmaxw = *rxw;
+ }
+ }
+ return asp;
+}
+
static void
-_smart_extents_calculate(Evas_Object *box, Evas_Object_Box_Data *priv, Eina_Bool horizontal, Eina_Bool homogeneous)
+_smart_extents_calculate(Evas_Object *box, Evas_Object_Box_Data *priv, int w, int h, double expand, Eina_Bool horizontal, Eina_Bool homogeneous)
{
Evas_Coord minw, minh, mnw, mnh, maxw, maxh;
const Eina_List *l;
Evas_Object_Box_Option *opt;
- Eina_Bool max = EINA_TRUE;
int c;
minw = 0;
minh = 0;
maxw = -1;
maxh = -1;
+ c = eina_list_count(priv->children);
if (homogeneous)
{
+ Evas_Aspect_Control paspect = -1; //causes overflow
+ int pasx = -1, pasy = -1;
+
EINA_LIST_FOREACH(priv->children, l, opt)
{
+ Evas_Aspect_Control aspect = EVAS_ASPECT_CONTROL_NONE;
+ int asx, asy, ow = 0, oh = 0, fw, fh, ww, hh;
+ double ax, ay;
+
+ evas_object_size_hint_align_get(opt->obj, &ax, &ay);
+ if (ax < 0) fw = 1;
+ if (ay < 0) fh = 1;
+
evas_object_size_hint_min_get(opt->obj, &mnw, &mnh);
if (minh < mnh) minh = mnh;
if (minw < mnw) minw = mnw;
+ evas_object_size_hint_aspect_get(opt->obj, &aspect, &asx, &asy);
+ if (aspect && ((asx < 1) || (asy < 1)))
+ {
+ aspect = EVAS_ASPECT_CONTROL_NONE;
+ ERR("Invalid aspect specified!");
+ }
+ if (paspect < 100) //value starts overflowed as UINT_MAX
+ {
+ /* this condition can cause some items to not be the same size,
+ * resulting in a non-homogeneous homogeneous layout
+ */
+ if ((aspect != paspect) || (asx != pasx) || (asy != pasy))
+ ERR("Homogeneous box with differently-aspected items!");
+ }
+
evas_object_size_hint_max_get(opt->obj, &mnw, &mnh);
if (mnh >= 0)
{
@@ -39,81 +271,57 @@ _smart_extents_calculate(Evas_Object *box, Evas_Object_Box_Data *priv, Eina_Bool
if (maxw == -1) maxw = mnw;
else if (maxw > mnw) maxw = mnw;
}
+ if (aspect)
+ {
+ if (horizontal)
+ {
+ ww = ((w - (c - 1) * priv->pad.h) / c);
+ hh = h;
+ }
+ else
+ {
+ hh = ((h - (c - 1) * priv->pad.v) / c);
+ ww = w;
+ }
+ if (_box_object_aspect_calc(&ow, &oh, mnw, mnh, maxw, maxh,
+ fw, fh, ww, hh, aspect, asx / (double)asy))
+ {
+ if ((oh > mnh) && (minh < oh)) minh = oh;
+ if ((ow > mnw) && (minw < ow)) minw = ow;
+ }
+ }
+
+ paspect = aspect;
+ pasx = asx, pasy = asy;
}
if (horizontal)
{
- minw *= eina_list_count(priv->children);
+ minw *= c;
if (maxw != -1)
- maxw *= eina_list_count(priv->children);
+ maxw *= c;
else maxw = -1;
}
else
{
- minh *= eina_list_count(priv->children);
+ minh *= c;
if (maxh != -1)
- maxh *= eina_list_count(priv->children);
+ maxh *= c;
else maxh = -1;
}
}
else
{
- /* calculate but after switched w and h for horizontal mode */
- Evas_Coord *rw, *rh, *rminw, *rminh, *rmaxw, *rmaxh;
- if (!horizontal)
- {
- rw = &mnw;
- rh = &mnh;
- rminw = &minw;
- rminh = &minh;
- rmaxw = &maxw;
- rmaxh = &maxh;
- }
- else
+ /* returns true if at least one item has aspect hint */
+ if (_smart_extents_non_homogeneous_calc(priv, w, h, &minw, &minh, &maxw, &maxh, expand, horizontal, 0))
{
- rw = &mnh;
- rh = &mnw;
- rminw = &minh;
- rminh = &minw;
- rmaxw = &maxh;
- rmaxh = &maxw;
- }
- EINA_LIST_FOREACH(priv->children, l, opt)
- {
- evas_object_size_hint_min_get(opt->obj, &mnw, &mnh);
- if (*rminw < *rw) *rminw = *rw;
- *rminh += *rh;
-
- evas_object_size_hint_max_get(opt->obj, &mnw, &mnh);
- if (*rh < 0)
- {
- *rmaxh = -1;
- max = EINA_FALSE;
- }
- if (max) *rmaxh += *rh;
-
- if (*rw >= 0)
- {
- if (*rmaxw == -1) *rmaxw = *rw;
- else if (*rmaxw > *rw) *rmaxw = *rw;
- }
- }
- }
- if ((maxw >= 0) && (minw > maxw)) maxw = minw;
- if ((maxh >= 0) && (minh > maxh)) maxh = minh;
- c = eina_list_count(priv->children) - 1;
- if (c > 0)
- {
- if (horizontal)
- {
- minw += priv->pad.h * c;
- if (maxw != -1) maxw += priv->pad.h * c;
- }
- else
- {
- minh += priv->pad.v * c;
- if (maxh != -1) maxh += priv->pad.v * c;
+ /* aspect can only be accurately calculated after the full (non-aspected) min size of the box has
+ * been calculated due to the use of this min size during aspect calculations
+ */
+ _smart_extents_padding_calc(priv, &minw, &minh, &maxw, &maxh, horizontal);
+ _smart_extents_non_homogeneous_calc(priv, w, h, &minw, &minh, &maxw, &maxh, expand, horizontal, 1);
}
}
+ _smart_extents_padding_calc(priv, &minw, &minh, &maxw, &maxh, horizontal);
evas_object_size_hint_min_set(box, minw, minh);
evas_object_size_hint_max_set(box, maxw, maxh);
}
@@ -132,12 +340,22 @@ _els_box_layout(Evas_Object *o, Evas_Object_Box_Data *priv, Eina_Bool horizontal
double *rwy;
Evas_Object_Box_Option *opt;
- _smart_extents_calculate(o, priv, horizontal, homogeneous);
-
evas_object_geometry_get(o, &x, &y, &w, &h);
+ /* accummulate expand after switched x and y for horizontal mode */
+ if (!horizontal)
+ rwy = &wy;
+ else
+ rwy = &wx;
+ EINA_LIST_FOREACH(priv->children, l, opt)
+ {
+ evas_object_size_hint_weight_get(opt->obj, &wx, &wy);
+ if (*rwy > 0.0) expand += *rwy;
+ }
+ _smart_extents_calculate(o, priv, w, h, expand, horizontal, homogeneous);
evas_object_size_hint_min_get(o, &minw, &minh);
evas_object_box_align_get(o, &ax, &ay);
+ /* if object size is less than min, apply align to trigger viewporting */
if (w < minw)
{
x = x + ((w - minw) * (1.0 - ax));
@@ -150,16 +368,6 @@ _els_box_layout(Evas_Object *o, Evas_Object_Box_Data *priv, Eina_Bool horizontal
}
count = eina_list_count(priv->children);
- /* accummulate expand as same way but after switched x and y for horizontal mode */
- if (!horizontal)
- rwy = &wy;
- else
- rwy = &wx;
- EINA_LIST_FOREACH(priv->children, l, opt)
- {
- evas_object_size_hint_weight_get(opt->obj, &wx, &wy);
- if (*rwy > 0.0) expand += *rwy;
- }
if (!expand)
{
if (rtl) ax = 1.0 - ax;
@@ -179,15 +387,21 @@ _els_box_layout(Evas_Object *o, Evas_Object_Box_Data *priv, Eina_Bool horizontal
EINA_LIST_FOREACH(priv->children, l, opt)
{
Evas_Coord mnw, mnh, mxw, mxh;
- int fw, fh, xw, xh;
+ int fw, fh, xw, xh;//fillw, fillw, expandw, expandh
+ Evas_Aspect_Control aspect = EVAS_ASPECT_CONTROL_NONE;
+ int asx, asy;
obj = opt->obj;
evas_object_size_hint_align_get(obj, &ax, &ay);
evas_object_size_hint_weight_get(obj, &wx, &wy);
evas_object_size_hint_min_get(obj, &mnw, &mnh);
evas_object_size_hint_max_get(obj, &mxw, &mxh);
+ evas_object_size_hint_aspect_get(obj, &aspect, &asx, &asy);
+ if (aspect && ((asx < 1) || (asy < 1)))
+ aspect = EVAS_ASPECT_CONTROL_NONE;
fw = fh = 0;
xw = xh = 0;
+ /* align(-1) means fill to maximum apportioned size */
if (ax == -1.0) {fw = 1; ax = 0.5;}
if (ay == -1.0) {fh = 1; ay = 0.5;}
if (rtl) ax = 1.0 - ax;
@@ -195,7 +409,7 @@ _els_box_layout(Evas_Object *o, Evas_Object_Box_Data *priv, Eina_Bool horizontal
if (wy > 0.0) xh = 1;
if (horizontal)
{
- Evas_Coord ww, hh, ow, oh;
+ Evas_Coord ww, hh, ow = 0, oh = 0;
if (homogeneous)
{
@@ -211,12 +425,13 @@ _els_box_layout(Evas_Object *o, Evas_Object_Box_Data *priv, Eina_Bool horizontal
}
}
hh = h;
- ow = mnw;
- if (fw) ow = ww;
- if ((mxw >= 0) && (mxw < ow)) ow = mxw;
- oh = mnh;
- if (fh) oh = hh;
- if ((mxh >= 0) && (mxh < oh)) oh = mxh;
+
+ _box_object_aspect_calc(&ow, &oh, mnw, mnh, mxw, mxh, fw, fh, ww, hh, aspect, asx / (double)asy);
+ /* non-homogeneous, aspected, expending items are calculated based on object size
+ * during extents calc, so use this for positioning during layout as well
+ */
+ if (xw && aspect && (!homogeneous))
+ ww = ow;
evas_object_move(obj,
((!rtl) ? (xx) : (x + (w - (xx - x) - ww)))
+ (Evas_Coord)(((double)(ww - ow)) * ax),
@@ -227,7 +442,7 @@ _els_box_layout(Evas_Object *o, Evas_Object_Box_Data *priv, Eina_Bool horizontal
}
else
{
- Evas_Coord ww, hh, ow, oh;
+ Evas_Coord ww, hh, ow = 0, oh = 0;
if (homogeneous)
{
@@ -243,12 +458,10 @@ _els_box_layout(Evas_Object *o, Evas_Object_Box_Data *priv, Eina_Bool horizontal
}
}
ww = w;
- ow = mnw;
- if (fw) ow = ww;
- if ((mxw >= 0) && (mxw < ow)) ow = mxw;
- oh = mnh;
- if (fh) oh = hh;
- if ((mxh >= 0) && (mxh < oh)) oh = mxh;
+
+ _box_object_aspect_calc(&ow, &oh, mnw, mnh, mxw, mxh, fw, fh, ww, hh, aspect, asx / (double)asy);
+ if (xh && aspect && (!homogeneous))
+ hh = oh;
evas_object_move(obj,
xx + (Evas_Coord)(((double)(ww - ow)) * ax),
yy + (Evas_Coord)(((double)(hh - oh)) * ay));