canvas::Layout: support for alignment.

Set alignment inside layouts, taking care of where
excess space is distributed.
This commit is contained in:
Thomas Geymayer 2014-08-03 12:02:39 +02:00
parent 3bcd0bafd5
commit d9b66fc0ef
7 changed files with 364 additions and 85 deletions

View File

@ -46,9 +46,11 @@ namespace canvas
}
//----------------------------------------------------------------------------
void BoxLayout::addItem(const LayoutItemRef& item, int stretch)
void BoxLayout::addItem( const LayoutItemRef& item,
int stretch,
uint8_t alignment )
{
insertItem(-1, item, stretch);
insertItem(-1, item, stretch, alignment);
}
//----------------------------------------------------------------------------
@ -64,12 +66,18 @@ namespace canvas
}
//----------------------------------------------------------------------------
void BoxLayout::insertItem(int index, const LayoutItemRef& item, int stretch)
void BoxLayout::insertItem( int index,
const LayoutItemRef& item,
int stretch,
uint8_t alignment )
{
ItemData item_data = {0};
item_data.layout_item = item;
item_data.stretch = std::max(0, stretch);
if( alignment != AlignFill )
item->setAlignment(alignment);
if( SGWeakReferenced::count(this) )
item->setParent(this);
else
@ -270,6 +278,11 @@ namespace canvas
item_data.size_hint = (item.sizeHint().*_get_layout_coord)();
item_data.has_hfw = item.hasHeightForWidth();
uint8_t alignment_mask = horiz()
? AlignHorizontal_Mask
: AlignVertical_Mask;
item_data.has_align = (item.alignment() & alignment_mask) != 0;
if( !dynamic_cast<SpacerItem*>(item_data.layout_item.get()) )
{
if( is_first )
@ -452,7 +465,10 @@ namespace canvas
_layout_data.size_hint = size_hint_save;
// and finally set the layouted geometry for each item
int fixed_size = (geom.size().*_get_fixed_coord)();
SGVec2i size( 0,
// Always assign all available space. Alignment handles final
// size.
(geom.size().*_get_fixed_coord)() );
SGVec2i cur_pos( (geom.pos().*_get_layout_coord)(),
(geom.pos().*_get_fixed_coord)() );
@ -467,16 +483,7 @@ namespace canvas
continue;
cur_pos.x() += reverse ? -data.padding - data.size : data.padding;
SGVec2i size(
data.size,
std::min( (data.layout_item->maximumSize().*_get_fixed_coord)(),
fixed_size )
);
// Center in fixed direction (TODO allow specifying alignment)
int offset_fixed = (fixed_size - size.y()) / 2;
cur_pos.y() += offset_fixed;
size.x() = data.size;
data.layout_item->setGeometry(SGRecti(
(cur_pos.*_get_layout_coord)(),
@ -487,7 +494,6 @@ namespace canvas
if( !reverse )
cur_pos.x() += data.size;
cur_pos.y() -= offset_fixed;
}
}

View File

@ -49,13 +49,18 @@ namespace canvas
virtual void addItem(const LayoutItemRef& item);
void addItem(const LayoutItemRef& item, int stretch);
void addItem( const LayoutItemRef& item,
int stretch,
uint8_t alignment = 0 );
void addStretch(int stretch = 0);
void addSpacing(int size);
void insertItem(int index, const LayoutItemRef& item, int stretch = 0);
void insertItem( int index,
const LayoutItemRef& item,
int stretch = 0,
uint8_t alignment = 0 );
void insertStretch(int index, int stretch = 0);

View File

@ -44,6 +44,19 @@ namespace canvas
takeAt(0);
}
//----------------------------------------------------------------------------
SGRecti Layout::alignmentRect(const SGRecti& geom) const
{
return alignment() == AlignFill
// Without explicit alignment (default == AlignFill) use the whole
// available space and let the layout and its items distribute the
// excess space.
? geom
// Otherwise align according to flags.
: LayoutItem::alignmentRect(geom);
}
//----------------------------------------------------------------------------
void Layout::ItemData::reset()
{
@ -56,6 +69,7 @@ namespace canvas
size = 0;
stretch = 0;
visible = false;
has_align = false;
has_hfw = false;
done = false;
}
@ -78,6 +92,16 @@ namespace canvas
return layout_item->minimumSize().y();
}
//----------------------------------------------------------------------------
Layout::Layout():
_num_not_done(0),
_sum_stretch(0),
_space_stretch(0),
_space_left(0)
{
}
//----------------------------------------------------------------------------
void Layout::contentsRectChanged(const SGRecti& rect)
{
@ -238,27 +262,59 @@ namespace canvas
else
{
_space_left = space.size - space.max_size;
int num_align = 0;
for(int i = 0; i < num_children; ++i)
{
if( items[i].visible )
_num_not_done += 1;
if( !items[i].visible )
continue;
_num_not_done += 1;
if( items[i].has_align )
num_align += 1;
}
SG_LOG(
SG_GUI,
SG_DEBUG,
"Distributing excess space:"
" not_done=" << _num_not_done
<< ", num_align=" << num_align
<< ", space_left=" << _space_left
);
for(int i = 0; i < num_children; ++i)
{
ItemData& d = items[i];
if( !d.visible )
continue;
d.padding = d.padding_orig;
d.size = d.max_size;
// Add superfluous space as padding
d.padding = d.padding_orig + _space_left
// Padding after last child...
/ (_num_not_done + 1);
int space_add = 0;
_space_left -= d.padding - d.padding_orig;
_num_not_done -= 1;
if( d.has_align )
{
// Equally distribute superfluous space and let each child items
// alignment handle the exact usage.
space_add = _space_left / num_align;
num_align -= 1;
d.size += space_add;
}
else if( num_align <= 0 )
{
// Add superfluous space as padding
space_add = _space_left
// Padding after last child...
/ (_num_not_done + 1);
_num_not_done -= 1;
d.padding += space_add;
}
_space_left -= space_add;
}
}

View File

@ -69,6 +69,18 @@ namespace canvas
*/
virtual void clear();
/**
* Get the actual geometry of this layout given the rectangle \a geom
* taking into account the alignment flags and size hints. For layouts,
* if no alignment (different to AlignFill) is set, the whole area is
* used. Excess space is distributed by the layouting algorithm and
* handled by the individual children.
*
* @param geom Area available to this layout.
* @return The resulting geometry for this layout.
*/
virtual SGRecti alignmentRect(const SGRecti& geom) const;
protected:
enum LayoutFlags
{
@ -86,6 +98,7 @@ namespace canvas
size, //!< layouted size
stretch; //!< stretch factor
bool visible : 1,
has_align: 1, //!< Has alignment factor set (!= AlignFill)
has_hfw : 1, //!< height for width
done : 1; //!< layouting done
@ -96,6 +109,8 @@ namespace canvas
int mhfw(int w) const;
};
Layout();
virtual void contentsRectChanged(const SGRecti& rect);
/**

View File

@ -76,6 +76,7 @@ namespace canvas
//----------------------------------------------------------------------------
LayoutItem::LayoutItem():
_alignment(AlignFill),
_flags(VISIBLE),
_size_hint(0, 0),
_min_size(0, 0),
@ -184,6 +185,22 @@ namespace canvas
return h < 0 ? -1 : SGMisc<int>::addClipOverflow(h, _margins.vert());
}
//----------------------------------------------------------------------------
void LayoutItem::setAlignment(uint8_t align)
{
if( align == _alignment )
return;
_alignment = align;
invalidateParent();
}
//----------------------------------------------------------------------------
uint8_t LayoutItem::alignment() const
{
return _alignment;
}
//----------------------------------------------------------------------------
void LayoutItem::setVisible(bool visible)
{
@ -232,9 +249,10 @@ namespace canvas
//----------------------------------------------------------------------------
void LayoutItem::setGeometry(const SGRecti& geom)
{
if( geom != _geometry )
SGRecti ar = alignmentRect(geom);
if( ar != _geometry )
{
_geometry = geom;
_geometry = ar;
_flags |= LAYOUT_DIRTY;
}
@ -247,6 +265,41 @@ namespace canvas
return _geometry;
}
//----------------------------------------------------------------------------
SGRecti LayoutItem::alignmentRect(const SGRecti& geom) const
{
uint8_t halign = alignment() & AlignHorizontal_Mask,
valign = alignment() & AlignVertical_Mask;
// Size
SGVec2i size = sizeHint();
if( halign == AlignFill )
size.x() = maximumSize().x();
size.x() = std::min(size.x(), geom.width());
if( valign == AlignFill )
size.y() = maximumSize().y();
else if( hasHeightForWidth() )
size.y() = heightForWidth(size.x());
size.y() = std::min(size.y(), geom.height());
// Position
SGVec2i pos = geom.pos();
if( halign & AlignRight )
pos.x() += geom.width() - size.x();
else if( !(halign & AlignLeft) )
pos.x() += (geom.width() - size.x()) / 2;
if( valign & AlignBottom )
pos.y() += geom.height() - size.y();
else if( !(valign & AlignTop) )
pos.y() += (geom.height() - size.y()) / 2;
return SGRecti(pos, pos + size);
}
//----------------------------------------------------------------------------
void LayoutItem::setCanvas(const CanvasWeakPtr& canvas)
{

View File

@ -82,6 +82,34 @@ namespace canvas
bool isNull() const;
};
/**
* Flags for LayoutItem alignment inside Layouts.
*
* @note You can only use one horizontal and one vertical flag at the same.
*/
enum AlignmentFlag
{
AlignFill = 0, //!< Use all available space
AlignLeft = 0x01, //!< Align with left edge
AlignRight = 0x02, //!< Align with right edge
AlignHCenter = 0x04, //!< Center horizontally in available space
AlignTop = 0x20, //!< Align with top edge
AlignBottom = 0x40, //!< Align with bottom edge
AlignVCenter = 0x80, //!< Center vertically in available space
AlignCenter = AlignVCenter //!< Center both vertically and horizontally
| AlignHCenter,
AlignHorizontal_Mask = AlignLeft
| AlignRight
| AlignHCenter,
AlignVertical_Mask = AlignTop
| AlignBottom
| AlignVCenter
};
/**
* Base class for all layouting elements. Specializations either implement a
* layouting algorithm or a widget.
@ -186,6 +214,22 @@ namespace canvas
*/
int minimumHeightForWidth(int w) const;
/**
* Set alignment of item within @link{Layout Layouts}.
*
* @param alignment Bitwise combination of vertical and horizontal
* alignment flags.
* @see AlignmentFlag
*/
void setAlignment(uint8_t alignment);
/**
* Get all alignment flags.
*
* @see AlignmentFlag
*/
uint8_t alignment() const;
virtual void setVisible(bool visible);
virtual bool isVisible() const;
@ -220,6 +264,20 @@ namespace canvas
*/
virtual SGRecti geometry() const;
/**
* Get the actual geometry of this item given the rectangle \a geom
* taking into account the alignment flags and size hints.
*
* @param geom Area available to this item.
* @return The resulting geometry for this item.
*
* @see setAlignment()
* @see minimumSize()
* @see maximumSize()
* @see sizeHint()
*/
virtual SGRecti alignmentRect(const SGRecti& geom) const;
/**
* Set the canvas this item is attached to.
*/
@ -266,6 +324,7 @@ namespace canvas
SGRecti _geometry;
Margins _margins;
uint8_t _alignment;
mutable uint32_t _flags;
mutable SGVec2i _size_hint,

View File

@ -110,45 +110,45 @@ typedef SGSharedPtr<TestWidget> TestWidgetRef;
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( horizontal_layout )
{
sc::BoxLayout box_layout(sc::BoxLayout::BottomToTop);
box_layout.setSpacing(5);
sc::BoxLayoutRef box_layout(new sc::BoxLayout(sc::BoxLayout::BottomToTop));
box_layout->setSpacing(5);
BOOST_CHECK_EQUAL(box_layout.direction(), sc::BoxLayout::BottomToTop);
BOOST_CHECK_EQUAL(box_layout.spacing(), 5);
BOOST_CHECK_EQUAL(box_layout->direction(), sc::BoxLayout::BottomToTop);
BOOST_CHECK_EQUAL(box_layout->spacing(), 5);
box_layout.setDirection(sc::BoxLayout::LeftToRight);
box_layout.setSpacing(9);
box_layout->setDirection(sc::BoxLayout::LeftToRight);
box_layout->setSpacing(9);
BOOST_CHECK_EQUAL(box_layout.direction(), sc::BoxLayout::LeftToRight);
BOOST_CHECK_EQUAL(box_layout.spacing(), 9);
BOOST_CHECK_EQUAL(box_layout->direction(), sc::BoxLayout::LeftToRight);
BOOST_CHECK_EQUAL(box_layout->spacing(), 9);
TestWidgetRef fixed_size_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(16, 16),
SGVec2i(16, 16) ) );
box_layout.addItem(fixed_size_widget);
box_layout->addItem(fixed_size_widget);
BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(16, 16));
BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(16, 16));
BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(16, 16));
BOOST_CHECK_EQUAL(box_layout->minimumSize(), SGVec2i(16, 16));
BOOST_CHECK_EQUAL(box_layout->sizeHint(), SGVec2i(16, 16));
BOOST_CHECK_EQUAL(box_layout->maximumSize(), SGVec2i(16, 16));
TestWidgetRef limited_resize_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(256, 64) ) );
box_layout.addItem(limited_resize_widget);
box_layout->addItem(limited_resize_widget);
// Combined sizes of both widget plus the padding between them
BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(41, 16));
BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(57, 32));
BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(281, 64));
BOOST_CHECK_EQUAL(box_layout->minimumSize(), SGVec2i(41, 16));
BOOST_CHECK_EQUAL(box_layout->sizeHint(), SGVec2i(57, 32));
BOOST_CHECK_EQUAL(box_layout->maximumSize(), SGVec2i(281, 64));
// Test with different spacing/padding
box_layout.setSpacing(5);
box_layout->setSpacing(5);
BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(37, 16));
BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(53, 32));
BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(277, 64));
BOOST_CHECK_EQUAL(box_layout->minimumSize(), SGVec2i(37, 16));
BOOST_CHECK_EQUAL(box_layout->sizeHint(), SGVec2i(53, 32));
BOOST_CHECK_EQUAL(box_layout->maximumSize(), SGVec2i(277, 64));
box_layout.setGeometry(SGRecti(0, 0, 128, 32));
box_layout->setGeometry(SGRecti(0, 0, 128, 32));
// Fixed size for first widget and remaining space goes to second widget
BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(0, 8, 16, 16));
@ -157,12 +157,12 @@ BOOST_AUTO_TEST_CASE( horizontal_layout )
TestWidgetRef stretch_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(128, 32) ) );
box_layout.addItem(stretch_widget, 1);
box_layout.update();
box_layout->addItem(stretch_widget, 1);
box_layout->update();
BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(58, 16));
BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(90, 32));
BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(410, 64));
BOOST_CHECK_EQUAL(box_layout->minimumSize(), SGVec2i(58, 16));
BOOST_CHECK_EQUAL(box_layout->sizeHint(), SGVec2i(90, 32));
BOOST_CHECK_EQUAL(box_layout->maximumSize(), SGVec2i(410, 64));
// Due to the stretch factor only the last widget gets additional space. All
// other widgets get the preferred size.
@ -172,63 +172,101 @@ BOOST_AUTO_TEST_CASE( horizontal_layout )
// Test stretch factor
TestWidgetRef fast_stretch( new TestWidget(*stretch_widget) );
sc::BoxLayout box_layout_stretch(sc::BoxLayout::LeftToRight);
sc::BoxLayoutRef box_layout_stretch(
new sc::BoxLayout(sc::BoxLayout::LeftToRight)
);
box_layout_stretch.addItem(stretch_widget, 1);
box_layout_stretch.addItem(fast_stretch, 2);
box_layout_stretch->addItem(stretch_widget, 1);
box_layout_stretch->addItem(fast_stretch, 2);
box_layout_stretch.setGeometry(SGRecti(0,0,128,32));
box_layout_stretch->setGeometry(SGRecti(0,0,128,32));
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 41, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(46, 0, 82, 32));
box_layout_stretch.setGeometry(SGRecti(0,0,256,32));
box_layout_stretch->setGeometry(SGRecti(0,0,256,32));
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 123, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(128, 0, 128, 32));
// Test superflous space to padding
box_layout_stretch.setGeometry(SGRecti(0,0,512,32));
box_layout_stretch->setGeometry(SGRecti(0,0,512,32));
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(83, 0, 128, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(300, 0, 128, 32));
// Test more space then preferred, but less than maximum
{
sc::HBoxLayout hbox;
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(9999, 32) ) ),
w2( new TestWidget(*w1) );
// ...and now with alignment
//
// All widgets without alignment get their maximum space and the remaining
// space is equally distributed to the remaining items. All items with
// alignment are set to their size hint and positioned according to their
// alignment.
hbox.addItem(w1);
hbox.addItem(w2);
// Left widget: size hint and positioned on the left
// Right widget: maximum size and positioned on the right
stretch_widget->setAlignment(sc::AlignLeft);
box_layout_stretch->update();
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 32, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(384, 0, 128, 32));
hbox.setGeometry( SGRecti(0, 0, 256, 32) );
// Left widget: align right
stretch_widget->setAlignment(sc::AlignRight);
box_layout_stretch->update();
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(347, 0, 32, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(384, 0, 128, 32));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 126, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(131, 0, 125, 32));
// Left widget: size hint and positioned on the right
// Right widget: size hint and positioned on the left of the right half
fast_stretch->setAlignment(sc::AlignLeft);
box_layout_stretch->update();
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(221, 0, 32, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(258, 0, 32, 32));
hbox.setStretch(0, 1);
hbox.setStretch(1, 1);
// Also check vertical alignment
stretch_widget->setAlignment(sc::AlignLeft | sc::AlignTop);
fast_stretch->setAlignment(sc::AlignLeft | sc::AlignBottom);
box_layout_stretch->setGeometry(SGRecti(0,0,512,64));
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 32, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(258, 32, 32, 32));
}
BOOST_CHECK_EQUAL(hbox.stretch(0), 1);
BOOST_CHECK_EQUAL(hbox.stretch(1), 1);
//------------------------------------------------------------------------------
// Test more space then preferred, but less than maximum
BOOST_AUTO_TEST_CASE( hbox_pref_to_max )
{
sc::BoxLayoutRef hbox(new sc::HBoxLayout());
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(9999, 32) ) ),
w2( new TestWidget(*w1) );
hbox.update();
hbox->addItem(w1);
hbox->addItem(w2);
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 125, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(130, 0, 126, 32));
hbox->setGeometry( SGRecti(0, 0, 256, 32) );
BOOST_REQUIRE( hbox.setStretchFactor(w1, 2) );
BOOST_REQUIRE( hbox.setStretchFactor(w2, 3) );
BOOST_CHECK_EQUAL(hbox.stretch(0), 2);
BOOST_CHECK_EQUAL(hbox.stretch(1), 3);
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 126, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(131, 0, 125, 32));
hbox.removeItem(w1);
hbox->setStretch(0, 1);
hbox->setStretch(1, 1);
BOOST_CHECK( !hbox.setStretchFactor(w1, 0) );
}
BOOST_CHECK_EQUAL(hbox->stretch(0), 1);
BOOST_CHECK_EQUAL(hbox->stretch(1), 1);
hbox->update();
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 125, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(130, 0, 126, 32));
BOOST_REQUIRE( hbox->setStretchFactor(w1, 2) );
BOOST_REQUIRE( hbox->setStretchFactor(w2, 3) );
BOOST_CHECK_EQUAL(hbox->stretch(0), 2);
BOOST_CHECK_EQUAL(hbox->stretch(1), 3);
hbox->removeItem(w1);
BOOST_CHECK( !hbox->setStretchFactor(w1, 0) );
}
//------------------------------------------------------------------------------
@ -452,6 +490,7 @@ BOOST_AUTO_TEST_CASE( boxlayout_contents_margins )
hbox->setGeometry(SGRecti(0, 0, 30, 40));
BOOST_CHECK_EQUAL(hbox->geometry(), SGRecti(0, 0, 30, 40));
BOOST_CHECK_EQUAL(hbox->contentsRect(), SGRecti(5, 10, 10, 10));
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
@ -587,6 +626,52 @@ BOOST_AUTO_TEST_CASE( boxlayout_hfw )
BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 90, 24, 32));
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( item_alignment_rect )
{
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32) ) );
const SGRecti r(10, 10, 64, 64);
// Default: AlignFill -> fill up to maximum size
BOOST_CHECK_EQUAL(w1->alignmentRect(r), r);
// Horizontal
// AlignLeft -> width from size hint, positioned on the left
w1->setAlignment(sc::AlignLeft);
BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(10, 10, 32, 64));
// AlignRight -> width from size hint, positioned on the left
w1->setAlignment(sc::AlignRight);
BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(42, 10, 32, 64));
// AlignHCenter -> width from size hint, positioned in the center
w1->setAlignment(sc::AlignHCenter);
BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(26, 10, 32, 64));
// Vertical
// AlignTop -> height from size hint, positioned on the top
w1->setAlignment(sc::AlignTop);
BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(10, 10, 64, 32));
// AlignBottom -> height from size hint, positioned on the bottom
w1->setAlignment(sc::AlignBottom);
BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(10, 42, 64, 32));
// AlignVCenter -> height from size hint, positioned in the center
w1->setAlignment(sc::AlignVCenter);
BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(10, 26, 64, 32));
// Vertical + Horizontal
w1->setAlignment(sc::AlignCenter);
BOOST_CHECK_EQUAL(w1->alignmentRect(r), SGRecti(26, 26, 32, 32));
}
//------------------------------------------------------------------------------
// TODO extend to_nasal_helper for automatic argument conversion
static naRef f_Widget_visibilityChanged(nasal::CallContext ctx)