canvas::BoxLayout: add custom additional whitespace (spacer/stretch)

This commit is contained in:
Thomas Geymayer 2014-06-08 23:30:11 +02:00
parent c54e3f8101
commit e508ff724c
8 changed files with 207 additions and 24 deletions

View File

@ -17,6 +17,7 @@
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "BoxLayout.hxx"
#include "SpacerItem.hxx"
#include <simgear/canvas/Canvas.hxx>
namespace simgear
@ -39,6 +40,24 @@ namespace canvas
//----------------------------------------------------------------------------
void BoxLayout::addItem(const LayoutItemRef& item, int stretch)
{
insertItem(-1, item, stretch);
}
//----------------------------------------------------------------------------
void BoxLayout::addStretch(int stretch)
{
insertStretch(-1, stretch);
}
//----------------------------------------------------------------------------
void BoxLayout::addSpacing(int size)
{
insertSpacing(-1, size);
}
//----------------------------------------------------------------------------
void BoxLayout::insertItem(int index, const LayoutItemRef& item, int stretch)
{
ItemData item_data = {0};
item_data.layout_item = item;
@ -47,11 +66,31 @@ namespace canvas
item->setCanvas(_canvas);
item->setParent(this);
_layout_items.push_back(item_data);
if( index < 0 )
_layout_items.push_back(item_data);
else
_layout_items.insert(_layout_items.begin() + index, item_data);
invalidate();
}
//----------------------------------------------------------------------------
void BoxLayout::insertStretch(int index, int stretch)
{
insertItem(index, LayoutItemRef(new SpacerItem()), stretch);
}
//----------------------------------------------------------------------------
void BoxLayout::insertSpacing(int index, int size)
{
SGVec2i size_hint = horiz()
? SGVec2i(size, 0)
: SGVec2i(0, size),
max_size = size_hint;
insertItem(index, LayoutItemRef(new SpacerItem(size_hint, max_size)));
}
//----------------------------------------------------------------------------
void BoxLayout::setStretch(size_t index, int stretch)
{
@ -90,24 +129,20 @@ namespace canvas
//----------------------------------------------------------------------------
void BoxLayout::setDirection(Direction dir)
{
_direction = dir;
_get_layout_coord = &SGVec2i::x;
_get_fixed_coord = &SGVec2i::y;
if( dir == TopToBottom || dir == BottomToTop )
if( !horiz() )
std::swap(_get_layout_coord, _get_fixed_coord);
_reverse = (dir == RightToLeft || dir == BottomToTop );
invalidate();
}
//----------------------------------------------------------------------------
BoxLayout::Direction BoxLayout::direction() const
{
if( _get_layout_coord == static_cast<CoordGetter>(&SGVec2i::x) )
return _reverse ? RightToLeft : LeftToRight;
else
return _reverse ? BottomToTop : TopToBottom;
return _direction;
}
//----------------------------------------------------------------------------
@ -119,6 +154,12 @@ namespace canvas
_layout_items[i].layout_item->setCanvas(canvas);
}
//----------------------------------------------------------------------------
bool BoxLayout::horiz() const
{
return (_direction == LeftToRight) || (_direction == RightToLeft);
}
//----------------------------------------------------------------------------
void BoxLayout::updateSizeHints() const
{
@ -140,15 +181,19 @@ namespace canvas
item_data.max_size = (item.maximumSize().*_get_layout_coord)();
item_data.size_hint = (item.sizeHint().*_get_layout_coord)();
if( is_first )
if( !dynamic_cast<SpacerItem*>(item_data.layout_item.get()) )
{
item_data.padding_orig = 0;
is_first = false;
if( is_first )
{
item_data.padding_orig = 0;
is_first = false;
}
else
{
item_data.padding_orig = _padding;
_layout_data.padding += item_data.padding_orig;
}
}
else
item_data.padding_orig = _padding;
_layout_data.padding += item_data.padding_orig;
// Add sizes of all children in layout direction
safeAdd(min_size.x(), item_data.min_size);
@ -160,7 +205,7 @@ namespace canvas
(item.minimumSize().*_get_fixed_coord)() );
max_size.y() = std::max( max_size.y(),
(item.maximumSize().*_get_fixed_coord)() );
size_hint.y() = std::max( min_size.y(),
size_hint.y() = std::max( size_hint.y(),
(item.sizeHint().*_get_fixed_coord)() );
}
@ -216,14 +261,16 @@ namespace canvas
int fixed_size = (geom.size().*_get_fixed_coord)();
SGVec2i cur_pos( (geom.pos().*_get_layout_coord)(),
(geom.pos().*_get_fixed_coord)() );
if( _reverse )
bool reverse = (_direction == RightToLeft) || (_direction == BottomToTop);
if( reverse )
cur_pos.x() += (geom.size().*_get_layout_coord)();
// TODO handle reverse layouting (rtl/btt)
for(size_t i = 0; i < _layout_items.size(); ++i)
{
ItemData const& data = _layout_items[i];
cur_pos.x() += _reverse ? -data.padding - data.size : data.padding;
cur_pos.x() += reverse ? -data.padding - data.size : data.padding;
SGVec2i size(
data.size,
@ -242,7 +289,7 @@ namespace canvas
(size.*_get_fixed_coord)()
));
if( !_reverse )
if( !reverse )
cur_pos.x() += data.size;
cur_pos.y() -= offset_fixed;
}

