From e508ff724cc17ffdd890eb75d69c7538f2e4d65d Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Sun, 8 Jun 2014 23:30:11 +0200 Subject: [PATCH] canvas::BoxLayout: add custom additional whitespace (spacer/stretch) --- simgear/canvas/layout/BoxLayout.cxx | 85 +++++++++++++++----- simgear/canvas/layout/BoxLayout.hxx | 14 +++- simgear/canvas/layout/CMakeLists.txt | 2 + simgear/canvas/layout/LayoutItem.cxx | 13 ++- simgear/canvas/layout/LayoutItem.hxx | 2 + simgear/canvas/layout/SpacerItem.cxx | 36 +++++++++ simgear/canvas/layout/SpacerItem.hxx | 43 ++++++++++ simgear/canvas/layout/canvas_layout_test.cxx | 36 +++++++++ 8 files changed, 207 insertions(+), 24 deletions(-) create mode 100644 simgear/canvas/layout/SpacerItem.cxx create mode 100644 simgear/canvas/layout/SpacerItem.hxx diff --git a/simgear/canvas/layout/BoxLayout.cxx b/simgear/canvas/layout/BoxLayout.cxx index cabe0ba9..5ba2ad36 100644 --- a/simgear/canvas/layout/BoxLayout.cxx +++ b/simgear/canvas/layout/BoxLayout.cxx @@ -17,6 +17,7 @@ // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA #include "BoxLayout.hxx" +#include "SpacerItem.hxx" #include 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(&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(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; } diff --git a/simgear/canvas/layout/BoxLayout.hxx b/simgear/canvas/layout/BoxLayout.hxx index 01c3077c..10537274 100644 --- a/simgear/canvas/layout/BoxLayout.hxx +++ b/simgear/canvas/layout/BoxLayout.hxx @@ -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; // _layout_items; mutable ItemData _layout_data; diff --git a/simgear/canvas/layout/CMakeLists.txt b/simgear/canvas/layout/CMakeLists.txt index f02c30fb..f15cc4a8 100644 --- a/simgear/canvas/layout/CMakeLists.txt +++ b/simgear/canvas/layout/CMakeLists.txt @@ -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}") diff --git a/simgear/canvas/layout/LayoutItem.cxx b/simgear/canvas/layout/LayoutItem.cxx index 31be629e..3ebeeddc 100644 --- a/simgear/canvas/layout/LayoutItem.cxx +++ b/simgear/canvas/layout/LayoutItem.cxx @@ -23,10 +23,15 @@ namespace simgear { namespace canvas { + const SGVec2i LayoutItem::MAX_SIZE( SGLimits::max(), + SGLimits::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::max(), SGLimits::max()); + return _max_size; } } // namespace canvas diff --git a/simgear/canvas/layout/LayoutItem.hxx b/simgear/canvas/layout/LayoutItem.hxx index 740ccd1a..6654349a 100644 --- a/simgear/canvas/layout/LayoutItem.hxx +++ b/simgear/canvas/layout/LayoutItem.hxx @@ -43,6 +43,8 @@ namespace canvas { public: + static const SGVec2i MAX_SIZE; + LayoutItem(); virtual ~LayoutItem(); diff --git a/simgear/canvas/layout/SpacerItem.cxx b/simgear/canvas/layout/SpacerItem.cxx new file mode 100644 index 00000000..9482f432 --- /dev/null +++ b/simgear/canvas/layout/SpacerItem.cxx @@ -0,0 +1,36 @@ +// Element providing blank space in a layout. +// +// Copyright (C) 2014 Thomas Geymayer +// +// 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 diff --git a/simgear/canvas/layout/SpacerItem.hxx b/simgear/canvas/layout/SpacerItem.hxx new file mode 100644 index 00000000..b3b4f4e6 --- /dev/null +++ b/simgear/canvas/layout/SpacerItem.hxx @@ -0,0 +1,43 @@ +///@file Element providing blank space in a layout. +// +// Copyright (C) 2014 Thomas Geymayer +// +// 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_ */ diff --git a/simgear/canvas/layout/canvas_layout_test.cxx b/simgear/canvas/layout/canvas_layout_test.cxx index ffbc1c90..b05afd48 100644 --- a/simgear/canvas/layout/canvas_layout_test.cxx +++ b/simgear/canvas/layout/canvas_layout_test.cxx @@ -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) {