add_subdirectory(ShivaVG/src)
add_subdirectory(elements)
add_subdirectory(events)
+add_subdirectory(layout)
simgear_scene_component(canvas canvas "${SOURCES}" "${HEADERS}")
return _root_group;
}
+ //----------------------------------------------------------------------------
+ void Canvas::setLayout(const LayoutRef& layout)
+ {
+ _layout = layout;
+ _layout->setCanvas(this);
+ _status |= LAYOUT_DIRTY;
+ }
+
//----------------------------------------------------------------------------
void Canvas::enableRendering(bool force)
{
//----------------------------------------------------------------------------
void Canvas::update(double delta_time_sec)
{
- if( (!_texture.serviceable() && _status != STATUS_DIRTY)
- || (_status & CREATE_FAILED) )
+ if( _status & (CREATE_FAILED | MISSING_SIZE) )
return;
- if( _status == STATUS_DIRTY )
+ if( _status & STATUS_DIRTY )
{
_texture.setSize(_size_x, _size_y);
}
}
+ if( _layout )
+ {
+ if( (_status & LAYOUT_DIRTY) )
+ {
+ _layout->setGeometry(SGRecti(0, 0, _view_width, _view_height));
+ _status &= ~LAYOUT_DIRTY;
+ }
+ else
+ _layout->update();
+ }
+
if( _visible || _render_always )
{
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _child_canvases)
if( _view_width == w )
return;
_view_width = w;
+ _status |= LAYOUT_DIRTY;
_texture.setViewSize(_view_width, _view_height);
}
if( _view_height == h )
return;
_view_height = h;
+ _status |= LAYOUT_DIRTY;
_texture.setViewSize(_view_width, _view_height);
}
_status_msg = "Missing size-y";
else if( _status & CREATE_FAILED )
_status_msg = "Creating render target failed";
- else if( _status == STATUS_DIRTY )
+ else if( _status & STATUS_DIRTY )
_status_msg = "Creation pending...";
else
_status_msg = "Ok";
#include "ODGauge.hxx"
#include <simgear/canvas/elements/CanvasGroup.hxx>
+#include <simgear/canvas/layout/Layout.hxx>
#include <simgear/math/SGRect.hxx>
#include <simgear/props/PropertyBasedElement.hxx>
#include <simgear/props/propertyObject.hxx>
enum StatusFlags
{
STATUS_OK,
- STATUS_DIRTY = 1,
- MISSING_SIZE_X = STATUS_DIRTY << 1,
+ STATUS_DIRTY = 1,
+ LAYOUT_DIRTY = STATUS_DIRTY << 1,
+ MISSING_SIZE_X = LAYOUT_DIRTY << 1,
MISSING_SIZE_Y = MISSING_SIZE_X << 1,
+ MISSING_SIZE = MISSING_SIZE_X | MISSING_SIZE_Y,
CREATE_FAILED = MISSING_SIZE_Y << 1
};
*/
GroupPtr getRootGroup();
+ /**
+ * Set the layout of the canvas (the layout will automatically update with
+ * the viewport size of the canvas)
+ */
+ void setLayout(const LayoutRef& layout);
+
/**
* Enable rendering for the next frame
*
_visible;
ODGauge _texture;
- GroupPtr _root_group;
+
+ GroupPtr _root_group;
+ LayoutRef _layout;
CullCallbackPtr _cull_callback;
bool _render_always; //<! Used to disable automatic lazy rendering (culling)
void Window::setCanvasContent(CanvasPtr canvas)
{
_canvas_content = canvas;
+ if( _layout )
+ {
+ canvas->setLayout(_layout);
+ _layout.clear();
+ }
if( _image_content )
// Placement within decoration canvas
return _canvas_content;
}
+ //----------------------------------------------------------------------------
+ void Window::setLayout(const LayoutRef& layout)
+ {
+ CanvasPtr canvas = _canvas_content.lock();
+ if( canvas )
+ canvas->setLayout(layout);
+ else
+ _layout = layout; // keep layout until content canvas is set
+ }
+
//----------------------------------------------------------------------------
CanvasPtr Window::getCanvasDecoration() const
{
#define CANVAS_WINDOW_HXX_
#include <simgear/canvas/elements/CanvasImage.hxx>
+#include <simgear/canvas/layout/Layout.hxx>
#include <simgear/canvas/events/MouseEvent.hxx>
#include <simgear/props/PropertyBasedElement.hxx>
#include <simgear/props/propertyObject.hxx>
{
class Window:
- public Image
+ public Image,
+ public LayoutItem
{
public:
static const std::string TYPE_NAME;
void setCanvasContent(CanvasPtr canvas);
simgear::canvas::CanvasWeakPtr getCanvasContent() const;
+ void setLayout(const LayoutRef& layout);
+
CanvasPtr getCanvasDecoration() const;
bool isResizable() const;
CanvasPtr _canvas_decoration;
CanvasWeakPtr _canvas_content;
+ LayoutRef _layout;
ImagePtr _image_content,
_image_shadow;
/**
* Get bounding box (may not be as tight as bounding box returned by
- * #getTightBounds)
+ * #getTightBoundingBox)
*/
osg::BoundingBox getBoundingBox() const;
--- /dev/null
+// Align items horizontally or vertically in a box
+//
+// 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 "BoxLayout.hxx"
+#include <simgear/canvas/Canvas.hxx>
+
+namespace simgear
+{
+namespace canvas
+{
+
+ //----------------------------------------------------------------------------
+ BoxLayout::BoxLayout(Direction dir):
+ _padding(5)
+ {
+ setDirection(dir);
+ }
+
+ //----------------------------------------------------------------------------
+ void BoxLayout::addItem(const LayoutItemRef& item)
+ {
+ return addItem(item, 0);
+ }
+
+ //----------------------------------------------------------------------------
+ void BoxLayout::addItem(const LayoutItemRef& item, int stretch)
+ {
+ ItemData item_data = {0};
+ item_data.layout_item = item;
+ item_data.stretch = std::max(0, stretch);
+
+ item->setCanvas(_canvas);
+ item->setParent(this);
+
+ _layout_items.push_back(item_data);
+
+ invalidate();
+ }
+
+ //----------------------------------------------------------------------------
+ void BoxLayout::setStretch(size_t index, int stretch)
+ {
+ if( index >= _layout_items.size() )
+ return;
+
+ _layout_items.at(index).stretch = std::max(0, stretch);
+ invalidate();
+ }
+
+ //----------------------------------------------------------------------------
+ int BoxLayout::stretch(size_t index) const
+ {
+ if( index >= _layout_items.size() )
+ return 0;
+
+ return _layout_items.at(index).stretch;
+ }
+
+ //----------------------------------------------------------------------------
+ void BoxLayout::setSpacing(int spacing)
+ {
+ if( spacing == _padding )
+ return;
+
+ _padding = spacing;
+ invalidate();
+ }
+
+ //----------------------------------------------------------------------------
+ int BoxLayout::spacing() const
+ {
+ return _padding;
+ }
+
+ //----------------------------------------------------------------------------
+ void BoxLayout::setDirection(Direction dir)
+ {
+ _get_layout_coord = &SGVec2i::x;
+ _get_fixed_coord = &SGVec2i::y;
+
+ if( dir == TopToBottom || dir == BottomToTop )
+ 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;
+ }
+
+ //----------------------------------------------------------------------------
+ void BoxLayout::setCanvas(const CanvasWeakPtr& canvas)
+ {
+ _canvas = canvas;
+
+ for(size_t i = 0; i < _layout_items.size(); ++i)
+ _layout_items[i].layout_item->setCanvas(canvas);
+ }
+
+ //----------------------------------------------------------------------------
+ void BoxLayout::updateSizeHints() const
+ {
+ SGVec2i min_size(0, 0),
+ max_size(0, 0),
+ size_hint(0, 0);
+
+ _layout_data.reset();
+ bool is_first = true;
+
+ for(size_t i = 0; i < _layout_items.size(); ++i)
+ {
+ // TODO check visible
+
+ ItemData& item_data = _layout_items[i];
+ LayoutItem const& item = *item_data.layout_item;
+
+ item_data.min_size = (item.minimumSize().*_get_layout_coord)();
+ item_data.max_size = (item.maximumSize().*_get_layout_coord)();
+ item_data.size_hint = (item.sizeHint().*_get_layout_coord)();
+
+ if( is_first )
+ {
+ item_data.padding_orig = 0;
+ is_first = false;
+ }
+ 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);
+ safeAdd(max_size.x(), item_data.max_size);
+ safeAdd(size_hint.x(), item_data.size_hint);
+
+ // Take maximum in fixed (non-layouted) direction
+ min_size.y() = std::max( min_size.y(),
+ (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(),
+ (item.sizeHint().*_get_fixed_coord)() );
+ }
+
+ safeAdd(min_size.x(), _layout_data.padding);
+ safeAdd(max_size.x(), _layout_data.padding);
+ safeAdd(size_hint.x(), _layout_data.padding);
+
+ _layout_data.min_size = min_size.x();
+ _layout_data.max_size = max_size.x();
+ _layout_data.size_hint = size_hint.x();
+
+ _min_size.x() = (min_size.*_get_layout_coord)();
+ _max_size.x() = (max_size.*_get_layout_coord)();
+ _size_hint.x() = (size_hint.*_get_layout_coord)();
+
+ _min_size.y() = (min_size.*_get_fixed_coord)();
+ _max_size.y() = (max_size.*_get_fixed_coord)();
+ _size_hint.y() = (size_hint.*_get_fixed_coord)();
+
+ _flags &= ~SIZE_INFO_DIRTY;
+ }
+
+ //----------------------------------------------------------------------------
+ SGVec2i BoxLayout::sizeHintImpl() const
+ {
+ updateSizeHints();
+ return _size_hint;
+ }
+
+ //----------------------------------------------------------------------------
+ SGVec2i BoxLayout::minimumSizeImpl() const
+ {
+ updateSizeHints();
+ return _min_size;
+ }
+
+ //----------------------------------------------------------------------------
+ SGVec2i BoxLayout::maximumSizeImpl() const
+ {
+ updateSizeHints();
+ return _max_size;
+ }
+
+ //----------------------------------------------------------------------------
+ void BoxLayout::doLayout(const SGRecti& geom)
+ {
+ if( _flags & SIZE_INFO_DIRTY )
+ updateSizeHints();
+
+ _layout_data.size = (geom.size().*_get_layout_coord)();
+ distribute(_layout_items, _layout_data);
+
+ int fixed_size = (geom.size().*_get_fixed_coord)();
+ SGVec2i cur_pos( (geom.pos().*_get_layout_coord)(),
+ (geom.pos().*_get_fixed_coord)() );
+ 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;
+
+ 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(
+ (cur_pos.*_get_layout_coord)(),
+ (cur_pos.*_get_fixed_coord)(),
+ (size.*_get_layout_coord)(),
+ (size.*_get_fixed_coord)()
+ ));
+
+ if( !_reverse )
+ cur_pos.x() += data.size;
+ cur_pos.y() -= offset_fixed;
+ }
+ }
+
+ //----------------------------------------------------------------------------
+ HBoxLayout::HBoxLayout():
+ BoxLayout(LeftToRight)
+ {
+
+ }
+
+ //----------------------------------------------------------------------------
+ VBoxLayout::VBoxLayout():
+ BoxLayout(TopToBottom)
+ {
+
+ }
+
+} // namespace canvas
+} // namespace simgear
--- /dev/null
+// Align items horizontally or vertically in a box
+//
+// 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_BOX_LAYOUT_HXX_
+#define SG_CANVAS_BOX_LAYOUT_HXX_
+
+#include "Layout.hxx"
+
+namespace simgear
+{
+namespace canvas
+{
+
+ class BoxLayout:
+ public Layout
+ {
+ public:
+
+ enum Direction
+ {
+ LeftToRight,
+ RightToLeft,
+ TopToBottom,
+ BottomToTop
+ };
+
+ BoxLayout(Direction dir);
+
+ virtual void addItem(const LayoutItemRef& item);
+
+ void addItem(const LayoutItemRef& item, int stretch);
+
+ /**
+ * Set the stretch factor of the item at position @a index to @a stretch.
+ */
+ void setStretch(size_t index, int stretch);
+
+ /**
+ * Get the stretch factor of the item at position @a index
+ */
+ int stretch(size_t index) const;
+
+ virtual void setSpacing(int spacing);
+ virtual int spacing() const;
+
+ void setDirection(Direction dir);
+ Direction direction() const;
+
+ virtual void setCanvas(const CanvasWeakPtr& canvas);
+
+ protected:
+
+ typedef const int& (SGVec2i::*CoordGetter)() const;
+ CoordGetter _get_layout_coord, //<! getter for coordinate in layout
+ // direction
+ _get_fixed_coord; //<! getter for coordinate in secondary
+ // (fixed) direction
+
+ int _padding;
+ bool _reverse; //<! if true, right-to-left/bottom-to-top layouting
+
+ mutable std::vector<ItemData> _layout_items;
+ mutable ItemData _layout_data;
+
+ void updateSizeHints() const;
+
+ virtual SGVec2i sizeHintImpl() const;
+ virtual SGVec2i minimumSizeImpl() const;
+ virtual SGVec2i maximumSizeImpl() const;
+
+ virtual void doLayout(const SGRecti& geom);
+ };
+
+ /**
+ * Shortcut for creating a horizontal box layout
+ */
+ class HBoxLayout:
+ public BoxLayout
+ {
+ public:
+ HBoxLayout();
+ };
+
+ /**
+ * Shortcut for creating a vertical box layout
+ */
+ class VBoxLayout:
+ public BoxLayout
+ {
+ public:
+ VBoxLayout();
+ };
+
+} // namespace canvas
+} // namespace simgear
+
+
+#endif /* SG_CANVAS_BOX_LAYOUT_HXX_ */
+
--- /dev/null
+include (SimGearComponent)
+
+set(HEADERS
+ BoxLayout.hxx
+ Layout.hxx
+ LayoutItem.hxx
+ NasalWidget.hxx
+)
+
+set(SOURCES
+ BoxLayout.cxx
+ Layout.cxx
+ LayoutItem.cxx
+ NasalWidget.cxx
+)
+
+simgear_scene_component(canvas-layout canvas/layout "${SOURCES}" "${HEADERS}")
+
+add_boost_test(canvas_layout
+ SOURCES canvas_layout_test.cxx
+ LIBRARIES ${TEST_LIBS}
+)
\ No newline at end of file
--- /dev/null
+// Basic class for canvas layouts
+//
+// 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 "Layout.hxx"
+#include <simgear/debug/logstream.hxx>
+
+namespace simgear
+{
+namespace canvas
+{
+
+ //----------------------------------------------------------------------------
+ void Layout::update()
+ {
+ if( !(_flags & (LAYOUT_DIRTY | SIZE_INFO_DIRTY)) )
+ return;
+
+ doLayout(_geometry);
+
+ _flags &= ~LAYOUT_DIRTY;
+ }
+
+ //----------------------------------------------------------------------------
+ void Layout::invalidate()
+ {
+ LayoutItem::invalidate();
+ _flags |= LAYOUT_DIRTY;
+ }
+
+ //----------------------------------------------------------------------------
+ void Layout::setGeometry(const SGRecti& geom)
+ {
+ if( geom == _geometry )
+ return;
+
+ _geometry = geom;
+ _flags |= LAYOUT_DIRTY;
+
+ update();
+ }
+
+ //----------------------------------------------------------------------------
+ void Layout::ItemData::reset()
+ {
+ layout_item = 0;
+ size_hint = 0;
+ min_size = 0;
+ max_size = 0;
+ padding_orig= 0;
+ padding = 0;
+ size = 0;
+ stretch = 0;
+ done = false;
+ }
+
+ //----------------------------------------------------------------------------
+ void Layout::safeAdd(int& a, int b)
+ {
+ if( SGLimits<int>::max() - b < a )
+ a = SGLimits<int>::max();
+ else
+ a += b;
+ }
+
+ //----------------------------------------------------------------------------
+ void Layout::distribute(std::vector<ItemData>& items, const ItemData& space)
+ {
+ const int num_children = static_cast<int>(items.size());
+ _num_not_done = num_children;
+
+ SG_LOG( SG_GUI,
+ SG_DEBUG,
+ "Layout::distribute(" << num_children << " items)" );
+
+ if( space.size < space.min_size )
+ {
+ // TODO
+ SG_LOG( SG_GUI, SG_WARN, "Layout: not enough size (not implemented)");
+ }
+ else if( space.size < space.max_size )
+ {
+ _sum_stretch = 0;
+ _space_stretch = 0;
+
+ bool less_then_hint = space.size < space.size_hint;
+
+ // Give min_size/size_hint to all items
+ _space_left = space.size
+ - (less_then_hint ? space.min_size : space.size_hint);
+ for(int i = 0; i < num_children; ++i)
+ {
+ ItemData& d = items[i];
+ d.size = less_then_hint ? d.min_size : d.size_hint;
+ d.padding = d.padding_orig;
+ d.done = d.size >= (less_then_hint ? d.size_hint : d.max_size);
+
+ if( d.done )
+ {
+ _num_not_done -= 1;
+ continue;
+ }
+
+ if( d.stretch > 0 )
+ {
+ _sum_stretch += d.stretch;
+ _space_stretch += d.size;
+ }
+ }
+
+ // Distribute remaining space to increase the size of each item up to its
+ // size_hint/max_size
+ while( _space_left > 0 )
+ {
+ if( _num_not_done <= 0 )
+ {
+ SG_LOG(SG_GUI, SG_WARN, "space left, but no more items?");
+ break;
+ }
+
+ int space_per_element = std::max(1, _space_left / _num_not_done);
+
+ SG_LOG(SG_GUI, SG_DEBUG, "space/element=" << space_per_element);
+
+ for(int i = 0; i < num_children; ++i)
+ {
+ ItemData& d = items[i];
+
+ SG_LOG(
+ SG_GUI,
+ SG_DEBUG,
+ i << ") left=" << _space_left
+ << ", not_done=" << _num_not_done
+ << ", sum=" << _sum_stretch
+ << ", stretch=" << _space_stretch
+ << ", stretch/unit=" << _space_stretch / std::max(1, _sum_stretch)
+ );
+
+ if( d.done )
+ continue;
+
+ if( _sum_stretch > 0 && d.stretch <= 0 )
+ d.done = true;
+ else
+ {
+ int target_size = 0;
+ int max_size = less_then_hint ? d.size_hint : d.max_size;
+
+ if( _sum_stretch > 0 )
+ {
+ target_size = (d.stretch * (_space_left + _space_stretch))
+ / _sum_stretch;
+
+ // Item would be smaller than minimum size or larger than maximum
+ // size, so just keep bounded size and ignore stretch factor
+ if( target_size <= d.size || target_size >= max_size )
+ {
+ d.done = true;
+ _sum_stretch -= d.stretch;
+ _space_stretch -= d.size;
+
+ if( target_size >= max_size )
+ target_size = max_size;
+ else
+ target_size = d.size;
+ }
+ else
+ _space_stretch += target_size - d.size;
+ }
+ else
+ {
+ // Give space evenly to all remaining elements in this round
+ target_size = d.size + std::min(_space_left, space_per_element);
+
+ if( target_size >= max_size )
+ {
+ d.done = true;
+ target_size = max_size;
+ }
+ }
+
+ int old_size = d.size;
+ d.size = target_size;
+ _space_left -= d.size - old_size;
+ }
+
+ if( d.done )
+ {
+ _num_not_done -= 1;
+
+ if( _sum_stretch <= 0 && d.stretch > 0 )
+ // Distribute remaining space evenly to all non-stretchable items
+ // in a new round
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ _space_left = space.size - space.max_size;
+ for(int i = 0; i < num_children; ++i)
+ {
+ ItemData& d = items[i];
+ 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);
+
+ _space_left -= d.padding - d.padding_orig;
+ _num_not_done -= 1;
+ }
+ }
+
+ SG_LOG(SG_GUI, SG_DEBUG, "distribute:");
+ for(int i = 0; i < num_children; ++i)
+ {
+ ItemData const& d = items[i];
+ SG_LOG( SG_GUI,
+ SG_DEBUG,
+ i << ") pad=" << d.padding
+ << ", size = " << d.size );
+ }
+ }
+
+} // namespace canvas
+} // namespace simgear
--- /dev/null
+// Basic class for canvas layouts
+//
+// 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_LAYOUT_HXX_
+#define SG_CANVAS_LAYOUT_HXX_
+
+#include "LayoutItem.hxx"
+#include <vector>
+
+namespace simgear
+{
+namespace canvas
+{
+
+ class Layout:
+ public LayoutItem
+ {
+ public:
+ void update();
+
+ virtual void invalidate();
+ virtual void setGeometry(const SGRecti& geom);
+
+ virtual void addItem(const LayoutItemRef& item) = 0;
+ virtual void setSpacing(int spacing) = 0;
+ virtual int spacing() const = 0;
+
+ protected:
+ enum LayoutFlags
+ {
+ LAYOUT_DIRTY = LayoutItem::LAST_FLAG << 1,
+ LAST_FLAG = LAYOUT_DIRTY
+ };
+
+ struct ItemData
+ {
+ LayoutItemRef layout_item;
+ int size_hint,
+ min_size,
+ max_size,
+ padding_orig, //<! original padding as specified by the user
+ padding, //<! padding before element (layouted)
+ size, //<! layouted size
+ stretch; //<! stretch factor
+ bool done; //<! layouting done
+
+ /** Clear values (reset to default/empty state) */
+ void reset();
+ };
+
+ /**
+ * Override to implement the actual layouting
+ */
+ virtual void doLayout(const SGRecti& geom) = 0;
+
+ /**
+ * Add two integers taking care of overflow (limit to INT_MAX)
+ */
+ static void safeAdd(int& a, int b);
+
+ /**
+ * Distribute the available @a space to all @a items
+ */
+ void distribute(std::vector<ItemData>& items, const ItemData& space);
+
+ private:
+
+ int _num_not_done, //<! number of children not layouted yet
+ _sum_stretch, //<! sum of stretch factors of all not yet layouted
+ // children
+ _space_stretch,//<! space currently assigned to all not yet layouted
+ // stretchable children
+ _space_left; //<! remaining space not used by any child yet
+
+ };
+
+ typedef SGSharedPtr<Layout> LayoutRef;
+
+} // namespace canvas
+} // namespace simgear
+
+
+#endif /* SG_CANVAS_LAYOUT_HXX_ */
--- /dev/null
+// Basic element for layouting canvas elements
+//
+// 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 "LayoutItem.hxx"
+#include <simgear/canvas/Canvas.hxx>
+
+namespace simgear
+{
+namespace canvas
+{
+
+ //----------------------------------------------------------------------------
+ LayoutItem::LayoutItem():
+ _flags(0)
+ {
+ invalidate();
+ }
+
+ //----------------------------------------------------------------------------
+ LayoutItem::~LayoutItem()
+ {
+
+ }
+
+ //----------------------------------------------------------------------------
+ SGVec2i LayoutItem::sizeHint() const
+ {
+ if( _flags & SIZE_HINT_DIRTY )
+ {
+ _size_hint = sizeHintImpl();
+ _flags &= ~SIZE_HINT_DIRTY;
+ }
+
+ return _size_hint;
+ }
+
+ //----------------------------------------------------------------------------
+ SGVec2i LayoutItem::minimumSize() const
+ {
+ if( _flags & MINIMUM_SIZE_DIRTY )
+ {
+ _min_size = minimumSizeImpl();
+ _flags &= ~MINIMUM_SIZE_DIRTY;
+ }
+
+ return _min_size;
+ }
+
+ //----------------------------------------------------------------------------
+ SGVec2i LayoutItem::maximumSize() const
+ {
+ if( _flags & MAXIMUM_SIZE_DIRTY )
+ {
+ _max_size = maximumSizeImpl();
+ _flags &= ~MAXIMUM_SIZE_DIRTY;
+ }
+
+ return _max_size;
+ }
+
+ //----------------------------------------------------------------------------
+ void LayoutItem::invalidate()
+ {
+ _flags |= SIZE_HINT_DIRTY
+ | MINIMUM_SIZE_DIRTY
+ | MAXIMUM_SIZE_DIRTY;
+
+ invalidateParent();
+ }
+
+ //----------------------------------------------------------------------------
+ void LayoutItem::invalidateParent()
+ {
+ LayoutItemRef parent = _parent.lock();
+ if( parent )
+ parent->invalidate();
+ }
+
+ //----------------------------------------------------------------------------
+ void LayoutItem::setGeometry(const SGRecti& geom)
+ {
+ _geometry = geom;
+ }
+
+ //----------------------------------------------------------------------------
+ SGRecti LayoutItem::geometry() const
+ {
+ return _geometry;
+ }
+
+ //----------------------------------------------------------------------------
+ void LayoutItem::setCanvas(const CanvasWeakPtr& canvas)
+ {
+ _canvas = canvas;
+ }
+
+ //----------------------------------------------------------------------------
+ CanvasPtr LayoutItem::getCanvas() const
+ {
+ return _canvas.lock();
+ }
+
+ //----------------------------------------------------------------------------
+ void LayoutItem::setParent(const LayoutItemWeakRef& parent)
+ {
+ _parent = parent;
+ LayoutItemRef parent_ref = parent.lock();
+ setCanvas(parent_ref ? parent_ref->_canvas : CanvasWeakPtr());
+ }
+
+ //----------------------------------------------------------------------------
+ LayoutItemRef LayoutItem::getParent() const
+ {
+ return _parent.lock();
+ }
+
+ //----------------------------------------------------------------------------
+ SGVec2i LayoutItem::sizeHintImpl() const
+ {
+ return SGVec2i(16, 16);
+ }
+
+ //----------------------------------------------------------------------------
+ SGVec2i LayoutItem::minimumSizeImpl() const
+ {
+ return SGVec2i(0, 0);
+ }
+
+ //----------------------------------------------------------------------------
+ SGVec2i LayoutItem::maximumSizeImpl() const
+ {
+ return SGVec2i(SGLimits<int>::max(), SGLimits<int>::max());
+ }
+
+} // namespace canvas
+} // namespace simgear
--- /dev/null
+///@file Basic element for layouting canvas elements
+//
+// 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_LAYOUT_ITEM_HXX_
+#define SG_CANVAS_LAYOUT_ITEM_HXX_
+
+#include <simgear/canvas/canvas_fwd.hxx>
+#include <simgear/math/SGMath.hxx>
+#include <simgear/math/SGRect.hxx>
+#include <simgear/misc/stdint.hxx>
+#include <simgear/structure/SGWeakReferenced.hxx>
+#include <simgear/structure/SGSharedPtr.hxx>
+
+namespace simgear
+{
+namespace canvas
+{
+ class LayoutItem;
+ typedef SGSharedPtr<LayoutItem> LayoutItemRef;
+ typedef SGWeakPtr<LayoutItem> LayoutItemWeakRef;
+
+ /**
+ * Base class for all layouting elements. Specializations either implement a
+ * layouting algorithm or a widget.
+ */
+ class LayoutItem:
+ public virtual SGVirtualWeakReferenced
+ {
+ public:
+
+ LayoutItem();
+ virtual ~LayoutItem();
+
+ /**
+ * Get the preferred size of this item.
+ */
+ SGVec2i sizeHint() const;
+
+ /**
+ * Get the minimum amount of the space this item requires.
+ */
+ SGVec2i minimumSize() const;
+
+ /**
+ * Get the maximum amount of space this item can use.
+ */
+ SGVec2i maximumSize() const;
+
+ /**
+ * Mark all cached data as invalid and require it to be recalculated.
+ */
+ virtual void invalidate();
+
+ /**
+ * Mark all cached data of parent item as invalid (if it is known)
+ */
+ void invalidateParent();
+
+ /**
+ * Set position and size of this element. For layouts this triggers a
+ * recalculation of the layout.
+ */
+ virtual void setGeometry(const SGRecti& geom);
+
+ /**
+ * Get position and size of this element.
+ */
+ virtual SGRecti geometry() const;
+
+ /**
+ * Set the canvas this item is attached to.
+ */
+ virtual void setCanvas(const CanvasWeakPtr& canvas);
+
+ /**
+ * Get the canvas this item is attached to.
+ */
+ CanvasPtr getCanvas() const;
+
+ /**
+ * Set the parent layout item (usally this is a layout).
+ */
+ void setParent(const LayoutItemWeakRef& parent);
+
+ /**
+ * Get the parent layout.
+ */
+ LayoutItemRef getParent() const;
+
+ protected:
+
+ friend class Canvas;
+
+ enum Flags
+ {
+ SIZE_HINT_DIRTY = 1,
+ MINIMUM_SIZE_DIRTY = SIZE_HINT_DIRTY << 1,
+ MAXIMUM_SIZE_DIRTY = MINIMUM_SIZE_DIRTY << 1,
+ SIZE_INFO_DIRTY = SIZE_HINT_DIRTY
+ | MINIMUM_SIZE_DIRTY
+ | MAXIMUM_SIZE_DIRTY,
+ LAST_FLAG = MAXIMUM_SIZE_DIRTY
+ };
+
+ CanvasWeakPtr _canvas;
+ LayoutItemWeakRef _parent;
+
+ SGRecti _geometry;
+
+ mutable uint32_t _flags;
+ mutable SGVec2i _size_hint,
+ _min_size,
+ _max_size;
+
+ virtual SGVec2i sizeHintImpl() const;
+ virtual SGVec2i minimumSizeImpl() const;
+ virtual SGVec2i maximumSizeImpl() const;
+
+ };
+
+} // namespace canvas
+} // namespace simgear
+
+#endif /* SG_CANVAS_LAYOUT_ITEM_HXX_ */
--- /dev/null
+// Glue for GUI layout items implemented in Nasal space
+//
+// 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 "NasalWidget.hxx"
+
+#include <simgear/canvas/Canvas.hxx>
+#include <simgear/nasal/cppbind/Ghost.hxx>
+
+namespace simgear
+{
+namespace canvas
+{
+
+ //----------------------------------------------------------------------------
+ NasalWidget::NasalWidget(naRef impl):
+ _nasal_impl(impl)
+ {
+ assert( naIsHash(_nasal_impl.get_naRef()) );
+ }
+
+ //----------------------------------------------------------------------------
+ void NasalWidget::setGeometry(const SGRect<int>& geom)
+ {
+ if( _geometry == geom )
+ return;
+
+ _geometry = geom;
+
+ if( _set_geometry )
+ _set_geometry(_nasal_impl.get_naRef(), geom);
+ }
+
+ //----------------------------------------------------------------------------
+ void NasalWidget::setSetGeometryFunc(const SetGeometryFunc& func)
+ {
+ _set_geometry = func;
+ }
+
+ //----------------------------------------------------------------------------
+ void NasalWidget::setImpl(naRef obj)
+ {
+ _nasal_impl.reset(obj);
+ }
+
+ //----------------------------------------------------------------------------
+ naRef NasalWidget::getImpl() const
+ {
+ return _nasal_impl.get_naRef();
+ }
+
+ //----------------------------------------------------------------------------
+ void NasalWidget::setSizeHint(const SGVec2i& s)
+ {
+ if( _size_hint == s )
+ return;
+
+ _size_hint = s;
+
+ // TODO just invalidate size_hint? Probably not a performance issue...
+ invalidateParent();
+ }
+
+ //----------------------------------------------------------------------------
+ void NasalWidget::setMinimumSize(const SGVec2i& s)
+ {
+ if( _min_size == s )
+ return;
+
+ _min_size = s;
+ invalidateParent();
+ }
+
+ //----------------------------------------------------------------------------
+ void NasalWidget::setMaximumSize(const SGVec2i& s)
+ {
+ if( _max_size == s )
+ return;
+
+ _max_size = s;
+ invalidateParent();
+ }
+
+ //----------------------------------------------------------------------------
+ bool NasalWidget::_set(naContext c, const std::string& key, naRef val)
+ {
+ if( !_nasal_impl.valid() )
+ return false;
+
+ nasal::Hash(_nasal_impl.get_naRef(), c).set(key, val);
+ return true;
+ }
+
+ //----------------------------------------------------------------------------
+ static naRef f_makeNasalWidget(const nasal::CallContext& ctx)
+ {
+ return ctx.to_nasal(NasalWidgetRef(
+ new NasalWidget( ctx.requireArg<naRef>(0) )
+ ));
+ }
+
+ //----------------------------------------------------------------------------
+ void NasalWidget::setupGhost(nasal::Hash& ns)
+ {
+ nasal::Ghost<NasalWidgetRef>::init("canvas.Widget")
+ .bases<LayoutItemRef>()
+ ._set(&NasalWidget::_set)
+ .member("parents", &NasalWidget::getParents)
+ .method("setSetGeometryFunc", &NasalWidget::setSetGeometryFunc)
+ .method("setSizeHint", &NasalWidget::setSizeHint)
+ .method("setMinimumSize", &NasalWidget::setMinimumSize)
+ .method("setMaximumSize", &NasalWidget::setMaximumSize);
+
+ nasal::Hash widget_hash = ns.createHash("Widget");
+ widget_hash.set("new", &f_makeNasalWidget);
+ }
+
+ //----------------------------------------------------------------------------
+ naRef NasalWidget::getParents(NasalWidget& w, naContext c)
+ {
+ naRef parents[] = { w._nasal_impl.get_naRef() };
+ return nasal::to_nasal(c, parents);
+ }
+
+ //----------------------------------------------------------------------------
+ SGVec2i NasalWidget::sizeHintImpl() const
+ {
+ return _size_hint;
+ }
+
+ //----------------------------------------------------------------------------
+ SGVec2i NasalWidget::minimumSizeImpl() const
+ {
+ return _min_size;
+ }
+
+ //----------------------------------------------------------------------------
+ SGVec2i NasalWidget::maximumSizeImpl() const
+ {
+ return _max_size;
+ }
+
+} // namespace canvas
+} // namespace simgear
--- /dev/null
+///@file Glue for GUI widgets implemented in Nasal space
+//
+// 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_NASAL_WIDGET_HXX_
+#define SG_CANVAS_NASAL_WIDGET_HXX_
+
+#include "LayoutItem.hxx"
+
+#include <simgear/nasal/cppbind/from_nasal.hxx>
+#include <simgear/nasal/cppbind/NasalHash.hxx>
+
+namespace simgear
+{
+namespace canvas
+{
+
+ /**
+ * Baseclass/ghost to create widgets with Nasal.
+ */
+ class NasalWidget:
+ public LayoutItem
+ {
+ public:
+
+ typedef boost::function<void (nasal::Me, const SGRecti&)> SetGeometryFunc;
+
+ /**
+ *
+ * @param impl Initial implementation hash (nasal part of
+ * implementation)
+ */
+ NasalWidget(naRef impl);
+
+ virtual void setGeometry(const SGRecti& geom);
+
+ void setSetGeometryFunc(const SetGeometryFunc& func);
+
+ void setImpl(naRef obj);
+ naRef getImpl() const;
+
+ void setSizeHint(const SGVec2i& s);
+ void setMinimumSize(const SGVec2i& s);
+ void setMaximumSize(const SGVec2i& s);
+
+ bool _set(naContext c, const std::string& key, naRef val);
+
+ /**
+ * @param ns Namespace to register the class interface
+ */
+ static void setupGhost(nasal::Hash& ns);
+
+ protected:
+ SetGeometryFunc _set_geometry;
+ nasal::ObjectHolder<> _nasal_impl;
+
+ static naRef getParents(NasalWidget&, naContext);
+
+ virtual SGVec2i sizeHintImpl() const;
+ virtual SGVec2i minimumSizeImpl() const;
+ virtual SGVec2i maximumSizeImpl() const;
+
+ };
+
+ typedef SGSharedPtr<NasalWidget> NasalWidgetRef;
+
+} // namespace canvas
+} // namespace simgear
+
+
+#endif /* SG_CANVAS_NASAL_WIDGET_HXX_ */
--- /dev/null
+// Testing canvas layouting system
+//
+// 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
+
+#define BOOST_TEST_MODULE canvas_layout
+#include <BoostTestTargetConfig.h>
+
+#include "BoxLayout.hxx"
+#include "NasalWidget.hxx"
+
+#include <simgear/debug/logstream.hxx>
+#include <cstdlib>
+
+//------------------------------------------------------------------------------
+struct SetLogLevelFixture
+{
+ SetLogLevelFixture()
+ {
+ sglog().set_log_priority(SG_DEBUG);
+ }
+};
+BOOST_GLOBAL_FIXTURE(SetLogLevelFixture);
+
+//------------------------------------------------------------------------------
+namespace sc = simgear::canvas;
+
+class TestWidget:
+ public sc::LayoutItem
+{
+ public:
+ TestWidget( const SGVec2i& min_size,
+ const SGVec2i& size_hint,
+ const SGVec2i& max_size )
+ {
+ _size_hint = size_hint;
+ _min_size = min_size;
+ _max_size = max_size;
+ }
+
+ TestWidget(const TestWidget& rhs)
+ {
+ _size_hint = rhs._size_hint;
+ _min_size = rhs._min_size;
+ _max_size = rhs._max_size;
+ }
+
+ void setMinSize(const SGVec2i& size) { _min_size = size; }
+ void setMaxSize(const SGVec2i& size) { _max_size = size; }
+ void setSizeHint(const SGVec2i& size) { _size_hint = size; }
+
+ virtual void setGeometry(const SGRecti& geom) { _geom = geom; }
+ virtual SGRecti geometry() const { return _geom; }
+
+ protected:
+
+ SGRecti _geom;
+
+ virtual SGVec2i sizeHintImpl() const { return _size_hint; }
+ virtual SGVec2i minimumSizeImpl() const { return _min_size; }
+ virtual SGVec2i maximumSizeImpl() const { return _max_size; }
+};
+
+typedef SGSharedPtr<TestWidget> TestWidgetRef;
+
+//------------------------------------------------------------------------------
+BOOST_AUTO_TEST_CASE( horizontal_layout )
+{
+ sc::BoxLayout box_layout(sc::BoxLayout::BottomToTop);
+ box_layout.setSpacing(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);
+
+ 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);
+
+ 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);
+
+ // 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));
+
+ // Test with different spacing/padding
+ 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));
+
+ 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));
+ BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(21, 0, 107, 32));
+
+ TestWidgetRef stretch_widget( new TestWidget( SGVec2i(16, 16),
+ SGVec2i(32, 32),
+ SGVec2i(128, 32) ) );
+ 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));
+
+ // Due to the stretch factor only the last widget gets additional space. All
+ // other widgets get the preferred size.
+ BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(0, 8, 16, 16));
+ BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(21, 0, 32, 32));
+ BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(58, 0, 70, 32));
+
+ // Test stretch factor
+ TestWidgetRef fast_stretch( new TestWidget(*stretch_widget) );
+ sc::BoxLayout box_layout_stretch(sc::BoxLayout::LeftToRight);
+
+ box_layout_stretch.addItem(stretch_widget, 1);
+ box_layout_stretch.addItem(fast_stretch, 2);
+
+ 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));
+
+ 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));
+
+ 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) );
+
+ hbox.addItem(w1);
+ hbox.addItem(w2);
+
+ hbox.setGeometry( SGRecti(0, 0, 256, 32) );
+
+ BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 126, 32));
+ BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(131, 0, 125, 32));
+
+ hbox.setStretch(0, 1);
+ hbox.setStretch(1, 1);
+
+ 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_AUTO_TEST_CASE( vertical_layout)
+{
+ sc::BoxLayout vbox(sc::BoxLayout::TopToBottom);
+ vbox.setSpacing(7);
+
+ TestWidgetRef fixed_size_widget( new TestWidget( SGVec2i(16, 16),
+ SGVec2i(16, 16),
+ SGVec2i(16, 16) ) );
+ TestWidgetRef limited_resize_widget( new TestWidget( SGVec2i(16, 16),
+ SGVec2i(32, 32),
+ SGVec2i(256, 64) ) );
+
+ vbox.addItem(fixed_size_widget);
+ vbox.addItem(limited_resize_widget);
+
+ BOOST_CHECK_EQUAL(vbox.minimumSize(), SGVec2i(16, 39));
+ BOOST_CHECK_EQUAL(vbox.sizeHint(), SGVec2i(32, 55));
+ BOOST_CHECK_EQUAL(vbox.maximumSize(), SGVec2i(256, 87));
+
+ vbox.setGeometry(SGRecti(10, 20, 16, 55));
+
+ BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(10, 20, 16, 16));
+ BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(10, 43, 16, 32));
+
+ vbox.setDirection(sc::BoxLayout::BottomToTop);
+}
+
+//------------------------------------------------------------------------------
+BOOST_AUTO_TEST_CASE( nasal_layout )
+{
+ naContext c = naNewContext();
+ naRef me = naNewHash(c);
+
+ sc::LayoutItemRef nasal_item( new sc::NasalWidget(me) );
+
+ naFreeContext(c);
+}
SG_ENVIRONMENT = 0x00100000,
SG_SOUND = 0x00200000,
SG_NAVAID = 0x00400000,
- SG_UNDEFD = 0x00800000, // For range checking
+ SG_GUI = 0x00800000,
+ SG_UNDEFD = 0x01000000, // For range checking
SG_ALL = 0xFFFFFFFF
} sgDebugClass;
case SG_ENVIRONMENT:return "environment";
case SG_SOUND: return "sound";
case SG_NAVAID: return "navaid";
+ case SG_GUI: return "gui";
default: return "unknown";
}
}
global_privateLogstream->requestConsole();
}
-} // of namespace simgear
\ No newline at end of file
+} // of namespace simgear
*/
class PropertyBasedElement:
public SGPropertyChangeListener,
- public SGWeakReferenced
+ public virtual SGVirtualWeakReferenced
{
public:
PropertyBasedElement(SGPropertyNode* node);