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}; ItemData item_data = {0};
item_data.layout_item = item; item_data.layout_item = item;
item_data.stretch = std::max(0, stretch); item_data.stretch = std::max(0, stretch);
if( alignment != AlignFill )
item->setAlignment(alignment);
if( SGWeakReferenced::count(this) ) if( SGWeakReferenced::count(this) )
item->setParent(this); item->setParent(this);
else else
@ -270,6 +278,11 @@ namespace canvas
item_data.size_hint = (item.sizeHint().*_get_layout_coord)(); item_data.size_hint = (item.sizeHint().*_get_layout_coord)();
item_data.has_hfw = item.hasHeightForWidth(); 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( !dynamic_cast<SpacerItem*>(item_data.layout_item.get()) )
{ {
if( is_first ) if( is_first )
@ -452,7 +465,10 @@ namespace canvas
_layout_data.size_hint = size_hint_save; _layout_data.size_hint = size_hint_save;
// and finally set the layouted geometry for each item // 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)(), SGVec2i cur_pos( (geom.pos().*_get_layout_coord)(),
(geom.pos().*_get_fixed_coord)() ); (geom.pos().*_get_fixed_coord)() );
@ -467,16 +483,7 @@ namespace canvas
continue; continue;
cur_pos.x() += reverse ? -data.padding - data.size : data.padding; cur_pos.x() += reverse ? -data.padding - data.size : data.padding;
size.x() = data.size;
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;
data.layout_item->setGeometry(SGRecti( data.layout_item->setGeometry(SGRecti(
(cur_pos.*_get_layout_coord)(), (cur_pos.*_get_layout_coord)(),
@ -487,7 +494,6 @@ namespace canvas
if( !reverse ) if( !reverse )
cur_pos.x() += data.size; cur_pos.x() += data.size;
cur_pos.y() -= offset_fixed;
} }
} }

View File

@ -49,13 +49,18 @@ namespace canvas
virtual void addItem(const LayoutItemRef& item); 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 addStretch(int stretch = 0);
void addSpacing(int size); 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); void insertStretch(int index, int stretch = 0);

View File