View File

@ -45,6 +45,16 @@ namespace canvas
void addItem(const LayoutItemRef& item, int stretch);
void addStretch(int stretch = 0);
void addSpacing(int size);
void insertItem(int index, const LayoutItemRef& item, int stretch = 0);
void insertStretch(int index, int stretch = 0);
void insertSpacing(int index, int size);
/**
* Set the stretch factor of the item at position @a index to @a stretch.
*/
@ -63,6 +73,8 @@ namespace canvas
virtual void setCanvas(const CanvasWeakPtr& canvas);
bool horiz() const;
protected:
typedef const int& (SGVec2i::*CoordGetter)() const;
@ -72,7 +84,7 @@ namespace canvas
// (fixed) direction
int _padding;
bool _reverse; //<! if true, right-to-left/bottom-to-top layouting
Direction _direction;
mutable std::vector<ItemData> _layout_items;
mutable ItemData _layout_data;

View File

@ -5,6 +5,7 @@ set(HEADERS
Layout.hxx
LayoutItem.hxx
NasalWidget.hxx
SpacerItem.hxx
)
set(SOURCES
@ -12,6 +13,7 @@ set(SOURCES
Layout.cxx
LayoutItem.cxx
NasalWidget.cxx
SpacerItem.cxx
)
simgear_scene_component(canvas-layout canvas/layout "${SOURCES}" "${HEADERS}")

View File

@ -23,10 +23,15 @@ namespace simgear
{
namespace canvas
{
const SGVec2i LayoutItem::MAX_SIZE( SGLimits<int>::max(),
SGLimits<int>::max() );
//----------------------------------------------------------------------------
LayoutItem::LayoutItem():
_flags(0)
_flags(0),
_size_hint(16, 16),
_min_size(0, 0),
_max_size(MAX_SIZE)
{
invalidate();
}
@ -132,19 +137,19 @@ namespace canvas
//----------------------------------------------------------------------------
SGVec2i LayoutItem::sizeHintImpl() const
{
return SGVec2i(16, 16);
return _size_hint;
}
//----------------------------------------------------------------------------
SGVec2i LayoutItem::minimumSizeImpl() const
{
return SGVec2i(0, 0);
return _min_size;
}
//----------------------------------------------------------------------------
SGVec2i LayoutItem::maximumSizeImpl() const
{
return SGVec2i(SGLimits<int>::max(), SGLimits<int>::max());
return _max_size;
}
} // namespace canvas

View File

@ -43,6 +43,8 @@ namespace canvas
{
public:
static const SGVec2i MAX_SIZE;
LayoutItem();
virtual ~LayoutItem();

View File

@ -0,0 +1,36 @@
// Element providing blank space in a layout.
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "SpacerItem.hxx"
namespace simgear
{
namespace canvas
{
//----------------------------------------------------------------------------
SpacerItem::SpacerItem( const SGVec2i& size,
const SGVec2i& max_size )
{
_size_hint = size;
_min_size = size;
_max_size = max_size;
}
} // namespace canvas
} // namespace simgear

View File

@ -0,0 +1,43 @@
///@file Element providing blank space in a layout.
//
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_CANVAS_SPACER_ITEM_HXX_
#define SG_CANVAS_SPACER_ITEM_HXX_
#include "LayoutItem.hxx"
namespace simgear
{
namespace canvas
{
/**
* Element for providing blank space in a layout.
*/
class SpacerItem:
public LayoutItem
{
public:
SpacerItem( const SGVec2i& size = SGVec2i(0, 0),
const SGVec2i& max_size = MAX_SIZE );
};
} // namespace canvas
} // namespace simgear
#endif /* SG_CANVAS_SPACER_ITEM_HXX_ */

View File

@ -191,6 +191,42 @@ BOOST_AUTO_TEST_CASE( horizontal_layout )
}
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( spacer_layouting )
{
sc::HBoxLayout hbox;
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32),
SGVec2i(9999, 9999) ) ),
w2( new TestWidget(*w1) );
hbox.addItem(w1);
hbox.addItem(w2);
hbox.addStretch(1);
BOOST_CHECK_EQUAL(hbox.minimumSize(), SGVec2i(37, 16));
BOOST_CHECK_EQUAL(hbox.sizeHint(), SGVec2i(69, 32));
BOOST_CHECK_EQUAL(hbox.maximumSize(), sc::LayoutItem::MAX_SIZE);
hbox.setGeometry(SGRecti(0, 0, 256, 40));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 32, 40));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(37, 0, 32, 40));
// now center with increased spacing between both widgets
hbox.insertStretch(0, 1);
hbox.insertSpacing(2, 10);
BOOST_CHECK_EQUAL(hbox.minimumSize(), SGVec2i(47, 16));
BOOST_CHECK_EQUAL(hbox.sizeHint(), SGVec2i(79, 32));
BOOST_CHECK_EQUAL(hbox.maximumSize(), sc::LayoutItem::MAX_SIZE);
hbox.update();
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(88, 0, 32, 40));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(135, 0, 32, 40));
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( vertical_layout)
{