1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4
5 #include <Elementary.h>
6
7 #include "elm_priv.h"
8 #include "els_box.h"
9
10 /* calculate an object's aspected size */
11 static Eina_Bool
_box_object_aspect_calc(int * ow,int * oh,int minw,int minh,int maxw,int maxh,int fw,int fh,int ww,int hh,Evas_Aspect_Control aspect,double ratio)12 _box_object_aspect_calc(int *ow, int *oh, int minw, int minh, int maxw, int maxh,
13 int fw /* fill width */, int fh /* fill height */,
14 int ww /* "maximum" width */, int hh /* "maximum" height */,
15 Evas_Aspect_Control aspect, double ratio)
16 {
17 *ow = minw;
18 *oh = minh;
19
20 switch (aspect)
21 {
22 case EVAS_ASPECT_CONTROL_HORIZONTAL:
23 /* set height using aspect+width */
24 if (fw) *ow = ww;
25 if ((maxw >= 0) && (maxw < *ow)) *ow = maxw;
26 *oh = (1 / ratio) * *ow;
27 /* apply min/max */
28 if ((maxh >= 0) && (maxh < *oh)) *oh = maxh;
29 else if ((minh >= 0) && (minh > *oh)) *oh = minh;
30 else return EINA_TRUE;
31 return EINA_FALSE;
32 case EVAS_ASPECT_CONTROL_VERTICAL:
33 /* set width using aspect+height */
34 if (fh) *oh = hh;
35 if ((maxh >= 0) && (maxh < *oh)) *oh = maxh;
36 *ow = ratio * *oh;
37 /* apply min/max */
38 if ((maxw >= 0) && (maxw < *ow)) *ow = maxw;
39 else if ((minw >= 0) && (minw > *ow)) *ow = minw;
40 else return EINA_TRUE;
41 return EINA_FALSE;
42 case EVAS_ASPECT_CONTROL_BOTH:
43 /* try width then height */
44
45 /* set width using aspect+height */
46 if (fh) *oh = hh;
47 if ((maxh >= 0) && (maxh < *oh)) *oh = maxh;
48 *ow = ratio * *oh;
49 /* apply min/max */
50 if ((maxw >= 0) && (maxw < *ow)) *ow = maxw;
51 else if ((minw >= 0) && (minw > *ow)) *ow = minw;
52 else return EINA_TRUE;
53 /* set height using aspect+width */
54 if (fw) *ow = ww;
55 if ((maxw >= 0) && (maxw < *ow)) *ow = maxw;
56 *oh = (1 / ratio) * *ow;
57 /* apply min/max */
58 if ((maxh >= 0) && (maxh < *oh)) *oh = maxh;
59 else if ((minh >= 0) && (minh > *oh)) *oh = minh;
60 else return EINA_TRUE;
61 /* fallthrough on BOTH failure */
62 default: break;
63 }
64 /* on failure or ASPECT_NONE, use default size calc:
65 * - apply fill
66 * - apply max size constraints
67 */
68 if (fw) *ow = ww;
69 if ((maxw >= 0) && (maxw < *ow)) *ow = maxw;
70 if (fh) *oh = hh;
71 if ((maxh >= 0) && (maxh < *oh)) *oh = maxh;
72 return EINA_FALSE;
73 }
74
75 /* add box w/h padding to min/max totals */
76 static void
_smart_extents_padding_calc(Evas_Object_Box_Data * priv,int * minw,int * minh,int * maxw,int * maxh,Eina_Bool horizontal)77 _smart_extents_padding_calc(Evas_Object_Box_Data *priv, int *minw, int *minh, int *maxw, int *maxh, Eina_Bool horizontal)
78 {
79 int c;
80
81 if ((*maxw >= 0) && (*minw > *maxw)) *maxw = *minw;
82 if ((*maxh >= 0) && (*minh > *maxh)) *maxh = *minh;
83 c = eina_list_count(priv->children) - 1;
84 if (c > 0)
85 {
86 if (horizontal)
87 {
88 *minw += priv->pad.h * c;
89 if (*maxw != -1) *maxw += priv->pad.h * c;
90 }
91 else
92 {
93 *minh += priv->pad.v * c;
94 if (*maxh != -1) *maxh += priv->pad.v * c;
95 }
96 }
97 }
98
99 /* calculate extents for non-homogeneous layout;
100 * called twice if aspected items exist
101 */
102 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)103 _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)
104 {
105 const Eina_List *l;
106 Evas_Object_Box_Option *opt;
107 int mnw, mnh, mxw, mxh, cminw, cminh;
108 Evas_Coord pad_l, pad_r, pad_t, pad_b;
109 Evas_Coord *rw, *rh, *rxw, *rxh, *rminw, *rminh, *rmaxw, *rmaxh;
110 Eina_Bool max = EINA_TRUE, asp = EINA_FALSE;
111
112 cminw = *minw, cminh = *minh;
113 *minw = *minh = 0;
114 *maxw = *maxh = -1;
115 /* use pointers to values to simplify horizontal vs vertical calculations into
116 * a single algorithm for both orientations
117 */
118 if (!horizontal)
119 {
120 rw = &mnw;
121 rh = &mnh;
122 rxw = &mxw;
123 rxh = &mxh;
124 rminw = minw;
125 rminh = minh;
126 rmaxw = maxw;
127 rmaxh = maxh;
128 }
129 else
130 {
131 rw = &mnh;
132 rh = &mnw;
133 rxw = &mxh;
134 rxh = &mxw;
135 rminw = minh;
136 rminh = minw;
137 rmaxw = maxh;
138 rmaxh = maxw;
139 }
140 EINA_LIST_FOREACH(priv->children, l, opt)
141 {
142 Evas_Aspect_Control aspect = EVAS_ASPECT_CONTROL_NONE;
143 int asx, asy, ow = 0, oh = 0, *rrw, *rrh;
144
145 if (!horizontal)
146 rrw = &ow, rrh = &oh;
147 else
148 rrw = &oh, rrh = &ow;
149
150 evas_object_size_hint_padding_get(opt->obj, &pad_l, &pad_r, &pad_t, &pad_b);
151 evas_object_size_hint_combined_min_get(opt->obj, &mnw, &mnh);
152 mnw += pad_l + pad_r;
153 mnh += pad_t + pad_b;
154 if (*rminw < *rw) *rminw = *rw;
155 *rminh += *rh;
156
157 evas_object_size_hint_aspect_get(opt->obj, &aspect, &asx, &asy);
158 if (aspect && ((!EINA_DBL_NONZERO(asx)) || (asx < 0.0) ||
159 (!EINA_DBL_NONZERO(asy)) || (asy < 0.0)))
160 {
161 aspect = EVAS_ASPECT_CONTROL_NONE;
162 ERR("Invalid aspect specified!");
163 }
164 /* return whether any aspected items exist */
165 asp |= !!aspect;
166
167 evas_object_size_hint_max_get(opt->obj, &mxw, &mxh);
168 if (mxw >= 0) mxw += pad_l + pad_r;
169 if (mxh >= 0) mxh += pad_t + pad_b;
170 if (*rxh < 0)
171 {
172 *rmaxh = -1;
173 max = EINA_FALSE;
174 }
175 if (max) *rmaxh += *rxh;
176
177 if (do_aspect && aspect)
178 {
179 int ww, hh, fw = 0, fh = 0;
180 double wx, wy, ax, ay;
181
182 evas_object_size_hint_weight_get(opt->obj, &wx, &wy);
183
184 if (horizontal)
185 {
186 /* use min size to start */
187 ww = mnw;
188 if ((expand > 0) && (wx > 0.0))
189 {
190 /* add remaining container value after applying weight hint */
191 ow = ((w - cminw) * wx) / expand;
192 ww += ow;
193 }
194 hh = h;
195 }
196 else
197 {
198 hh = mnh;
199 if ((expand > 0) && (wy > 0.0))
200 {
201 oh = ((h - cminh) * wy) / expand;
202 hh += oh;
203 }
204 ww = w;
205 }
206 evas_object_size_hint_align_get(opt->obj, &ax, &ay);
207 if (ax < 0) fw = 1;
208 if (ay < 0) fh = 1;
209
210 /* if aspecting succeeds, use aspected size for min size */
211 if (_box_object_aspect_calc(&ow, &oh, mnw, mnh, mxw, mxh,
212 fw, fh, ww, hh, aspect, asx / (double)asy))
213 {
214 *rminh += (*rrh - *rh);
215 if (*rminw < *rrw) *rminw = *rrw;
216 }
217 }
218 if (*rxw >= 0)
219 {
220 if (*rmaxw == -1) *rmaxw = *rxw;
221 else if (*rmaxw > *rxw) *rmaxw = *rxw;
222 }
223 }
224 return asp;
225 }
226
227 static void
_smart_extents_calculate(Evas_Object * box,Evas_Object_Box_Data * priv,int w,int h,double expand,Eina_Bool horizontal,Eina_Bool homogeneous)228 _smart_extents_calculate(Evas_Object *box, Evas_Object_Box_Data *priv, int w, int h, double expand, Eina_Bool horizontal, Eina_Bool homogeneous)
229 {
230 Evas_Coord minw, minh, child_mxw, child_mxh, maxw, maxh;
231 Evas_Coord pad_l, pad_r, pad_t, pad_b;
232 const Eina_List *l;
233 Evas_Object_Box_Option *opt;
234 int c;
235
236 minw = 0;
237 minh = 0;
238 maxw = -1;
239 maxh = -1;
240 c = eina_list_count(priv->children);
241 if (homogeneous || (c == 1))
242 {
243 Evas_Aspect_Control paspect = -1; //causes overflow
244 int pasx = -1, pasy = -1;
245
246 EINA_LIST_FOREACH(priv->children, l, opt)
247 {
248 Evas_Aspect_Control aspect = EVAS_ASPECT_CONTROL_NONE;
249 int asx, asy, ow = 0, oh = 0, fw = 0, fh = 0, ww, hh;
250 double ax, ay;
251
252 evas_object_size_hint_align_get(opt->obj, &ax, &ay);
253 if (ax < 0) fw = 1;
254 if (ay < 0) fh = 1;
255
256 evas_object_size_hint_padding_get(opt->obj, &pad_l, &pad_r, &pad_t, &pad_b);
257 evas_object_size_hint_combined_min_get(opt->obj, &child_mxw, &child_mxh);
258 child_mxw += pad_l + pad_r;
259 child_mxh += pad_t + pad_b;
260 if (minh < child_mxh) minh = child_mxh;
261 if (minw < child_mxw) minw = child_mxw;
262
263 evas_object_size_hint_aspect_get(opt->obj, &aspect, &asx, &asy);
264 if (aspect && ((asx < 1) || (asy < 1)))
265 {
266 aspect = EVAS_ASPECT_CONTROL_NONE;
267 ERR("Invalid aspect specified!");
268 }
269 if ((unsigned) paspect < 100) //value starts overflowed as UINT_MAX
270 {
271 /* this condition can cause some items to not be the same size,
272 * resulting in a non-homogeneous homogeneous layout
273 */
274 if ((aspect != paspect) || (asx != pasx) || (asy != pasy))
275 ERR("Homogeneous box with differently-aspected items!");
276 }
277
278 evas_object_size_hint_max_get(opt->obj, &child_mxw, &child_mxh);
279 if (child_mxh >= 0)
280 {
281 child_mxh += pad_t + pad_b;
282 if (maxh == -1) maxh = child_mxh;
283 else if (maxh > child_mxh) maxh = child_mxh;
284 }
285 if (child_mxw >= 0)
286 {
287 child_mxw += pad_l + pad_r;
288 if (maxw == -1) maxw = child_mxw;
289 else if (maxw > child_mxw) maxw = child_mxw;
290 }
291 if (aspect)
292 {
293 if (horizontal)
294 {
295 ww = ((w - (c - 1) * priv->pad.h) / c);
296 hh = h;
297 }
298 else
299 {
300 hh = ((h - (c - 1) * priv->pad.v) / c);
301 ww = w;
302 }
303 if (_box_object_aspect_calc(&ow, &oh, child_mxw, child_mxh, maxw, maxh,
304 fw, fh, ww, hh, aspect, asx / (double)asy))
305 {
306 if ((oh > child_mxh) && (minh < oh)) minh = oh;
307 if ((ow > child_mxw) && (minw < ow)) minw = ow;
308 }
309 }
310
311 paspect = aspect;
312 pasx = asx, pasy = asy;
313 }
314 if (horizontal)
315 {
316 minw *= c;
317 if (maxw != -1)
318 maxw *= c;
319 else maxw = -1;
320 }
321 else
322 {
323 minh *= c;
324 if (maxh != -1)
325 maxh *= c;
326 else maxh = -1;
327 }
328 }
329 else
330 {
331 /* returns true if at least one item has aspect hint */
332 if (_smart_extents_non_homogeneous_calc(priv, w, h, &minw, &minh,
333 &maxw, &maxh, expand,
334 horizontal, EINA_FALSE))
335 {
336 /* aspect can only be accurately calculated after the full (non-aspected) min size of the box has
337 * been calculated due to the use of this min size during aspect calculations
338 */
339 int aminw = minw;
340 int aminh = minh;
341 _smart_extents_padding_calc(priv, &minw, &minh, &maxw, &maxh,
342 horizontal);
343 _smart_extents_non_homogeneous_calc(priv, w, h, &aminw, &aminh,
344 &maxw, &maxh, expand,
345 horizontal, EINA_TRUE);
346 if (horizontal) minw = aminw;
347 else minh = aminh;
348 }
349 }
350 _smart_extents_padding_calc(priv, &minw, &minh, &maxw, &maxh, horizontal);
351 evas_object_size_hint_min_set(box, minw, minh);
352 evas_object_size_hint_max_set(box, maxw, maxh);
353 }
354
355 void
_els_box_layout(Evas_Object * o,Evas_Object_Box_Data * priv,Eina_Bool horizontal,Eina_Bool homogeneous,Eina_Bool rtl)356 _els_box_layout(Evas_Object *o, Evas_Object_Box_Data *priv, Eina_Bool horizontal, Eina_Bool homogeneous, Eina_Bool rtl)
357 {
358 Evas_Coord x, y, w, h, xx, yy;
359 const Eina_List *l;
360 Evas_Object *obj;
361 Evas_Coord minw, minh;
362 int count = 0;
363 double expand = 0.0;
364 double ax, ay;
365 double wx, wy;
366 double *rwy;
367 Evas_Object_Box_Option *opt;
368
369 evas_object_geometry_get(o, &x, &y, &w, &h);
370 /* accummulate expand after switched x and y for horizontal mode */
371 if (!horizontal)
372 rwy = &wy;
373 else
374 rwy = &wx;
375 EINA_LIST_FOREACH(priv->children, l, opt)
376 {
377 evas_object_size_hint_weight_get(opt->obj, &wx, &wy);
378 if (*rwy > 0.0) expand += *rwy;
379 }
380 _smart_extents_calculate(o, priv, w, h, expand, horizontal, homogeneous);
381 evas_object_geometry_get(o, &x, &y, &w, &h);
382
383 evas_object_size_hint_combined_min_get(o, &minw, &minh);
384 evas_object_box_align_get(o, &ax, &ay);
385 /* if object size is less than min, apply align to trigger viewporting */
386 if (w < minw)
387 {
388 x = x + ((w - minw) * (1.0 - ax));
389 w = minw;
390 }
391 if (h < minh)
392 {
393 y = y + ((h - minh) * (1.0 - ay));
394 h = minh;
395 }
396 count = eina_list_count(priv->children);
397
398 if (EINA_DBL_EQ(expand, 0))
399 {
400 if (rtl) ax = 1.0 - ax;
401 if (horizontal)
402 {
403 x += (double)(w - minw) * ax;
404 w = minw;
405 }
406 else
407 {
408 y += (double)(h - minh) * ay;
409 h = minh;
410 }
411 }
412 xx = x;
413 yy = y;
414 EINA_LIST_FOREACH(priv->children, l, opt)
415 {
416 Evas_Coord mnw, mnh, mxw, mxh;
417 Evas_Coord pad_l, pad_r, pad_t, pad_b;
418 int fw, fh, xw, xh;//fillw, fillw, expandw, expandh
419 Evas_Aspect_Control aspect = EVAS_ASPECT_CONTROL_NONE;
420 int asx, asy;
421
422 obj = opt->obj;
423 evas_object_size_hint_align_get(obj, &ax, &ay);
424 evas_object_size_hint_weight_get(obj, &wx, &wy);
425 evas_object_size_hint_padding_get(obj, &pad_l, &pad_r, &pad_t, &pad_b);
426 evas_object_size_hint_combined_min_get(obj, &mnw, &mnh);
427 mnw += pad_l + pad_r;
428 mnh += pad_t + pad_b;
429 evas_object_size_hint_max_get(obj, &mxw, &mxh);
430 if (mxw >= 0) mxw += pad_l + pad_r;
431 if (mxh >= 0) mxh += pad_t + pad_b;
432 evas_object_size_hint_aspect_get(obj, &aspect, &asx, &asy);
433 if (aspect && ((asx < 1) || (asy < 1)))
434 aspect = EVAS_ASPECT_CONTROL_NONE;
435 fw = fh = 0;
436 xw = xh = 0;
437 /* align(-1) means fill to maximum apportioned size */
438 if (EINA_DBL_EQ(ax, -1.0)) {fw = 1; ax = 0.5;}
439 if (EINA_DBL_EQ(ay, -1.0)) {fh = 1; ay = 0.5;}
440 if (rtl) ax = 1.0 - ax;
441 if (wx > 0.0) xw = 1;
442 if (wy > 0.0) xh = 1;
443 if (horizontal)
444 {
445 Evas_Coord ww, hh, ow = 0, oh = 0;
446
447 if (homogeneous)
448 {
449 ww = ((w - (count - 1) * priv->pad.h) / (Evas_Coord)count);
450 }
451 else
452 {
453 ww = mnw;
454 if ((expand > 0) && (xw))
455 {
456 ow = ((w - minw) * wx) / expand;
457 ww += ow;
458 }
459 }
460 hh = h;
461
462 _box_object_aspect_calc(&ow, &oh, mnw, mnh, mxw, mxh, fw, fh,
463 ww, hh, aspect, asx / (double)asy);
464 /* non-homogeneous, aspected, expending items are calculated
465 * based on object size during extents calc, so use this for
466 * positioning during layout as well
467 */
468 if (aspect && (!homogeneous))
469 ww = ow;
470 evas_object_move(obj,
471 ((!rtl) ? (xx + pad_l) : (x + (w - (xx - x) - ww) + pad_r))
472 + (Evas_Coord)(((double)(ww - ow)) * ax),
473 yy + (Evas_Coord)(((double)(hh - oh)) * ay) + pad_t);
474 ow -= pad_l + pad_r;
475 oh -= pad_t + pad_b;
476 evas_object_resize(obj, ow, oh);
477 xx += ww;
478 xx += priv->pad.h;
479 }
480 else
481 {
482 Evas_Coord ww, hh, ow = 0, oh = 0;
483
484 if (homogeneous)
485 {
486 hh = ((h - (count - 1) * priv->pad.v) / (Evas_Coord)count);
487 }
488 else
489 {
490 hh = mnh;
491 if ((expand > 0) && (xh))
492 {
493 oh = ((h - minh) * wy) / expand;
494 hh += oh;
495 }
496 }
497 ww = w;
498
499 _box_object_aspect_calc(&ow, &oh, mnw, mnh, mxw, mxh, fw, fh,
500 ww, hh, aspect, asx / (double)asy);
501 if (aspect && (!homogeneous))
502 hh = oh;
503 evas_object_move(obj,
504 xx + (Evas_Coord)(((double)(ww - ow)) * ax) + pad_l,
505 yy + (Evas_Coord)(((double)(hh - oh)) * ay) + pad_t);
506 ow -= pad_l + pad_r;
507 oh -= pad_t + pad_b;
508 evas_object_resize(obj, ow, oh);
509 yy += hh;
510 yy += priv->pad.v;
511 }
512 }
513 }
514