@ -44,6 +44,19 @@ namespace canvas
takeAt(0); 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() void Layout::ItemData::reset()
{ {
@ -56,6 +69,7 @@ namespace canvas
size = 0; size = 0;
stretch = 0; stretch = 0;
visible = false; visible = false;
has_align = false;
has_hfw = false; has_hfw = false;
done = false; done = false;
} }
@ -78,6 +92,16 @@ namespace canvas
return layout_item->minimumSize().y(); 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) void Layout::contentsRectChanged(const SGRecti& rect)
{ {
@ -238,27 +262,59 @@ namespace canvas
else else
{ {
_space_left = space.size - space.max_size; _space_left = space.size - space.max_size;
int num_align = 0;
for(int i = 0; i < num_children; ++i) for(int i = 0; i < num_children; ++i)
{ {
if( items[i].visible ) if( !items[i].visible )
continue;
_num_not_done += 1; _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) for(int i = 0; i < num_children; ++i)
{ {
ItemData& d = items[i]; ItemData& d = items[i];
if( !d.visible ) if( !d.visible )
continue; continue;
d.padding = d.padding_orig;
d.size = d.max_size; d.size = d.max_size;
int space_add = 0;
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 // Add superfluous space as padding
d.padding = d.padding_orig + _space_left space_add = _space_left
// Padding after last child... // Padding after last child...
/ (_num_not_done + 1); / (_num_not_done + 1);
_space_left -= d.padding - d.padding_orig;
_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(); 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: protected:
enum LayoutFlags enum LayoutFlags
{ {
@ -86,6 +98,7 @@ namespace canvas
size, //!< layouted size size, //!< layouted size
stretch; //!< stretch factor stretch; //!< stretch factor
bool visible : 1, bool visible : 1,
has_align: 1, //!< Has alignment factor set (!= AlignFill)
has_hfw : 1, //!< height for width has_hfw : 1, //!< height for width
done : 1; //!< layouting done done : 1; //!< layouting done
@ -96,6 +109,8 @@ namespace canvas
int mhfw(int w) const; int mhfw(int w) const;
}; };
Layout();
virtual void contentsRectChanged(const SGRecti& rect); virtual void contentsRectChanged(const SGRecti& rect);
/** /**

View File

@ -76,6 +76,7 @@ namespace canvas
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
LayoutItem::LayoutItem(): LayoutItem::LayoutItem():
_alignment(AlignFill),
_flags(VISIBLE), _flags(VISIBLE),
_size_hint(0, 0), _size_hint(0, 0),
_min_size(0, 0), _min_size(0, 0),
@ -184,6 +185,22 @@ namespace canvas
return h < 0 ? -1 : SGMisc<int>::addClipOverflow(h, _margins.vert()); 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) void LayoutItem::setVisible(bool visible)
{ {
@ -232,9 +249,10 @@ namespace canvas
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void LayoutItem::setGeometry(const SGRecti& geom) void LayoutItem::setGeometry(const SGRecti& geom)
{ {
if( geom != _geometry ) SGRecti ar = alignmentRect(geom);
if( ar != _geometry )
{ {
_geometry = geom; _geometry = ar;
_flags |= LAYOUT_DIRTY; _flags |= LAYOUT_DIRTY;
} }
@ -247,6 +265,41 @@ namespace canvas
return _geometry; 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) void LayoutItem::setCanvas(const CanvasWeakPtr& canvas)
{ {

View File

@ -82,6 +82,34 @@ namespace canvas
bool isNull() const; 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 * Base class for all layouting elements. Specializations either implement a
* layouting algorithm or a widget. * layouting algorithm or a widget.
@ -186,6 +214,22 @@ namespace canvas
*/ */
int minimumHeightForWidth(int w) const; 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 void setVisible(bool visible);
virtual bool isVisible() const; virtual bool isVisible() const;
@ -220,6 +264,20 @@ namespace canvas
*/ */
virtual SGRecti geometry() const; 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. * Set the canvas this item is attached to.
*/ */
@ -266,6 +324,7 @@ namespace canvas
SGRecti _geometry; SGRecti _geometry;
Margins _margins; Margins _margins;
uint8_t _alignment;
mutable uint32_t _flags; mutable uint32_t _flags;
mutable SGVec2i _size_hint, mutable SGVec2i _size_hint,

View File

@ -110,45 +110,45 @@ typedef SGSharedPtr<TestWidget> TestWidgetRef;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( horizontal_layout ) BOOST_AUTO_TEST_CASE( horizontal_layout )
{ {
sc::BoxLayout box_layout(sc::BoxLayout::BottomToTop); sc::BoxLayoutRef box_layout(new sc::BoxLayout(sc::BoxLayout::BottomToTop));
box_layout.setSpacing(5); box_layout->setSpacing(5);
BOOST_CHECK_EQUAL(box_layout.direction(), sc::BoxLayout::BottomToTop); BOOST_CHECK_EQUAL(box_layout->direction(), sc::BoxLayout::BottomToTop);
BOOST_CHECK_EQUAL(box_layout.spacing(), 5); BOOST_CHECK_EQUAL(box_layout->spacing(), 5);
box_layout.setDirection(sc::BoxLayout::LeftToRight); box_layout->setDirection(sc::BoxLayout::LeftToRight);
box_layout.setSpacing(9); box_layout->setSpacing(9);
BOOST_CHECK_EQUAL(box_layout.direction(), sc::BoxLayout::LeftToRight); BOOST_CHECK_EQUAL(box_layout->direction(), sc::BoxLayout::LeftToRight);
BOOST_CHECK_EQUAL(box_layout.spacing(), 9); BOOST_CHECK_EQUAL(box_layout->spacing(), 9);
TestWidgetRef fixed_size_widget( new TestWidget( SGVec2i(16, 16), TestWidgetRef fixed_size_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(16, 16), 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->minimumSize(), SGVec2i(16, 16));
BOOST_CHECK_EQUAL(box_layout.sizeHint(), 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->maximumSize(), SGVec2i(16, 16));
TestWidgetRef limited_resize_widget( new TestWidget( SGVec2i(16, 16), TestWidgetRef limited_resize_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32), SGVec2i(32, 32),
SGVec2i(256, 64) ) ); 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 // Combined sizes of both widget plus the padding between them
BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(41, 16)); BOOST_CHECK_EQUAL(box_layout->minimumSize(), SGVec2i(41, 16));
BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(57, 32)); BOOST_CHECK_EQUAL(box_layout->sizeHint(), SGVec2i(57, 32));
BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(281, 64)); BOOST_CHECK_EQUAL(box_layout->maximumSize(), SGVec2i(281, 64));
// Test with different spacing/padding // 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->minimumSize(), SGVec2i(37, 16));
BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(53, 32)); BOOST_CHECK_EQUAL(box_layout->sizeHint(), SGVec2i(53, 32));
BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(277, 64)); 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 // Fixed size for first widget and remaining space goes to second widget
BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(0, 8, 16, 16)); 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), TestWidgetRef stretch_widget( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32), SGVec2i(32, 32),
SGVec2i(128, 32) ) ); SGVec2i(128, 32) ) );
box_layout.addItem(stretch_widget, 1); box_layout->addItem(stretch_widget, 1);
box_layout.update(); box_layout->update();
BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(58, 16)); BOOST_CHECK_EQUAL(box_layout->minimumSize(), SGVec2i(58, 16));
BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(90, 32)); BOOST_CHECK_EQUAL(box_layout->sizeHint(), SGVec2i(90, 32));
BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(410, 64)); BOOST_CHECK_EQUAL(box_layout->maximumSize(), SGVec2i(410, 64));
// Due to the stretch factor only the last widget gets additional space. All // Due to the stretch factor only the last widget gets additional space. All
// other widgets get the preferred size. // other widgets get the preferred size.
@ -172,63 +172,101 @@ BOOST_AUTO_TEST_CASE( horizontal_layout )
// Test stretch factor // Test stretch factor
TestWidgetRef fast_stretch( new TestWidget(*stretch_widget) ); 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(stretch_widget, 1);
box_layout_stretch.addItem(fast_stretch, 2); 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(stretch_widget->geometry(), SGRecti(0, 0, 41, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(46, 0, 82, 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(stretch_widget->geometry(), SGRecti(0, 0, 123, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(128, 0, 128, 32)); BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(128, 0, 128, 32));
// Test superflous space to padding // 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(stretch_widget->geometry(), SGRecti(83, 0, 128, 32));
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(300, 0, 128, 32)); BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(300, 0, 128, 32));
// ...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.
// 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));
// 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));
// 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));
// 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));
}
//------------------------------------------------------------------------------
// Test more space then preferred, but less than maximum // Test more space then preferred, but less than maximum
BOOST_AUTO_TEST_CASE( hbox_pref_to_max )
{ {
sc::HBoxLayout hbox; sc::BoxLayoutRef hbox(new sc::HBoxLayout());
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16), TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32), SGVec2i(32, 32),
SGVec2i(9999, 32) ) ), SGVec2i(9999, 32) ) ),
w2( new TestWidget(*w1) ); w2( new TestWidget(*w1) );
hbox.addItem(w1); hbox->addItem(w1);
hbox.addItem(w2); hbox->addItem(w2);
hbox.setGeometry( SGRecti(0, 0, 256, 32) ); hbox->setGeometry( SGRecti(0, 0, 256, 32) );
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 126, 32)); BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 126, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(131, 0, 125, 32)); BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(131, 0, 125, 32));
hbox.setStretch(0, 1); hbox->setStretch(0, 1);
hbox.setStretch(1, 1); hbox->setStretch(1, 1);
BOOST_CHECK_EQUAL(hbox.stretch(0), 1); BOOST_CHECK_EQUAL(hbox->stretch(0), 1);
BOOST_CHECK_EQUAL(hbox.stretch(1), 1); BOOST_CHECK_EQUAL(hbox->stretch(1), 1);
hbox.update(); hbox->update();
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 125, 32)); BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 125, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(130, 0, 126, 32)); BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(130, 0, 126, 32));
BOOST_REQUIRE( hbox.setStretchFactor(w1, 2) ); BOOST_REQUIRE( hbox->setStretchFactor(w1, 2) );
BOOST_REQUIRE( hbox.setStretchFactor(w2, 3) ); BOOST_REQUIRE( hbox->setStretchFactor(w2, 3) );
BOOST_CHECK_EQUAL(hbox.stretch(0), 2); BOOST_CHECK_EQUAL(hbox->stretch(0), 2);
BOOST_CHECK_EQUAL(hbox.stretch(1), 3); BOOST_CHECK_EQUAL(hbox->stretch(1), 3);
hbox.removeItem(w1); hbox->removeItem(w1);
BOOST_CHECK( !hbox.setStretchFactor(w1, 0) ); BOOST_CHECK( !hbox->setStretchFactor(w1, 0) );
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -452,6 +490,7 @@ BOOST_AUTO_TEST_CASE( boxlayout_contents_margins )
hbox->setGeometry(SGRecti(0, 0, 30, 40)); 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)); BOOST_CHECK_EQUAL(hbox->contentsRect(), SGRecti(5, 10, 10, 10));
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16), 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_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 // TODO extend to_nasal_helper for automatic argument conversion
static naRef f_Widget_visibilityChanged(nasal::CallContext ctx) static naRef f_Widget_visibilityChanged(nasal::CallContext ctx)