1 // Testing canvas layouting system
3 // Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Library General Public
7 // License as published by the Free Software Foundation; either
8 // version 2 of the License, or (at your option) any later version.
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Library General Public License for more details.
15 // You should have received a copy of the GNU Library General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #define BOOST_TEST_MODULE canvas_layout
20 #include <BoostTestTargetConfig.h>
22 #include "BoxLayout.hxx"
23 #include "NasalWidget.hxx"
25 #include <simgear/debug/logstream.hxx>
28 //------------------------------------------------------------------------------
29 struct SetLogLevelFixture
33 sglog().set_log_priority(SG_DEBUG);
36 BOOST_GLOBAL_FIXTURE(SetLogLevelFixture);
38 //------------------------------------------------------------------------------
39 namespace sc = simgear::canvas;
45 TestWidget( const SGVec2i& min_size,
46 const SGVec2i& size_hint,
47 const SGVec2i& max_size = MAX_SIZE )
49 _size_hint = size_hint;
54 TestWidget(const TestWidget& rhs)
56 _size_hint = rhs._size_hint;
57 _min_size = rhs._min_size;
58 _max_size = rhs._max_size;
61 void setMinSize(const SGVec2i& size) { _min_size = size; }
62 void setMaxSize(const SGVec2i& size) { _max_size = size; }
63 void setSizeHint(const SGVec2i& size) { _size_hint = size; }
65 virtual void setGeometry(const SGRecti& geom) { _geom = geom; }
66 virtual SGRecti geometry() const { return _geom; }
72 virtual SGVec2i sizeHintImpl() const { return _size_hint; }
73 virtual SGVec2i minimumSizeImpl() const { return _min_size; }
74 virtual SGVec2i maximumSizeImpl() const { return _max_size; }
81 TestWidgetHFW( const SGVec2i& min_size,
82 const SGVec2i& size_hint,
83 const SGVec2i& max_size = MAX_SIZE ):
84 TestWidget(min_size, size_hint, max_size)
89 virtual bool hasHeightForWidth() const
94 virtual int heightForWidth(int w) const
96 return _size_hint.x() * _size_hint.y() / w;
99 virtual int minimumHeightForWidth(int w) const
101 return _min_size.x() * _min_size.y() / w;
105 typedef SGSharedPtr<TestWidget> TestWidgetRef;
107 //------------------------------------------------------------------------------
108 BOOST_AUTO_TEST_CASE( horizontal_layout )
110 sc::BoxLayout box_layout(sc::BoxLayout::BottomToTop);
111 box_layout.setSpacing(5);
113 BOOST_CHECK_EQUAL(box_layout.direction(), sc::BoxLayout::BottomToTop);
114 BOOST_CHECK_EQUAL(box_layout.spacing(), 5);
116 box_layout.setDirection(sc::BoxLayout::LeftToRight);
117 box_layout.setSpacing(9);
119 BOOST_CHECK_EQUAL(box_layout.direction(), sc::BoxLayout::LeftToRight);
120 BOOST_CHECK_EQUAL(box_layout.spacing(), 9);
122 TestWidgetRef fixed_size_widget( new TestWidget( SGVec2i(16, 16),
125 box_layout.addItem(fixed_size_widget);
127 BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(16, 16));
128 BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(16, 16));
129 BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(16, 16));
131 TestWidgetRef limited_resize_widget( new TestWidget( SGVec2i(16, 16),
133 SGVec2i(256, 64) ) );
134 box_layout.addItem(limited_resize_widget);
136 // Combined sizes of both widget plus the padding between them
137 BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(41, 16));
138 BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(57, 32));
139 BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(281, 64));
141 // Test with different spacing/padding
142 box_layout.setSpacing(5);
144 BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(37, 16));
145 BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(53, 32));
146 BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(277, 64));
148 box_layout.setGeometry(SGRecti(0, 0, 128, 32));
150 // Fixed size for first widget and remaining space goes to second widget
151 BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(0, 8, 16, 16));
152 BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(21, 0, 107, 32));
154 TestWidgetRef stretch_widget( new TestWidget( SGVec2i(16, 16),
156 SGVec2i(128, 32) ) );
157 box_layout.addItem(stretch_widget, 1);
160 BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(58, 16));
161 BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(90, 32));
162 BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(410, 64));
164 // Due to the stretch factor only the last widget gets additional space. All
165 // other widgets get the preferred size.
166 BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(0, 8, 16, 16));
167 BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(21, 0, 32, 32));
168 BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(58, 0, 70, 32));
170 // Test stretch factor
171 TestWidgetRef fast_stretch( new TestWidget(*stretch_widget) );
172 sc::BoxLayout box_layout_stretch(sc::BoxLayout::LeftToRight);
174 box_layout_stretch.addItem(stretch_widget, 1);
175 box_layout_stretch.addItem(fast_stretch, 2);
177 box_layout_stretch.setGeometry(SGRecti(0,0,128,32));
179 BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 41, 32));
180 BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(46, 0, 82, 32));
182 box_layout_stretch.setGeometry(SGRecti(0,0,256,32));
184 BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 123, 32));
185 BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(128, 0, 128, 32));
187 // Test superflous space to padding
188 box_layout_stretch.setGeometry(SGRecti(0,0,512,32));
190 BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(83, 0, 128, 32));
191 BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(300, 0, 128, 32));
193 // Test more space then preferred, but less than maximum
196 TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
198 SGVec2i(9999, 32) ) ),
199 w2( new TestWidget(*w1) );
204 hbox.setGeometry( SGRecti(0, 0, 256, 32) );
206 BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 126, 32));
207 BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(131, 0, 125, 32));
209 hbox.setStretch(0, 1);
210 hbox.setStretch(1, 1);
212 BOOST_CHECK_EQUAL(hbox.stretch(0), 1);
213 BOOST_CHECK_EQUAL(hbox.stretch(1), 1);
217 BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 125, 32));
218 BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(130, 0, 126, 32));
220 BOOST_REQUIRE( hbox.setStretchFactor(w1, 2) );
221 BOOST_REQUIRE( hbox.setStretchFactor(w2, 3) );
222 BOOST_CHECK_EQUAL(hbox.stretch(0), 2);
223 BOOST_CHECK_EQUAL(hbox.stretch(1), 3);
227 BOOST_CHECK( !hbox.setStretchFactor(w1, 0) );
231 //------------------------------------------------------------------------------
232 BOOST_AUTO_TEST_CASE( spacer_layouting )
235 TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
237 SGVec2i(9999, 9999) ) ),
238 w2( new TestWidget(*w1) );
244 BOOST_CHECK_EQUAL(hbox.minimumSize(), SGVec2i(37, 16));
245 BOOST_CHECK_EQUAL(hbox.sizeHint(), SGVec2i(69, 32));
246 BOOST_CHECK_EQUAL(hbox.maximumSize(), sc::LayoutItem::MAX_SIZE);
248 hbox.setGeometry(SGRecti(0, 0, 256, 40));
250 BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 32, 40));
251 BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(37, 0, 32, 40));
253 // now center with increased spacing between both widgets
254 hbox.insertStretch(0, 1);
255 hbox.insertSpacing(2, 10);
257 BOOST_CHECK_EQUAL(hbox.minimumSize(), SGVec2i(47, 16));
258 BOOST_CHECK_EQUAL(hbox.sizeHint(), SGVec2i(79, 32));
259 BOOST_CHECK_EQUAL(hbox.maximumSize(), sc::LayoutItem::MAX_SIZE);
263 BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(88, 0, 32, 40));
264 BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(135, 0, 32, 40));
267 //------------------------------------------------------------------------------
268 BOOST_AUTO_TEST_CASE( vertical_layout)
270 sc::BoxLayout vbox(sc::BoxLayout::TopToBottom);
273 TestWidgetRef fixed_size_widget( new TestWidget( SGVec2i(16, 16),
276 TestWidgetRef limited_resize_widget( new TestWidget( SGVec2i(16, 16),
278 SGVec2i(256, 64) ) );
280 vbox.addItem(fixed_size_widget);
281 vbox.addItem(limited_resize_widget);
283 BOOST_CHECK_EQUAL(vbox.minimumSize(), SGVec2i(16, 39));
284 BOOST_CHECK_EQUAL(vbox.sizeHint(), SGVec2i(32, 55));
285 BOOST_CHECK_EQUAL(vbox.maximumSize(), SGVec2i(256, 87));
287 vbox.setGeometry(SGRecti(10, 20, 16, 55));
289 BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(10, 20, 16, 16));
290 BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(10, 43, 16, 32));
292 vbox.setDirection(sc::BoxLayout::BottomToTop);
295 //------------------------------------------------------------------------------
296 BOOST_AUTO_TEST_CASE( boxlayout_insert_remove )
298 sc::BoxLayoutRef hbox( new sc::HBoxLayout );
300 BOOST_CHECK_EQUAL(hbox->count(), 0);
301 BOOST_CHECK(!hbox->itemAt(0));
302 BOOST_CHECK(!hbox->takeAt(0));
304 TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
306 SGVec2i(9999, 32) ) ),
307 w2( new TestWidget(*w1) );
310 BOOST_CHECK_EQUAL(hbox->count(), 1);
311 BOOST_CHECK_EQUAL(hbox->itemAt(0), w1);
312 BOOST_CHECK_EQUAL(w1->getParent(), hbox);
314 hbox->insertItem(0, w2);
315 BOOST_CHECK_EQUAL(hbox->count(), 2);
316 BOOST_CHECK_EQUAL(hbox->itemAt(0), w2);
317 BOOST_CHECK_EQUAL(hbox->itemAt(1), w1);
318 BOOST_CHECK_EQUAL(w2->getParent(), hbox);
320 hbox->removeItem(w2);
321 BOOST_CHECK_EQUAL(hbox->count(), 1);
322 BOOST_CHECK_EQUAL(hbox->itemAt(0), w1);
323 BOOST_CHECK( !w2->getParent() );
326 BOOST_CHECK_EQUAL(hbox->count(), 2);
327 BOOST_CHECK_EQUAL(w2->getParent(), hbox);
330 BOOST_CHECK_EQUAL(hbox->count(), 0);
331 BOOST_CHECK( !w1->getParent() );
332 BOOST_CHECK( !w2->getParent() );
335 //------------------------------------------------------------------------------
336 BOOST_AUTO_TEST_CASE( boxlayout_hfw )
338 TestWidgetRef w1( new TestWidgetHFW( SGVec2i(16, 16),
340 w2( new TestWidgetHFW( SGVec2i(24, 24),
343 BOOST_CHECK_EQUAL(w1->heightForWidth(16), 64);
344 BOOST_CHECK_EQUAL(w1->minimumHeightForWidth(16), 16);
345 BOOST_CHECK_EQUAL(w2->heightForWidth(24), 96);
346 BOOST_CHECK_EQUAL(w2->minimumHeightForWidth(24), 24);
348 TestWidgetRef w_no_hfw( new TestWidget( SGVec2i(16, 16),
350 BOOST_CHECK(!w_no_hfw->hasHeightForWidth());
351 BOOST_CHECK_EQUAL(w_no_hfw->heightForWidth(16), -1);
352 BOOST_CHECK_EQUAL(w_no_hfw->minimumHeightForWidth(16), -1);
360 BOOST_CHECK_EQUAL(hbox.heightForWidth(45), w2->heightForWidth(24));
361 BOOST_CHECK_EQUAL(hbox.heightForWidth(85), w2->heightForWidth(48));
363 hbox.addItem(w_no_hfw);
365 BOOST_CHECK_EQUAL(hbox.heightForWidth(66), 96);
366 BOOST_CHECK_EQUAL(hbox.heightForWidth(122), 48);
367 BOOST_CHECK_EQUAL(hbox.minimumHeightForWidth(66), 24);
368 BOOST_CHECK_EQUAL(hbox.minimumHeightForWidth(122), 16);
370 hbox.setGeometry(SGRecti(0, 0, 66, 24));
372 BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 16, 24));
373 BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(21, 0, 24, 24));
374 BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(50, 0, 16, 24));
382 BOOST_CHECK_EQUAL(vbox.heightForWidth(24), 143);
383 BOOST_CHECK_EQUAL(vbox.heightForWidth(48), 74);
384 BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(24), 39);
385 BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(48), 22);
387 vbox.addItem(w_no_hfw);
389 BOOST_CHECK_EQUAL(vbox.heightForWidth(24), 180);
390 BOOST_CHECK_EQUAL(vbox.heightForWidth(48), 111);
391 BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(24), 60);
392 BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(48), 43);
394 SGVec2i min_size = vbox.minimumSize(),
395 size_hint = vbox.sizeHint();
397 BOOST_CHECK_EQUAL(min_size, SGVec2i(24, 66));
398 BOOST_CHECK_EQUAL(size_hint, SGVec2i(48, 122));
400 vbox.setGeometry(SGRecti(0, 0, 24, 122));
402 BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 24, 33));
403 BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 38, 24, 47));
404 BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 90, 24, 32));
406 // Vertical layouting modifies size hints, so check if they are correctly
408 BOOST_CHECK_EQUAL(min_size, vbox.minimumSize());
409 BOOST_CHECK_EQUAL(size_hint, vbox.sizeHint());
411 vbox.setGeometry(SGRecti(0, 0, 50, 122));
413 BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 50, 25));
414 BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 30, 50, 51));
415 BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 86, 50, 36));
417 // Same geometry as before -> should get same widget geometry
418 // (check internal size hint cache updates correctly)
419 vbox.setGeometry(SGRecti(0, 0, 24, 122));
421 BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 24, 33));
422 BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 38, 24, 47));
423 BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 90, 24, 32));
426 //------------------------------------------------------------------------------
427 BOOST_AUTO_TEST_CASE( nasal_widget )
429 naContext c = naNewContext();
430 naRef me = naNewHash(c);
432 sc::NasalWidgetRef w( new sc::NasalWidget(me) );
434 // Default layout sizes (no user set values)
435 BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(16, 16));
436 BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(32, 32));
437 BOOST_CHECK_EQUAL(w->maximumSize(), sc::LayoutItem::MAX_SIZE);
439 // Changed layout sizes
440 w->setLayoutMinimumSize( SGVec2i(2, 12) );
441 w->setLayoutSizeHint( SGVec2i(3, 13) );
442 w->setLayoutMaximumSize( SGVec2i(4, 14) );
444 BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(2, 12));
445 BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(3, 13));
446 BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(4, 14));
448 // User set values (overwrite layout sizes)
449 w->setMinimumSize( SGVec2i(15, 16) );
450 w->setSizeHint( SGVec2i(17, 18) );
451 w->setMaximumSize( SGVec2i(19, 20) );
453 BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(15, 16));
454 BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(17, 18));
455 BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(19, 20));
457 // Only vertical user set values (layout/default for horizontal hints)
458 w->setMinimumSize( SGVec2i(0, 21) );
459 w->setSizeHint( SGVec2i(0, 22) );
460 w->setMaximumSize( SGVec2i(SGLimits<int>::max(), 23) );
462 BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(2, 21));
463 BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(3, 22));
464 BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(4, 23));