]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/layout/canvas_layout_test.cxx
Off-by-one error in the OSG_VERSION_LESS_THAN macro
[simgear.git] / simgear / canvas / layout / canvas_layout_test.cxx
1 // Testing canvas layouting system
2 //
3 // Copyright (C) 2014  Thomas Geymayer <tomgey@gmail.com>
4 //
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.
9 //
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.
14 //
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
18
19 #define BOOST_TEST_MODULE canvas_layout
20 #include <BoostTestTargetConfig.h>
21
22 #include "BoxLayout.hxx"
23 #include "NasalWidget.hxx"
24
25 #include <simgear/debug/logstream.hxx>
26 #include <simgear/nasal/cppbind/NasalContext.hxx>
27
28 #include <cstdlib>
29
30 //------------------------------------------------------------------------------
31 struct SetLogLevelFixture
32 {
33   SetLogLevelFixture()
34   {
35     sglog().set_log_priority(SG_DEBUG);
36   }
37 };
38 BOOST_GLOBAL_FIXTURE(SetLogLevelFixture);
39
40 //------------------------------------------------------------------------------
41 namespace sc = simgear::canvas;
42
43 class TestWidget:
44   public sc::LayoutItem
45 {
46   public:
47     TestWidget( const SGVec2i& min_size,
48                 const SGVec2i& size_hint,
49                 const SGVec2i& max_size = MAX_SIZE )
50     {
51       _size_hint = size_hint;
52       _min_size = min_size;
53       _max_size = max_size;
54     }
55
56     TestWidget(const TestWidget& rhs)
57     {
58       _size_hint = rhs._size_hint;
59       _min_size = rhs._min_size;
60       _max_size = rhs._max_size;
61     }
62
63     void setMinSize(const SGVec2i& size) { _min_size = size; }
64     void setMaxSize(const SGVec2i& size) { _max_size = size; }
65     void setSizeHint(const SGVec2i& size) { _size_hint = size; }
66
67   protected:
68
69     virtual SGVec2i sizeHintImpl() const { return _size_hint; }
70     virtual SGVec2i minimumSizeImpl() const { return _min_size; }
71     virtual SGVec2i maximumSizeImpl() const { return _max_size; }
72
73     virtual void visibilityChanged(bool visible)
74     {
75       if( !visible )
76         _geometry.set(0, 0, 0, 0);
77     }
78 };
79
80 class TestWidgetHFW:
81   public TestWidget
82 {
83   public:
84     TestWidgetHFW( const SGVec2i& min_size,
85                    const SGVec2i& size_hint,
86                    const SGVec2i& max_size = MAX_SIZE ):
87       TestWidget(min_size, size_hint, max_size)
88     {
89
90     }
91
92     virtual bool hasHeightForWidth() const
93     {
94       return true;
95     }
96
97     virtual int heightForWidthImpl(int w) const
98     {
99       return _size_hint.x() * _size_hint.y() / w;
100     }
101
102     virtual int minimumHeightForWidthImpl(int w) const
103     {
104       return _min_size.x() * _min_size.y() / w;
105     }
106 };
107
108 typedef SGSharedPtr<TestWidget> TestWidgetRef;
109
110 //------------------------------------------------------------------------------
111 BOOST_AUTO_TEST_CASE( horizontal_layout )
112 {
113   sc::BoxLayoutRef box_layout(new sc::BoxLayout(sc::BoxLayout::BottomToTop));
114   box_layout->setSpacing(5);
115
116   BOOST_CHECK_EQUAL(box_layout->direction(), sc::BoxLayout::BottomToTop);
117   BOOST_CHECK_EQUAL(box_layout->spacing(), 5);
118
119   box_layout->setDirection(sc::BoxLayout::LeftToRight);
120   box_layout->setSpacing(9);
121
122   BOOST_CHECK_EQUAL(box_layout->direction(), sc::BoxLayout::LeftToRight);
123   BOOST_CHECK_EQUAL(box_layout->spacing(), 9);
124
125   TestWidgetRef fixed_size_widget( new TestWidget( SGVec2i(16, 16),
126                                                    SGVec2i(16, 16),
127                                                    SGVec2i(16, 16) ) );
128   box_layout->addItem(fixed_size_widget);
129
130   BOOST_CHECK_EQUAL(box_layout->minimumSize(), SGVec2i(16, 16));
131   BOOST_CHECK_EQUAL(box_layout->sizeHint(), SGVec2i(16, 16));
132   BOOST_CHECK_EQUAL(box_layout->maximumSize(), SGVec2i(16, 16));
133
134   TestWidgetRef limited_resize_widget( new TestWidget( SGVec2i(16, 16),
135                                                        SGVec2i(32, 32),
136                                                        SGVec2i(256, 64) ) );
137   box_layout->addItem(limited_resize_widget);
138
139   // Combined sizes of both widget plus the padding between them
140   BOOST_CHECK_EQUAL(box_layout->minimumSize(), SGVec2i(41, 16));
141   BOOST_CHECK_EQUAL(box_layout->sizeHint(), SGVec2i(57, 32));
142   BOOST_CHECK_EQUAL(box_layout->maximumSize(), SGVec2i(281, 64));
143
144   // Test with different spacing/padding
145   box_layout->setSpacing(5);
146
147   BOOST_CHECK_EQUAL(box_layout->minimumSize(), SGVec2i(37, 16));
148   BOOST_CHECK_EQUAL(box_layout->sizeHint(), SGVec2i(53, 32));
149   BOOST_CHECK_EQUAL(box_layout->maximumSize(), SGVec2i(277, 64));
150
151   box_layout->setGeometry(SGRecti(0, 0, 128, 32));
152
153   // Fixed size for first widget and remaining space goes to second widget
154   BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(0, 8, 16, 16));
155   BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(21, 0, 107, 32));
156
157   TestWidgetRef stretch_widget( new TestWidget( SGVec2i(16, 16),
158                                                 SGVec2i(32, 32),
159                                                 SGVec2i(128, 32) ) );
160   box_layout->addItem(stretch_widget, 1);
161   box_layout->update();
162
163   BOOST_CHECK_EQUAL(box_layout->minimumSize(), SGVec2i(58, 16));
164   BOOST_CHECK_EQUAL(box_layout->sizeHint(), SGVec2i(90, 32));
165   BOOST_CHECK_EQUAL(box_layout->maximumSize(), SGVec2i(410, 64));
166
167   // Due to the stretch factor only the last widget gets additional space. All
168   // other widgets get the preferred size.
169   BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(0, 8, 16, 16));
170   BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(21, 0, 32, 32));
171   BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(58, 0, 70, 32));
172
173   // Test stretch factor
174   TestWidgetRef fast_stretch( new TestWidget(*stretch_widget) );
175   sc::BoxLayoutRef box_layout_stretch(
176     new sc::BoxLayout(sc::BoxLayout::LeftToRight)
177   );
178
179   box_layout_stretch->addItem(stretch_widget, 1);
180   box_layout_stretch->addItem(fast_stretch, 2);
181
182   box_layout_stretch->setGeometry(SGRecti(0,0,128,32));
183
184   BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 41, 32));
185   BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(46, 0, 82, 32));
186
187   box_layout_stretch->setGeometry(SGRecti(0,0,256,32));
188
189   BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 123, 32));
190   BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(128, 0, 128, 32));
191
192   // Test superflous space to padding
193   box_layout_stretch->setGeometry(SGRecti(0,0,512,32));
194
195   BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(83, 0, 128, 32));
196   BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(300, 0, 128, 32));
197
198   // ...and now with alignment
199   //
200   // All widgets without alignment get their maximum space and the remaining
201   // space is equally distributed to the remaining items. All items with
202   // alignment are set to their size hint and positioned according to their
203   // alignment.
204
205   // Left widget: size hint and positioned on the left
206   // Right widget: maximum size and positioned on the right
207   stretch_widget->setAlignment(sc::AlignLeft);
208   box_layout_stretch->update();
209   BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 32, 32));
210   BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(384, 0, 128, 32));
211
212   // Left widget: align right
213   stretch_widget->setAlignment(sc::AlignRight);
214   box_layout_stretch->update();
215   BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(347, 0, 32, 32));
216   BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(384, 0, 128, 32));
217
218   // Left widget: size hint and positioned on the right
219   // Right widget: size hint and positioned on the left of the right half
220   fast_stretch->setAlignment(sc::AlignLeft);
221   box_layout_stretch->update();
222   BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(221, 0, 32, 32));
223   BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(258, 0, 32, 32));
224
225   // Also check vertical alignment
226   stretch_widget->setAlignment(sc::AlignLeft | sc::AlignTop);
227   fast_stretch->setAlignment(sc::AlignLeft | sc::AlignBottom);
228   box_layout_stretch->setGeometry(SGRecti(0,0,512,64));
229   BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 32, 32));
230   BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(258, 32, 32, 32));
231 }
232
233 //------------------------------------------------------------------------------
234 // Test more space then preferred, but less than maximum
235 BOOST_AUTO_TEST_CASE( hbox_pref_to_max )
236 {
237   sc::BoxLayoutRef hbox(new sc::HBoxLayout());
238   TestWidgetRef w1( new TestWidget( SGVec2i(16,   16),
239                                     SGVec2i(32,   32),
240                                     SGVec2i(9999, 32) ) ),
241                 w2( new TestWidget(*w1) );
242
243   hbox->addItem(w1);
244   hbox->addItem(w2);
245
246   hbox->setGeometry( SGRecti(0, 0, 256, 32) );
247
248   BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0,   0, 126, 32));
249   BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(131, 0, 125, 32));
250
251   hbox->setStretch(0, 1);
252   hbox->setStretch(1, 1);
253
254   BOOST_CHECK_EQUAL(hbox->stretch(0), 1);
255   BOOST_CHECK_EQUAL(hbox->stretch(1), 1);
256
257   hbox->update();
258
259   BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0,   0, 125, 32));
260   BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(130, 0, 126, 32));
261
262   BOOST_REQUIRE( hbox->setStretchFactor(w1, 2) );
263   BOOST_REQUIRE( hbox->setStretchFactor(w2, 3) );
264   BOOST_CHECK_EQUAL(hbox->stretch(0), 2);
265   BOOST_CHECK_EQUAL(hbox->stretch(1), 3);
266
267   hbox->removeItem(w1);
268
269   BOOST_CHECK( !hbox->setStretchFactor(w1, 0) );
270 }
271
272 //------------------------------------------------------------------------------
273 BOOST_AUTO_TEST_CASE( spacer_layouting )
274 {
275   sc::HBoxLayout hbox;
276   TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
277                                     SGVec2i(32, 32),
278                                     SGVec2i(9999, 9999) ) ),
279                 w2( new TestWidget(*w1) );
280
281   hbox.addItem(w1);
282   hbox.addItem(w2);
283   hbox.addStretch(1);
284
285   BOOST_CHECK_EQUAL(hbox.minimumSize(), SGVec2i(37, 16));
286   BOOST_CHECK_EQUAL(hbox.sizeHint(), SGVec2i(69, 32));
287   BOOST_CHECK_EQUAL(hbox.maximumSize(), sc::LayoutItem::MAX_SIZE);
288
289   hbox.setGeometry(SGRecti(0, 0, 256, 40));
290
291   BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0,  0, 32, 40));
292   BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(37, 0, 32, 40));
293
294   // now center with increased spacing between both widgets
295   hbox.insertStretch(0, 1);
296   hbox.insertSpacing(2, 10);
297
298   BOOST_CHECK_EQUAL(hbox.minimumSize(), SGVec2i(47, 16));
299   BOOST_CHECK_EQUAL(hbox.sizeHint(), SGVec2i(79, 32));
300   BOOST_CHECK_EQUAL(hbox.maximumSize(), sc::LayoutItem::MAX_SIZE);
301
302   hbox.update();
303
304   BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(88,  0, 32, 40));
305   BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(135, 0, 32, 40));
306 }
307
308 //------------------------------------------------------------------------------
309 BOOST_AUTO_TEST_CASE( vertical_layout)
310 {
311   sc::BoxLayout vbox(sc::BoxLayout::TopToBottom);
312   vbox.setSpacing(7);
313
314   TestWidgetRef fixed_size_widget( new TestWidget( SGVec2i(16, 16),
315                                                    SGVec2i(16, 16),
316                                                    SGVec2i(16, 16) ) );
317   TestWidgetRef limited_resize_widget( new TestWidget( SGVec2i(16, 16),
318                                                        SGVec2i(32, 32),
319                                                        SGVec2i(256, 64) ) );
320
321   vbox.addItem(fixed_size_widget);
322   vbox.addItem(limited_resize_widget);
323
324   BOOST_CHECK_EQUAL(vbox.minimumSize(), SGVec2i(16, 39));
325   BOOST_CHECK_EQUAL(vbox.sizeHint(), SGVec2i(32, 55));
326   BOOST_CHECK_EQUAL(vbox.maximumSize(), SGVec2i(256, 87));
327
328   vbox.setGeometry(SGRecti(10, 20, 16, 55));
329
330   BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(10, 20, 16, 16));
331   BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(10, 43, 16, 32));
332
333   vbox.setDirection(sc::BoxLayout::BottomToTop);
334 }
335
336 //------------------------------------------------------------------------------
337 BOOST_AUTO_TEST_CASE( boxlayout_insert_remove )
338 {
339   sc::BoxLayoutRef hbox( new sc::HBoxLayout );
340
341   BOOST_CHECK_EQUAL(hbox->count(), 0);
342   BOOST_CHECK(!hbox->itemAt(0));
343   BOOST_CHECK(!hbox->takeAt(0));
344
345   TestWidgetRef w1( new TestWidget( SGVec2i(16,   16),
346                                     SGVec2i(32,   32),
347                                     SGVec2i(9999, 32) ) ),
348                 w2( new TestWidget(*w1) );
349
350   hbox->addItem(w1);
351   BOOST_CHECK_EQUAL(hbox->count(), 1);
352   BOOST_CHECK_EQUAL(hbox->itemAt(0), w1);
353   BOOST_CHECK_EQUAL(w1->getParent(), hbox);
354
355   hbox->insertItem(0, w2);
356   BOOST_CHECK_EQUAL(hbox->count(), 2);
357   BOOST_CHECK_EQUAL(hbox->itemAt(0), w2);
358   BOOST_CHECK_EQUAL(hbox->itemAt(1), w1);
359   BOOST_CHECK_EQUAL(w2->getParent(), hbox);
360
361   hbox->removeItem(w2);
362   BOOST_CHECK_EQUAL(hbox->count(), 1);
363   BOOST_CHECK_EQUAL(hbox->itemAt(0), w1);
364   BOOST_CHECK( !w2->getParent() );
365
366   hbox->addItem(w2);
367   BOOST_CHECK_EQUAL(hbox->count(), 2);
368   BOOST_CHECK_EQUAL(w2->getParent(), hbox);
369
370   hbox->clear();
371   BOOST_CHECK_EQUAL(hbox->count(), 0);
372   BOOST_CHECK( !w1->getParent() );
373   BOOST_CHECK( !w2->getParent() );
374 }
375
376 //------------------------------------------------------------------------------
377 BOOST_AUTO_TEST_CASE( boxlayout_visibility )
378 {
379   sc::BoxLayoutRef hbox( new sc::HBoxLayout );
380   TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
381                                     SGVec2i(32, 32) ) ),
382                 w2( new TestWidget(*w1) ),
383                 w3( new TestWidget(*w1) );
384
385   hbox->addItem(w1);
386   hbox->addItem(w2);
387   hbox->addItem(w3);
388
389   BOOST_REQUIRE_EQUAL(hbox->sizeHint().x(), 3 * 32 + 2 * hbox->spacing());
390
391   hbox->setGeometry(SGRecti(0, 0, 69, 32));
392
393   BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0,  0, 20, 32));
394   BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(25, 0, 20, 32));
395   BOOST_CHECK_EQUAL(w3->geometry(), SGRecti(50, 0, 19, 32));
396
397   w2->setVisible(false);
398
399   BOOST_REQUIRE(hbox->isVisible());
400   BOOST_REQUIRE(w1->isVisible());
401   BOOST_REQUIRE(!w2->isVisible());
402   BOOST_REQUIRE(w2->isExplicitlyHidden());
403   BOOST_REQUIRE(w3->isVisible());
404
405   BOOST_CHECK_EQUAL(hbox->sizeHint().x(), 2 * 32 + 1 * hbox->spacing());
406
407   hbox->update();
408
409   BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0,  0, 32, 32));
410   BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0,  0,  0,  0));
411   BOOST_CHECK_EQUAL(w3->geometry(), SGRecti(37, 0, 32, 32));
412
413   hbox->setVisible(false);
414
415   BOOST_REQUIRE(!hbox->isVisible());
416   BOOST_REQUIRE(hbox->isExplicitlyHidden());
417   BOOST_REQUIRE(!w1->isVisible());
418   BOOST_REQUIRE(!w1->isExplicitlyHidden());
419   BOOST_REQUIRE(!w2->isVisible());
420   BOOST_REQUIRE(w2->isExplicitlyHidden());
421   BOOST_REQUIRE(!w3->isVisible());
422   BOOST_REQUIRE(!w3->isExplicitlyHidden());
423
424   BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 0, 0));
425   BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 0, 0, 0));
426   BOOST_CHECK_EQUAL(w3->geometry(), SGRecti(0, 0, 0, 0));
427
428   w2->setVisible(true);
429
430   BOOST_REQUIRE(!w2->isVisible());
431   BOOST_REQUIRE(!w2->isExplicitlyHidden());
432
433   hbox->setVisible(true);
434
435   BOOST_REQUIRE(hbox->isVisible());
436   BOOST_REQUIRE(w1->isVisible());
437   BOOST_REQUIRE(w2->isVisible());
438   BOOST_REQUIRE(w3->isVisible());
439
440   hbox->update();
441
442   BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0,  0, 20, 32));
443   BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(25, 0, 20, 32));
444   BOOST_CHECK_EQUAL(w3->geometry(), SGRecti(50, 0, 19, 32));
445 }
446
447 //------------------------------------------------------------------------------
448 BOOST_AUTO_TEST_CASE( boxlayout_contents_margins )
449 {
450   sc::Margins m;
451
452   BOOST_REQUIRE(m.isNull());
453
454   m = sc::Margins(5);
455
456   BOOST_REQUIRE_EQUAL(m.l, 5);
457   BOOST_REQUIRE_EQUAL(m.t, 5);
458   BOOST_REQUIRE_EQUAL(m.r, 5);
459   BOOST_REQUIRE_EQUAL(m.b, 5);
460
461   m = sc::Margins(6, 7);
462
463   BOOST_REQUIRE_EQUAL(m.l, 6);
464   BOOST_REQUIRE_EQUAL(m.t, 7);
465   BOOST_REQUIRE_EQUAL(m.r, 6);
466   BOOST_REQUIRE_EQUAL(m.b, 7);
467
468   BOOST_REQUIRE_EQUAL(m.horiz(), 12);
469   BOOST_REQUIRE_EQUAL(m.vert(), 14);
470   BOOST_REQUIRE(!m.isNull());
471
472   m = sc::Margins(1, 2, 3, 4);
473
474   BOOST_REQUIRE_EQUAL(m.l, 1);
475   BOOST_REQUIRE_EQUAL(m.t, 2);
476   BOOST_REQUIRE_EQUAL(m.r, 3);
477   BOOST_REQUIRE_EQUAL(m.b, 4);
478
479   BOOST_REQUIRE_EQUAL(m.horiz(), 4);
480   BOOST_REQUIRE_EQUAL(m.vert(), 6);
481   BOOST_REQUIRE_EQUAL(m.size(), SGVec2i(4, 6));
482
483   sc::BoxLayoutRef hbox( new sc::HBoxLayout );
484
485   hbox->setContentsMargins(5, 10, 15, 20);
486
487   BOOST_CHECK_EQUAL(hbox->minimumSize(), SGVec2i(20, 30));
488   BOOST_CHECK_EQUAL(hbox->sizeHint(),    SGVec2i(20, 30));
489   BOOST_CHECK_EQUAL(hbox->maximumSize(), SGVec2i(20, 30));
490
491   hbox->setGeometry(SGRecti(0, 0, 30, 40));
492
493   BOOST_CHECK_EQUAL(hbox->geometry(), SGRecti(0, 0, 30, 40));
494   BOOST_CHECK_EQUAL(hbox->contentsRect(), SGRecti(5, 10, 10, 10));
495
496   TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
497                                     SGVec2i(32, 32) ) ),
498                 w2( new TestWidget(*w1) ),
499                 w3( new TestWidget(*w1) );
500
501   w1->setContentsMargin(5);
502   w2->setContentsMargin(6);
503   w3->setContentsMargin(7);
504
505   BOOST_CHECK_EQUAL(w1->minimumSize(), SGVec2i(26, 26));
506   BOOST_CHECK_EQUAL(w1->sizeHint(),    SGVec2i(42, 42));
507   BOOST_CHECK_EQUAL(w1->maximumSize(), sc::LayoutItem::MAX_SIZE);
508
509   BOOST_CHECK_EQUAL(w2->minimumSize(), SGVec2i(28, 28));
510   BOOST_CHECK_EQUAL(w2->sizeHint(),    SGVec2i(44, 44));
511   BOOST_CHECK_EQUAL(w2->maximumSize(), sc::LayoutItem::MAX_SIZE);
512
513   BOOST_CHECK_EQUAL(w3->minimumSize(), SGVec2i(30, 30));
514   BOOST_CHECK_EQUAL(w3->sizeHint(),    SGVec2i(46, 46));
515   BOOST_CHECK_EQUAL(w3->maximumSize(), sc::LayoutItem::MAX_SIZE);
516
517   hbox->addItem(w1);
518   hbox->addItem(w2);
519   hbox->addItem(w3);
520
521   BOOST_CHECK_EQUAL(hbox->minimumSize(), SGVec2i(114, 60));
522   BOOST_CHECK_EQUAL(hbox->sizeHint(),    SGVec2i(162, 76));
523   BOOST_CHECK_EQUAL(hbox->maximumSize(), sc::LayoutItem::MAX_SIZE);
524
525   hbox->setGeometry(SGRecti(0, 0, hbox->sizeHint().x(), hbox->sizeHint().y()));
526
527   BOOST_CHECK_EQUAL(hbox->contentsRect(), SGRecti(5, 10, 142, 46));
528
529   BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(5,   10, 42, 46));
530   BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(52,  10, 44, 46));
531   BOOST_CHECK_EQUAL(w3->geometry(), SGRecti(101, 10, 46, 46));
532
533   BOOST_CHECK_EQUAL(w1->contentsRect(), SGRecti(10,  15, 32, 36));
534   BOOST_CHECK_EQUAL(w2->contentsRect(), SGRecti(58,  16, 32, 34));
535   BOOST_CHECK_EQUAL(w3->contentsRect(), SGRecti(108, 17, 32, 32));
536 }
537
538 //------------------------------------------------------------------------------
539 BOOST_AUTO_TEST_CASE( boxlayout_hfw )
540 {
541   TestWidgetRef w1( new TestWidgetHFW( SGVec2i(16,   16),
542                                        SGVec2i(32,   32) ) ),
543                 w2( new TestWidgetHFW( SGVec2i(24,   24),
544                                        SGVec2i(48,   48) ) );
545
546   BOOST_CHECK_EQUAL(w1->heightForWidth(16), 64);
547   BOOST_CHECK_EQUAL(w1->minimumHeightForWidth(16), 16);
548   BOOST_CHECK_EQUAL(w2->heightForWidth(24), 96);
549   BOOST_CHECK_EQUAL(w2->minimumHeightForWidth(24), 24);
550
551   TestWidgetRef w_no_hfw( new TestWidget( SGVec2i(16,   16),
552                                           SGVec2i(32,   32) ) );
553   BOOST_CHECK(!w_no_hfw->hasHeightForWidth());
554   BOOST_CHECK_EQUAL(w_no_hfw->heightForWidth(16), -1);
555   BOOST_CHECK_EQUAL(w_no_hfw->minimumHeightForWidth(16), -1);
556
557   // horizontal
558   sc::HBoxLayout hbox;
559   hbox.setSpacing(5);
560   hbox.addItem(w1);
561   hbox.addItem(w2);
562
563   BOOST_CHECK_EQUAL(hbox.heightForWidth(45), w2->heightForWidth(24));
564   BOOST_CHECK_EQUAL(hbox.heightForWidth(85), w2->heightForWidth(48));
565
566   hbox.addItem(w_no_hfw);
567
568   BOOST_CHECK_EQUAL(hbox.heightForWidth(66), 96);
569   BOOST_CHECK_EQUAL(hbox.heightForWidth(122), 48);
570   BOOST_CHECK_EQUAL(hbox.minimumHeightForWidth(66), 24);
571   BOOST_CHECK_EQUAL(hbox.minimumHeightForWidth(122), 16);
572
573   hbox.setGeometry(SGRecti(0, 0, 66, 24));
574
575   BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0,  16, 24));
576   BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(21, 0, 24, 24));
577   BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(50, 0, 16, 24));
578
579   // vertical
580   sc::VBoxLayout vbox;
581   vbox.setSpacing(5);
582   vbox.addItem(w1);
583   vbox.addItem(w2);
584
585   BOOST_CHECK_EQUAL(vbox.heightForWidth(24), 143);
586   BOOST_CHECK_EQUAL(vbox.heightForWidth(48), 74);
587   BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(24), 39);
588   BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(48), 22);
589
590   vbox.addItem(w_no_hfw);
591
592   BOOST_CHECK_EQUAL(vbox.heightForWidth(24), 180);
593   BOOST_CHECK_EQUAL(vbox.heightForWidth(48), 111);
594   BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(24), 60);
595   BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(48), 43);
596
597   SGVec2i min_size = vbox.minimumSize(),
598           size_hint = vbox.sizeHint();
599
600   BOOST_CHECK_EQUAL(min_size, SGVec2i(24, 66));
601   BOOST_CHECK_EQUAL(size_hint, SGVec2i(48, 122));
602
603   vbox.setGeometry(SGRecti(0, 0, 24, 122));
604
605   BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0,  24, 33));
606   BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 38, 24, 47));
607   BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 90, 24, 32));
608
609   // Vertical layouting modifies size hints, so check if they are correctly
610   // restored
611   BOOST_CHECK_EQUAL(min_size, vbox.minimumSize());
612   BOOST_CHECK_EQUAL(size_hint, vbox.sizeHint());
613
614   vbox.setGeometry(SGRecti(0, 0, 50, 122));
615
616   BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0,  50, 25));
617   BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 30, 50, 51));
618   BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 86, 50, 36));
619
620   // Same geometry as before -> should get same widget geometry
621   // (check internal size hint cache updates correctly)
622   vbox.setGeometry(SGRecti(0, 0, 24, 122));
623
624   BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0,  24, 33));
625   BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 38, 24, 47));
626   BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 90, 24, 32));
627 }
628
629 //------------------------------------------------------------------------------
630 BOOST_AUTO_TEST_CASE( item_alignment_rect )
631 {
632   TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
633                                     SGVec2i(32, 32) ) );
634
635   const SGRecti r(10, 10, 64, 64);
636
637   // Default: AlignFill -> fill up to maximum size
638   BOOST_CHECK_EQUAL(w1->alignmentRect(r), r);
639
640   // Horizontal
641
642   // AlignLeft -> width from size hint, positioned on the left
643   w1->setAlignment(sc::AlignLeft);
644   BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(10, 10, 32, 64));
645
646   // AlignRight -> width from size hint, positioned on the left
647   w1->setAlignment(sc::AlignRight);
648   BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(42, 10, 32, 64));
649
650   // AlignHCenter -> width from size hint, positioned in the center
651   w1->setAlignment(sc::AlignHCenter);
652   BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(26, 10, 32, 64));
653
654
655   // Vertical
656
657   // AlignTop -> height from size hint, positioned on the top
658   w1->setAlignment(sc::AlignTop);
659   BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(10, 10, 64, 32));
660
661   // AlignBottom -> height from size hint, positioned on the bottom
662   w1->setAlignment(sc::AlignBottom);
663   BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(10, 42, 64, 32));
664
665   // AlignVCenter -> height from size hint, positioned in the center
666   w1->setAlignment(sc::AlignVCenter);
667   BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(10, 26, 64, 32));
668
669
670   // Vertical + Horizontal
671   w1->setAlignment(sc::AlignCenter);
672   BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(26, 26, 32, 32));
673 }
674
675 //------------------------------------------------------------------------------
676 // TODO extend to_nasal_helper for automatic argument conversion
677 static naRef f_Widget_visibilityChanged(nasal::CallContext ctx)
678 {
679   sc::NasalWidget* w = ctx.from_nasal<sc::NasalWidget*>(ctx.me);
680
681   if( !ctx.requireArg<bool>(0) )
682     w->setGeometry(SGRecti(0, 0, -1, -1));
683
684   return naNil();
685 }
686
687 //------------------------------------------------------------------------------
688 BOOST_AUTO_TEST_CASE( nasal_widget )
689 {
690   nasal::Context c;
691   nasal::Hash globals = c.newHash();
692
693   nasal::Object::setupGhost();
694   nasal::Ghost<sc::LayoutItemRef>::init("LayoutItem");
695   sc::NasalWidget::setupGhost(globals);
696
697   nasal::Hash me = c.newHash();
698   me.set("visibilityChanged", &f_Widget_visibilityChanged);
699   sc::NasalWidgetRef w( new sc::NasalWidget(me.get_naRef()) );
700
701   // Default layout sizes (no user set values)
702   BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(16, 16));
703   BOOST_CHECK_EQUAL(w->sizeHint(),    SGVec2i(32, 32));
704   BOOST_CHECK_EQUAL(w->maximumSize(), sc::LayoutItem::MAX_SIZE);
705
706   // Changed layout sizes
707   w->setLayoutMinimumSize( SGVec2i(2, 12) );
708   w->setLayoutSizeHint(    SGVec2i(3, 13) );
709   w->setLayoutMaximumSize( SGVec2i(4, 14) );
710
711   BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(2, 12));
712   BOOST_CHECK_EQUAL(w->sizeHint(),    SGVec2i(3, 13));
713   BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(4, 14));
714
715   // User set values (overwrite layout sizes)
716   w->setMinimumSize( SGVec2i(15, 16) );
717   w->setSizeHint(    SGVec2i(17, 18) );
718   w->setMaximumSize( SGVec2i(19, 20) );
719
720   BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(15, 16));
721   BOOST_CHECK_EQUAL(w->sizeHint(),    SGVec2i(17, 18));
722   BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(19, 20));
723
724   // Only vertical user set values (layout/default for horizontal hints)
725   w->setMinimumSize( SGVec2i(0, 21) );
726   w->setSizeHint(    SGVec2i(0, 22) );
727   w->setMaximumSize( SGVec2i(SGLimits<int>::max(), 23) );
728
729   BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(2, 21));
730   BOOST_CHECK_EQUAL(w->sizeHint(),    SGVec2i(3, 22));
731   BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(4, 23));
732
733   w->setVisible(false);
734   BOOST_CHECK_EQUAL(w->geometry(), SGRecti(0, 0, -1, -1));
735 }