summaryrefslogtreecommitdiff
path: root/src/lib/elementary/efl_ui_table_layout.c
diff options
context:
space:
mode:
authorYeongjong Lee <cleanlyj@naver.com>2019-02-27 14:45:27 -0500
committerMike Blumenkrantz <zmike@samsung.com>2019-02-27 14:45:27 -0500
commit5572000f1a02ed1107d3757d7ca06727c1217813 (patch)
treecc902b9deccb83cf53bcbc1490dbb9272f6f9cfb /src/lib/elementary/efl_ui_table_layout.c
parent665f8877e93aa729a4d2eb8b532e69ad3580435b (diff)
efl_ui_table: refactor layout_update
Summary: There are three reasons to refactor layout_update of Efl.Ui.Table. === 1. Inconsistency of hint behavior. === Some hint property is often not respected. for example, hint_min is ignored in Table when it is used with hint_max even if hint_weight is 0. hint_aspect is always ignored in Table. The ambiguous behavior make it hard to layout widgets in container. of course, we documented 'it's just a hint that should be used whenever appropriate.' but i don't think it means that 'hint API is sometimes respected and we also don't know when that API is respected.'. at least there is rule for consistent behavior and we should be able to explain why a widget is located here and why some hint property is ignored. So, i'll suggest priority of hint property. this refactoring support following priority. 1) HintMin 2) HintMin + HintAspect 3) HintMargin 4) HintMax 5) HintAspect 6) HintWeight, HintFill 7) HintAlign ref T5487 Please check with unit test D7840 === 2. To Enhance usability. === Efl.Ui.Table is using homogeneous mode of evas_table which have same columns, rows size. but i think a table can generally change columns, rows size and we can provide homogeneous mode option.(D7892) In this patch - table columns(rows) min size is decided by maximum size among its cells width(height) min size. - table columns(rows) weight is decided by maximum weight among its cells horizontal(vertical) weight. Also, pack_align is implemented. it is used if no item has a weight. === 3. To remove internal evas_table. === This is low priority work. however, i guess is is necessary for lightweight container widget. there are two size_hint callback to adjust table size and efl_canvas_group_calculate is called twice when it is resized. This patch is first step to remove internal evas_table. Test Plan: make check elementary_test -to 'efl.ui.table' Reviewers: jpeg, Jaehyun_Cho, zmike Reviewed By: zmike Subscribers: zmike, cedric, #reviewers, #committers Tags: #efl Maniphest Tasks: T5487 Differential Revision: https://phab.enlightenment.org/D7841
Diffstat (limited to 'src/lib/elementary/efl_ui_table_layout.c')
-rw-r--r--src/lib/elementary/efl_ui_table_layout.c294
1 files changed, 294 insertions, 0 deletions
diff --git a/src/lib/elementary/efl_ui_table_layout.c b/src/lib/elementary/efl_ui_table_layout.c
new file mode 100644
index 0000000..0a21384
--- /dev/null
+++ b/src/lib/elementary/efl_ui_table_layout.c
@@ -0,0 +1,294 @@
1#include "efl_ui_table_private.h"
2#include "efl_ui_container_layout.h"
3
4typedef struct _Item_Calc Item_Calc;
5typedef struct _Cell_Calc Cell_Calc;
6typedef struct _Table_Calc Table_Calc;
7
8struct _Item_Calc
9{
10 Evas_Object *obj;
11 int cell_span[2];
12 int cell_index[2];
13 Efl_Ui_Container_Item_Hints hints[2]; /* 0 is x-axis, 1 is y-axis */
14};
15
16struct _Cell_Calc
17{
18 EINA_INLIST;
19
20 int index;
21 int next;
22 double acc;
23 double space;
24 double weight;
25 double weight_factor;
26 Eina_Bool occupied : 1;
27};
28
29struct _Table_Calc
30{
31 /* 0 is x-axis, 1 is y-axis */
32
33 int rows;
34 int cols;
35 int want[2];
36 double weight_sum[2];
37 Cell_Calc *cell_calc[2];
38 Efl_Ui_Container_Layout_Calc layout_calc[2];
39};
40
41static int
42_weight_sort_cb(const void *l1, const void *l2)
43{
44 Cell_Calc *cc1, *cc2;
45
46 cc1 = EINA_INLIST_CONTAINER_GET(l1, Cell_Calc);
47 cc2 = EINA_INLIST_CONTAINER_GET(l2, Cell_Calc);
48
49 return cc2->weight_factor <= cc1->weight_factor ? -1 : 1;
50}
51
52static void
53_cell_weight_calc(Table_Calc *table_calc, Eina_Bool axis)
54{
55 int i, count, layout_size, calc_size;
56 double denom, weight_sum, calc_weight;
57 Eina_Inlist *inlist = NULL;
58 Cell_Calc *cell_calc, *cc;
59
60 layout_size = calc_size = table_calc->layout_calc[axis].size;
61 weight_sum = calc_weight = table_calc->weight_sum[axis];
62 cell_calc = table_calc->cell_calc[axis];
63 count = axis ? table_calc->rows : table_calc->cols;
64
65 for (i = 0; i < count; i = cell_calc[i].next)
66 {
67 denom = (cell_calc[i].weight * layout_size) -
68 (weight_sum * cell_calc[i].space);
69 if (denom > 0)
70 {
71 cell_calc[i].weight_factor = (cell_calc[i].weight * layout_size) / denom;
72 inlist = eina_inlist_sorted_insert(inlist,
73 EINA_INLIST_GET(&cell_calc[i]),
74 _weight_sort_cb);
75 }
76 else
77 {
78 calc_size -= cell_calc[i].space;
79 calc_weight -= cell_calc[i].weight;
80 }
81 }
82
83 EINA_INLIST_FOREACH(inlist, cc)
84 {
85 double weight_len;
86
87 weight_len = (calc_size * cc->weight) / calc_weight;
88 if (cc->space < weight_len)
89 {
90 cc->space = weight_len;
91 }
92 else
93 {
94 calc_size -= cc->space;
95 calc_weight -= cc->weight;
96 }
97 }
98}
99
100static void
101_efl_ui_table_regular_cell_init(Table_Calc *table_calc, Eina_Bool axis)
102{
103 int i, index = 0, acc, want = 0, count;
104 double weight_sum = 0;
105 Cell_Calc *prev_cell = NULL, *cell_calc;
106 Efl_Ui_Container_Layout_Calc *layout_calc;
107
108 layout_calc = &(table_calc->layout_calc[axis]);
109 cell_calc = table_calc->cell_calc[axis];
110 count = axis ? table_calc->rows : table_calc->cols;
111
112 for (i = 0; i < count; i++)
113 {
114 if (!cell_calc[i].occupied) continue;
115
116 cell_calc[i].index = index++;
117 want += cell_calc[i].space;
118 weight_sum += cell_calc[i].weight;
119
120 if (prev_cell)
121 prev_cell->next = i;
122
123 prev_cell = &cell_calc[i];
124 }
125 if (prev_cell)
126 prev_cell->next = count;
127
128 table_calc->want[axis] = want;
129 table_calc->weight_sum[axis] = weight_sum;
130 table_calc->layout_calc[axis].size -= (table_calc->layout_calc[axis].pad
131 * (index - 1));
132
133 if ((layout_calc->size > want) && (weight_sum > 0))
134 _cell_weight_calc(table_calc, axis);
135 if (EINA_DBL_EQ(weight_sum, 0.0))
136 layout_calc->pos += (layout_calc->size - want) * layout_calc->align;
137
138 for (i = 0, acc = 0; i < count; acc += cell_calc[i].space, i = cell_calc[i].next)
139 cell_calc[i].acc = acc;
140}
141
142static inline int
143_efl_ui_table_regular_item_pos_get(Table_Calc *table_calc, Item_Calc *item, Eina_Bool axis)
144{
145 return 0.5 + table_calc->layout_calc[axis].pos
146 + table_calc->cell_calc[axis][item->cell_index[axis]].acc
147 + (table_calc->cell_calc[axis][item->cell_index[axis]].index *
148 table_calc->layout_calc[axis].pad);
149}
150
151static inline int
152_efl_ui_table_regular_item_size_get(Table_Calc *table_calc, Item_Calc *item, Eina_Bool axis)
153{
154 int start, end;
155
156 start = item->cell_index[axis];
157 end = start + item->cell_span[axis] - 1;
158
159 return table_calc->cell_calc[axis][end].acc
160 - table_calc->cell_calc[axis][start].acc
161 + table_calc->cell_calc[axis][end].space
162 + ((item->cell_span[axis] - 1) * table_calc->layout_calc[axis].pad)
163 - item->hints[axis].margin[0] - item->hints[axis].margin[1];
164}
165
166void
167_efl_ui_table_custom_layout(Efl_Ui_Table *ui_table, Efl_Ui_Table_Data *pd)
168{
169 Table_Item *ti;
170 Item_Calc *items, *item;
171 Efl_Ui_Container_Item_Hints *hints;
172 int i = 0, rows, cols;
173
174 Table_Calc table_calc;
175
176 if (!pd->count)
177 {
178 efl_gfx_hint_size_min_set(ui_table, EINA_SIZE2D(0, 0));
179 return;
180 }
181
182 _efl_ui_container_layout_init(ui_table, table_calc.layout_calc);
183
184 table_calc.want[0] = table_calc.want[1] = 0;
185 table_calc.weight_sum[0] = table_calc.weight_sum[1] = 0;
186
187 efl_pack_table_size_get(ui_table, &cols, &rows);
188
189 table_calc.cell_calc[0] = alloca(cols * sizeof(Cell_Calc));
190 table_calc.cell_calc[1] = alloca(rows * sizeof(Cell_Calc));
191
192 memset(table_calc.cell_calc[0], 0, cols * sizeof(Cell_Calc));
193 memset(table_calc.cell_calc[1], 0, rows * sizeof(Cell_Calc));
194
195 items = alloca(pd->count * sizeof(*items));
196#ifdef DEBUG
197 memset(items, 0, pd->count * sizeof(*items));
198#endif
199
200 table_calc.cols = cols;
201 table_calc.rows = rows;
202 // scan all items, get their properties, calculate total weight & min size
203 EINA_INLIST_FOREACH(pd->items, ti)
204 {
205 if (((ti->col + ti->col_span) > cols) ||
206 ((ti->row + ti->row_span) > rows))
207 {
208 efl_gfx_entity_visible_set(ti->object, EINA_FALSE);
209 continue;
210 }
211
212 item = &items[i++];
213 item->obj = ti->object;
214 hints = item->hints;
215
216 _efl_ui_container_layout_item_init(item->obj, hints);
217
218 if (table_calc.layout_calc[0].fill)
219 hints[0].weight = 1;
220 else if (hints[0].weight < 0)
221 hints[0].weight = 0;
222
223 if (table_calc.layout_calc[1].fill)
224 hints[1].weight = 1;
225 else if (hints[1].weight < 0)
226 hints[1].weight = 0;
227
228 item->cell_index[0] = ti->col;
229 item->cell_index[1] = ti->row;
230 item->cell_span[0] = ti->col_span;
231 item->cell_span[1] = ti->row_span;
232
233 if (ti->col_span == 1)
234 {
235 table_calc.cell_calc[0][ti->col].occupied = EINA_TRUE;
236
237 if (table_calc.cell_calc[0][ti->col].space < hints[0].space)
238 table_calc.cell_calc[0][ti->col].space = hints[0].space;
239 if (table_calc.cell_calc[0][ti->col].weight < hints[0].weight)
240 table_calc.cell_calc[0][ti->col].weight = hints[0].weight;
241 }
242
243 if (ti->row_span == 1)
244 {
245 table_calc.cell_calc[1][ti->row].occupied = EINA_TRUE;
246
247 if (table_calc.cell_calc[1][ti->row].space < hints[1].space)
248 table_calc.cell_calc[1][ti->row].space = hints[1].space;
249 if (table_calc.cell_calc[1][ti->row].weight < hints[1].weight)
250 table_calc.cell_calc[1][ti->row].weight = hints[1].weight;
251 }
252 }
253
254 _efl_ui_table_regular_cell_init(&table_calc, 0);
255 _efl_ui_table_regular_cell_init(&table_calc, 1);
256
257 for (i = 0; i < pd->count; i++)
258 {
259 Eina_Rect space, item_geom;
260 item = &items[i];
261 hints = items[i].hints;
262
263 space.x = _efl_ui_table_regular_item_pos_get(&table_calc, item, 0);
264 space.y = _efl_ui_table_regular_item_pos_get(&table_calc, item, 1);
265 space.w = _efl_ui_table_regular_item_size_get(&table_calc, item, 0);
266 space.h = _efl_ui_table_regular_item_size_get(&table_calc, item, 1);
267
268 item_geom.w = hints[0].fill ? space.w : hints[0].min;
269 item_geom.h = hints[1].fill ? space.h : hints[1].min;
270
271 _efl_ui_container_layout_min_max_calc(hints, &item_geom.w, &item_geom.h,
272 (hints[0].aspect > 0) && (hints[1].aspect > 0));
273
274 item_geom.x = space.x + ((space.w - item_geom.w) * hints[0].align)
275 + hints[0].margin[0];
276 item_geom.y = space.y + ((space.h - item_geom.h) * hints[1].align)
277 + hints[1].margin[0];
278
279 efl_gfx_entity_geometry_set(item->obj, item_geom);
280 }
281
282 table_calc.want[0] += table_calc.layout_calc[0].margin[0]
283 + table_calc.layout_calc[0].margin[1]
284 + (table_calc.layout_calc[0].pad *
285 table_calc.cell_calc[0][cols - 1].index);
286
287 table_calc.want[1] += table_calc.layout_calc[1].margin[0]
288 + table_calc.layout_calc[1].margin[1]
289 + (table_calc.layout_calc[1].pad *
290 table_calc.cell_calc[1][rows - 1].index);
291
292 efl_gfx_hint_size_min_set(ui_table, EINA_SIZE2D(table_calc.want[0],
293 table_calc.want[1]));
294}