cgv
Loading...
Searching...
No Matches
layout_table.cxx
1#include <cgv/gui/layout_table.h>
2#include <memory.h>
3#include <cgv/type/variant.h>
4
55namespace cgv {
56 namespace gui {
57
58
59 layout_table::layout_table()
60 {
61 nr_cols = 1;
62 nr_rows = 1;
63 columns = 0;
64 rows = 0;
65 do_not_layout = false;
66 }
67
68
69 layout_table::layout_table(cgv::base::group_ptr container):
70 layout(container)
71 {
72 nr_cols = 1;
73 nr_rows = 1;
74 columns = 0;
75 rows = 0;
76 do_not_layout = false;
77 }
78
79
80 layout_table::~layout_table()
81 {
82 delete_space_tables();
83 }
84
85
86 void layout_table::delete_space_tables()
87 {
88 if (columns)
89 delete[] columns;
90
91 if (rows)
92 delete[] rows;
93
94 columns = 0;
95 rows = 0;
96 }
97
98
99 // calculate minimum and optimum size
100 void layout_table::initialize_space_tables()
101 {
102 int opt_width, opt_height, min_width, min_height, hints;
103
104 if (!columns) {
105 // initialize the row and column information
106 columns = new layout_table_cell[nr_cols];
107 rows = new layout_table_cell[nr_rows];
108 }
109
110 // elements can shrink by default
111 for (int c=0; c<nr_cols; c++)
112 columns[c].reset();
113 for (int r=0; r<nr_rows; r++)
114 rows[r].reset();
115
116 for (int r=0; r<nr_rows; r++)
117 for (int c=0; c<nr_cols; c++) {
118
119 cgv::base::base_ptr cur_child = get_child(c+r*nr_cols);
120
121 get_child_default_size(cur_child, opt_width, opt_height);
122 get_child_minimum_size(cur_child, min_width, min_height);
123 hints = get_child_layout_hints(cur_child);
124
125 update_spaces_table(columns[c], opt_width, min_width, hints, LH_HHINTS);
126 update_spaces_table(rows[r], opt_height, min_height, hints, LH_VHINTS);
127 }
128
129 }
130
131
132
133 void layout_table::update()
134 {
135 int opt_cwidth, opt_cheight, min_cwidth, min_cheight, chints;
136 int opt_width, opt_height;
137 int min_width, min_height;
138 int border_width, border_height;
139 int new_cwidth, new_cheight;
140 int realized_cwidth, realized_cheight;
141 bool is_wrong_size, need_relayout;
142 int pos_x, pos_y, pos_x_offset, pos_y_offset;
143
144
145 if (do_not_layout || !container || !container->get_nr_children() || !nr_cols || !nr_rows)
146 return;
147
148 true_w = w;
149 true_h = h;
150
151 is_wrong_size = false;
152
153 // calculate the space needed for the border and inner spacings
154 border_width = spacings.horizontal.element*(nr_cols-1) + spacings.horizontal.border*2;
155 border_height = spacings.vertical.element*(nr_rows-1) + spacings.vertical.border*2;
156
157 // initialize the column and row tables and calculate
158 // minimum and optimum size
159 initialize_space_tables();
160
161 int num_relayouts = 100;
162
163 // rearrange the children as long as relayouting is neccessary
164 do {
165 num_relayouts--;
166 need_relayout = false;
167
168 // get optimum and minimum sum size
169 get_sizes(columns, nr_cols, &opt_width, &min_width);
170 get_sizes(rows, nr_rows, &opt_height, &min_height);
171
172 // add border spaces
173 opt_width+=border_width;
174 min_width+=border_width;
175 opt_height+=border_height;
176 min_height+=border_height;
177
178 // is it possible to fit the table into the given size?
179 if (min_width > true_w) {
180 is_wrong_size = true;
181 true_w = min_width;
182 }
183 if (min_height > true_h) {
184 is_wrong_size = true;
185 true_h = min_height;
186 }
187
188
189 // split the space into cells
190 distribute_space(columns, nr_cols, true_w - border_width);
191 distribute_space(rows, nr_rows, true_h - border_height);
192
193 for (int r=0; r<nr_rows; r++) {
194 for (int c=0; c<nr_cols; c++) {
195 cgv::base::base_ptr cur_child = get_child(c+r*nr_cols);
196
197 get_child_default_size(cur_child, opt_cwidth, opt_cheight);
198 get_child_minimum_size(cur_child, min_cwidth, min_cheight);
199 chints = get_child_layout_hints(cur_child);
200
201 // calculate the child size according to the
202 // specific layout hints for a child
203 calculate_child_size(columns[c], opt_cwidth, min_cwidth, chints, LH_HHINTS, new_cwidth);
204 calculate_child_size(rows[r], opt_cheight, min_cheight, chints, LH_VHINTS, new_cheight);
205
206 // set size and position for this child
207 set_child_size(cur_child, new_cwidth, new_cheight);
208
209 // test whether the size request could be fulfilled
210 get_child_size(cur_child, realized_cwidth, realized_cheight);
211
212 // the width could not be set for this element as it returns a greater
213 // value than we tried to set. Update its minimum width, expand our
214 // container width (which will now not fit anymore) and relayout
215 if (realized_cwidth > columns[c].real_size) {
216 if (columns[c].min_size < realized_cwidth)
217 columns[c].min_size = realized_cwidth;
218 if (columns[c].opt_size < columns[c].min_size)
219 columns[c].opt_size = columns[c].min_size;
220
221 cur_child->set<int>("mw", realized_cwidth);
222 //true_w += realized_cwidth - columns[c].real_size;
223 is_wrong_size = true;
224 need_relayout = true;
225 break;
226 }
227
228 if (realized_cheight > rows[r].real_size) {
229 if (rows[r].min_size < realized_cheight)
230 rows[r].min_size = realized_cheight;
231 if (rows[r].opt_size < rows[r].min_size)
232 rows[r].opt_size = rows[r].min_size;
233
234 cur_child->set<int>("mh", realized_cheight);
235 is_wrong_size = true;
236 need_relayout = true;
237 break;
238 }
239 }
240 }
241 } while(need_relayout && num_relayouts>0);
242
243 if (num_relayouts == 0) {
244 std::cerr<<"layout_table stopped layouting the GUI because the maximum number of layouting iterations"<<std::endl;
245 std::cerr<<"was reached. This is probably a bug in the layouter."<<std::endl;
246 }
247
248 // elements are resized now. set the position
249 pos_y = spacings.vertical.border;
250
251 for (int r=0; r<nr_rows; r++) {
252 pos_x = spacings.horizontal.border;
253 for (int c=0; c<nr_cols; c++) {
254 cgv::base::base_ptr cur_child = get_child(c+r*nr_cols);
255
256 get_child_size(cur_child, opt_cwidth, opt_cheight);
257 chints = get_child_layout_hints(cur_child);
258
259 calculate_child_pos(columns[c], opt_cwidth, chints, LH_HHINTS, pos_x_offset);
260 calculate_child_pos(rows[r], opt_cheight, chints, LH_VHINTS, pos_y_offset);
261
262 set_child_position(cur_child, pos_x + pos_x_offset, pos_y + pos_y_offset);
263
264 pos_x+=columns[c].real_size + spacings.horizontal.element;
265 }
266 pos_y+=rows[r].real_size + spacings.vertical.element;
267 }
268
269 if (is_wrong_size) {
270 do_not_layout = true;
271
272 // if a resize was needed then the default values
273 // (minimum size and default size) do not seem
274 // to be correct. Repair them
275 repair_default_values(min_width, min_height,
276 opt_width, opt_height);
277
278 // set the actual size
279 container->set<int>("w", true_w);
280 container->set<int>("h", true_h);
281
282 do_not_layout = false;
283
284 }
285 }
286
287
288 void layout_table::repair_default_values(int min_width, int min_height, int opt_width, int opt_height)
289 {
290 int cur_dwidth, cur_dheight;
291
292 // set the new minimum size
293 container->set<int>("mw", min_width);
294 container->set<int>("mh", min_height);
295
296 // check whether the default size can be overwritten
297 get_child_default_size(container, cur_dwidth, cur_dheight);
298 int hints = get_child_layout_hints(container);
299
300 // if we can expand or fill then set the new default
301 // size to be the optimal size. Otherwise check whether
302 // the old default size is too small and repair it
303 if ((hints & LH_HEXPAND) || (hints & LH_HFILL))
304 container->set<int>("dw", opt_width);
305 else if (min_width > cur_dwidth)
306 container->set<int>("dw", min_width);
307
308 if ((hints & LH_VEXPAND) || (hints & LH_HEXPAND))
309 container->set<int>("dh", opt_height);
310 else if (min_height > cur_dheight)
311 container->set<int>("dh", min_height);
312
313 }
314
315
316
317 void layout_table::calculate_child_size(layout_table_cell &element, int opt_size, int min_size, int hints, int hints_shift, int &new_size)
318 {
319 new_size = opt_size;
320
321 if (element.real_size > opt_size && ((hints & (LH_HFILL<<hints_shift)) || (hints & (LH_HEXPAND<<hints_shift))))
322 new_size = element.real_size;
323
324 // decrease the size if we can shrink and the size is smaller
325 if ((hints & (LH_HSHRINK<<hints_shift)) && (element.real_size<opt_size))
326 {
327 if (element.real_size >= min_size)
328 new_size = element.real_size;
329 else
330 new_size = min_size;
331
332 }
333 }
334
335
336 void layout_table::calculate_child_pos(layout_table_cell &element, int child_size, int hints, int hints_shift, int &new_pos)
337 {
338 new_pos = 0;
339
340 // the next part is repositioning which does nothing if there is no space
341 if (element.real_size <= child_size)
342 return;
343
344 // reposition the element
345 // right/bottom aligned?
346 if ((hints & (LH_RIGHT<<hints_shift)))
347 new_pos = element.real_size - child_size;
348 else
349 // centered/middled?
350 if ((hints & (LH_CENTER<<hints_shift))) {
351 new_pos = (element.real_size - child_size)/2;
352 }
353
354 }
355
356
357
358
359 void layout_table::update_spaces_table(layout_table_cell &element, int opt_size, int min_size, int hints, int hints_shift)
360 {
361 // set the optimum size for this cell
362 if (opt_size > element.opt_size)
363 element.opt_size = opt_size;
364
365 // set the minimum size for this cell or if an
366 // element cannot be shrunk set the optimum size as minimum
367 if (!(hints & (LH_HSHRINK << hints_shift)))
368 min_size = opt_size;
369 if (min_size > element.min_size)
370 element.min_size = min_size;
371 if (element.opt_size < element.min_size)
372 element.opt_size = element.min_size;
373
374 // has the column element an element that shall be
375 // filled, expanded or shrinked?
376 if (hints & (LH_HFILL << hints_shift))
377 element.can_fill = true;
378
379 if (hints & (LH_HSHRINK << hints_shift))
380 element.can_shrink = true;
381
382 if (hints & (LH_HEXPAND << hints_shift))
383 element.can_expand = true;
384 }
385
386
387
388 void layout_table::get_sizes(layout_table_cell *elements, int nr_elements, int *opt_size, int *min_size)
389 {
390 if (opt_size) {
391 *opt_size = 0;
392 for (int i=0; i<nr_elements; i++)
393 (*opt_size)+=elements[i].opt_size;
394 }
395
396 if (min_size) {
397 *min_size=0;
398 for (int i=0; i<nr_elements; i++)
399 (*min_size)+=elements[i].min_size;
400 }
401 }
402
403
404
405
406 bool layout_table::distribute_space(layout_table_cell *elements, int nr_elements, int new_size)
407 {
408 if (new_size<0) {
409 new_size = 0;
410 }
411
412 int opt_size;
413 // calculate the optimum size
414 get_sizes(elements, nr_elements, &opt_size, NULL);
415 for (int i=0; i<nr_elements; i++)
416 elements[i].real_size = elements[i].opt_size;
417
418 // do we have enough space? if this is the case then first
419 // look for elements that shall fill the space and distribute
420 // the remaining space in the ratio of their old size
421 if (new_size >= opt_size) {
422 int remain_space = new_size - opt_size;
423
424 // get the sum of all fillable spaces
425 int fill_space = 0;
426 int expand_space = 0;
427 for (int i=0; i<nr_elements; i++) {
428 if (elements[i].can_fill)
429 fill_space+=elements[i].opt_size;
430 if (elements[i].can_expand)
431 expand_space+=elements[i].opt_size;
432 }
433
434 // do we have something that shall be filled?
435 // if yes then distribute the space and quit
436 if (fill_space>0) {
437 for (int i=0; i<nr_elements; i++)
438 if (elements[i].can_fill)
439 elements[i].real_size+=elements[i].opt_size*remain_space/fill_space;
440
441 return true;
442 }
443
444 // nothing to fill but elements to be expanded?
445 // also distribute the space and quit
446 if (expand_space > 0) {
447 for (int i=0; i<nr_elements; i++)
448 if (elements[i].can_expand)
449 elements[i].real_size+=elements[i].opt_size*remain_space/expand_space;
450
451 return true;
452 }
453
454 // still here. expand again but only all elements
455 for (int i=0; i<nr_elements; i++)
456 elements[i].real_size+=elements[i].opt_size*remain_space/opt_size;
457 }
458
459
460
461 // the size is not sufficient, so try to shrink elements
462 if (new_size < opt_size) {
463 // calculate the minimum size
464 int min_size;
465 get_sizes(elements, nr_elements, NULL, &min_size);
466
467 // is it possible to shrink the elements enough?
468 // if not we can stop trying
469 if (min_size>new_size) {
470 return false;
471 }
472
473 // how much can be shrunk
474 int shrink_size = opt_size - min_size;
475 // how much has to be shrunk
476 int overfl_size = opt_size - new_size;
477
478 int shrunk = 0;
479 // shrink all elements according to their ratio
480 for (int i=0; i<nr_elements; i++)
481 if (elements[i].can_shrink) {
482 elements[i].real_size-= (elements[i].opt_size - elements[i].min_size)*overfl_size/shrink_size;
483 shrunk+=(elements[i].opt_size - elements[i].min_size)*overfl_size/shrink_size;
484 }
485
486 // there might be one pixel left due to a division
487 // by an odd number. get it from the first element
488 // that can be shrunk.
489 if (shrunk != overfl_size)
490 for (int i=0; i<nr_elements; i++)
491 if (elements[i].can_shrink && elements[i].real_size > elements[i].min_size) {
492 elements[i].real_size--;
493 break;
494 }
495 }
496
497 return true;
498 }
499
500
501
502 std::string layout_table::get_property_declarations()
503 {
504 return layout::get_property_declarations()+";cols:int32;rows:int32";
505 }
506
507
508 bool layout_table::set_void(const std::string& property, const std::string& value_type, const void* value_ptr)
509 {
510 if (layout::set_void(property, value_type, value_ptr))
511 return true;
512
513 if (property == "cols")
514 nr_cols = cgv::type::variant<cgv::type::int32_type>::get(value_type, value_ptr);
515 else if (property == "rows")
516 nr_rows = cgv::type::variant<cgv::type::int32_type>::get(value_type, value_ptr);
517 else
518 return false;
519
520 return true;
521 }
522
523
524 bool layout_table::get_void(const std::string& property, const std::string& value_type, void* value_ptr)
525 {
526 if (layout::get_void(property, value_type, value_ptr))
527 return true;
528
529 if (property == "cols")
530 cgv::type::set_variant(nr_cols, value_type, value_ptr);
531 else if (property == "rows")
532 cgv::type::set_variant(nr_rows, value_type, value_ptr);
533 else
534 return false;
535
536 return true;
537 }
538
539 // get width of a column
540 int layout_table::get_column_width(int col)
541 {
542 if (col<nr_cols && columns)
543 return columns[col].min_size;
544
545 return -1;
546 }
547
548 // get height of a row
549 int layout_table::get_row_height(int row)
550 {
551 if (row<nr_rows && rows)
552 return rows[row].min_size;
553
554 return -1;
555 }
556
557 // get size of a cell
558 void layout_table::get_cell_size(int row, int col, int &width, int &height)
559 {
560 width = get_column_width(col);
561 height = get_row_height(row);
562 }
563
564
565
566 }
567}
the cgv namespace
Definition print.h:11
static T get(const std::string &value_type, const void *value_ptr)
convert the value pointed to by value_ptr of type value_type to type T and return it
Definition variant.h:23