canvas::Layout: support for hiding items.

This commit is contained in:
Thomas Geymayer
2014-07-21 00:24:25 +02:00
parent e71d6b24d7
commit 557e4f75b5
12 changed files with 263 additions and 20 deletions

View File

@@ -172,6 +172,19 @@ namespace canvas
return _capture_events;
}
//----------------------------------------------------------------------------
void Window::setVisible(bool visible)
{
LayoutItem::setVisible(visible);
Element::setVisible(LayoutItem::isVisible());
}
//----------------------------------------------------------------------------
bool Window::isVisible() const
{
return Element::isVisible();
}
//----------------------------------------------------------------------------
void Window::raise()
{

View File

@@ -84,6 +84,9 @@ namespace canvas
bool isResizable() const;
bool isCapturingEvents() const;
virtual void setVisible(bool visible);
virtual bool isVisible() const;
/**
* Moves window on top of all other windows with the same z-index.
*

View File

@@ -128,12 +128,12 @@ namespace canvas
/**
* Set visibility of the element.
*/
void setVisible(bool visible);
virtual void setVisible(bool visible);
/**
* Get whether the element is visible or hidden.
*/
bool isVisible() const;
virtual bool isVisible() const;
osg::MatrixTransform* getMatrixTransform();
osg::MatrixTransform const* getMatrixTransform() const;

View File

@@ -278,11 +278,13 @@ namespace canvas
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.visible = item.isVisible();
if( !item_data.visible )
continue;
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)();
@@ -354,6 +356,9 @@ namespace canvas
for(size_t i = 0; i < _layout_items.size(); ++i)
{
ItemData const& data = _layout_items[i];
if( !data.visible )
continue;
_hfw_height = std::max(_hfw_height, data.hfw(data.size));
_hfw_min_height = std::max(_hfw_min_height, data.mhfw(data.size));
}
@@ -363,6 +368,9 @@ namespace canvas
for(size_t i = 0; i < _layout_items.size(); ++i)
{
ItemData const& data = _layout_items[i];
if( !data.visible )
continue;
_hfw_height += data.hfw(w) + data.padding_orig;
_hfw_min_height += data.mhfw(w) + data.padding_orig;
}
@@ -412,6 +420,9 @@ namespace canvas
for(size_t i = 0; i < _layout_items.size(); ++i)
{
ItemData& data = _layout_items[i];
if( !data.visible )
continue;
if( data.has_hfw )
{
int w = SGMisc<int>::clip( geom.width(),
@@ -451,6 +462,9 @@ namespace canvas
for(size_t i = 0; i < _layout_items.size(); ++i)
{
ItemData const& data = _layout_items[i];
if( !data.visible )
continue;
cur_pos.x() += reverse ? -data.padding - data.size : data.padding;
SGVec2i size(
@@ -476,6 +490,13 @@ namespace canvas
}
}
//----------------------------------------------------------------------------
void BoxLayout::visibilityChanged(bool visible)
{
for(size_t i = 0; i < _layout_items.size(); ++i)
callSetVisibleInternal(_layout_items[i].layout_item.get(), visible);
}
//----------------------------------------------------------------------------
HBoxLayout::HBoxLayout():
BoxLayout(LeftToRight)

View File

@@ -122,6 +122,8 @@ namespace canvas
virtual SGVec2i maximumSizeImpl() const;
virtual void doLayout(const SGRecti& geom);
virtual void visibilityChanged(bool visible);
};
/**

View File

@@ -27,7 +27,7 @@ namespace canvas
//----------------------------------------------------------------------------
void Layout::update()
{
if( !(_flags & (LAYOUT_DIRTY | SIZE_INFO_DIRTY)) )
if( !(_flags & (LAYOUT_DIRTY | SIZE_INFO_DIRTY)) || !isVisible() )
return;
doLayout(_geometry);
@@ -85,6 +85,7 @@ namespace canvas
padding = 0;
size = 0;
stretch = 0;
visible = false;
has_hfw = false;
done = false;
}
@@ -120,7 +121,7 @@ namespace canvas
void Layout::distribute(std::vector<ItemData>& items, const ItemData& space)
{
const int num_children = static_cast<int>(items.size());
_num_not_done = num_children;
_num_not_done = 0;
SG_LOG( SG_GUI,
SG_DEBUG,
@@ -148,6 +149,9 @@ namespace canvas
for(int i = 0; i < num_children; ++i)
{
ItemData& d = items[i];
if( !d.visible )
continue;
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);
@@ -162,10 +166,8 @@ namespace canvas
);
if( d.done )
{
_num_not_done -= 1;
continue;
}
_num_not_done += 1;
if( d.stretch > 0 )
{
@@ -191,6 +193,8 @@ namespace canvas
for(int i = 0; i < num_children; ++i)
{
ItemData& d = items[i];
if( !d.visible )
continue;
SG_LOG(
SG_GUI,
@@ -265,9 +269,18 @@ namespace canvas
else
{
_space_left = space.size - space.max_size;
for(int i = 0; i < num_children; ++i)
{
if( items[i].visible )
_num_not_done += 1;
}
for(int i = 0; i < num_children; ++i)
{
ItemData& d = items[i];
if( !d.visible )
continue;
d.size = d.max_size;
// Add superfluous space as padding
@@ -284,10 +297,11 @@ namespace canvas
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 );
if( d.visible )
SG_LOG(SG_GUI, SG_DEBUG, i << ") pad=" << d.padding
<< ", size= " << d.size);
else
SG_LOG(SG_GUI, SG_DEBUG, i << ") [hidden]");
}
}

View File

@@ -88,7 +88,8 @@ namespace canvas
padding, //<! padding before element (layouted)
size, //<! layouted size
stretch; //<! stretch factor
bool has_hfw : 1, //<! height for width
bool visible : 1,
has_hfw : 1, //<! height for width
done : 1; //<! layouting done
/** Clear values (reset to default/empty state) */

View File

@@ -28,7 +28,7 @@ namespace canvas
//----------------------------------------------------------------------------
LayoutItem::LayoutItem():
_flags(0),
_flags(VISIBLE),
_size_hint(0, 0),
_min_size(0, 0),
_max_size(MAX_SIZE)
@@ -96,6 +96,29 @@ namespace canvas
return heightForWidth(w);
}
//----------------------------------------------------------------------------
void LayoutItem::setVisible(bool visible)
{
if( visible )
_flags &= ~EXPLICITLY_HIDDEN;
else
_flags |= EXPLICITLY_HIDDEN;
setVisibleInternal(visible);
}
//----------------------------------------------------------------------------
bool LayoutItem::isVisible() const
{
return _flags & VISIBLE;
}
//----------------------------------------------------------------------------
bool LayoutItem::isExplicitlyHidden() const
{
return _flags & EXPLICITLY_HIDDEN;
}
//----------------------------------------------------------------------------
void LayoutItem::invalidate()
{
@@ -141,6 +164,8 @@ namespace canvas
_parent = parent;
LayoutItemRef parent_ref = parent.lock();
setCanvas(parent_ref ? parent_ref->_canvas : CanvasWeakPtr());
setVisibleInternal(!parent_ref || parent_ref->isVisible());
}
//----------------------------------------------------------------------------
@@ -167,5 +192,31 @@ namespace canvas
return _max_size;
}
//----------------------------------------------------------------------------
void LayoutItem::setVisibleInternal(bool visible)
{
LayoutItemRef parent = getParent();
if( isExplicitlyHidden() || (parent && !parent->isVisible()) )
visible = false;
if( isVisible() == visible )
return;
invalidateParent();
if( visible )
_flags |= VISIBLE;
else
_flags &= ~VISIBLE;
visibilityChanged(visible);
}
//----------------------------------------------------------------------------
void LayoutItem::callSetVisibleInternal(LayoutItem* item, bool visible)
{
item->setVisibleInternal(visible);
}
} // namespace canvas
} // namespace simgear

View File

@@ -68,6 +68,14 @@ namespace canvas
virtual int heightForWidth(int w) const;
virtual int minimumHeightForWidth(int w) const;
virtual void setVisible(bool visible);
virtual bool isVisible() const;
bool isExplicitlyHidden() const;
void show() { setVisible(true); }
void hide() { setVisible(false); }
/**
* Mark all cached data as invalid and require it to be recalculated.
*/
@@ -124,7 +132,9 @@ namespace canvas
SIZE_INFO_DIRTY = SIZE_HINT_DIRTY
| MINIMUM_SIZE_DIRTY
| MAXIMUM_SIZE_DIRTY,
LAST_FLAG = MAXIMUM_SIZE_DIRTY
EXPLICITLY_HIDDEN = MAXIMUM_SIZE_DIRTY << 1,
VISIBLE = EXPLICITLY_HIDDEN << 1,
LAST_FLAG = VISIBLE
};
CanvasWeakPtr _canvas;
@@ -141,6 +151,18 @@ namespace canvas
virtual SGVec2i minimumSizeImpl() const;
virtual SGVec2i maximumSizeImpl() const;
/**
* @return whether the visibility has changed.
*/
void setVisibleInternal(bool visible);
virtual void visibilityChanged(bool visible) {}
/**
* Allow calling the protected setVisibleImpl from derived classes
*/
static void callSetVisibleInternal(LayoutItem* item, bool visible);
};
} // namespace canvas

View File

@@ -285,5 +285,22 @@ namespace canvas
);
}
//----------------------------------------------------------------------------
void NasalWidget::visibilityChanged(bool visible)
{
try
{
callMethod<void>("visibilityChanged", visible);
}
catch( std::exception const& ex )
{
SG_LOG(
SG_GUI,
SG_WARN,
"NasalWidget::visibilityChanged: callback error: '" << ex.what() << "'"
);
}
}
} // namespace canvas
} // namespace simgear

View File

@@ -118,6 +118,8 @@ namespace canvas
virtual SGVec2i minimumSizeImpl() const;
virtual SGVec2i maximumSizeImpl() const;
virtual void visibilityChanged(bool visible);
};
typedef SGSharedPtr<NasalWidget> NasalWidgetRef;

View File

@@ -23,6 +23,8 @@
#include "NasalWidget.hxx"
#include <simgear/debug/logstream.hxx>
#include <simgear/nasal/cppbind/NasalContext.hxx>
#include <cstdlib>
//------------------------------------------------------------------------------
@@ -72,6 +74,12 @@ class TestWidget:
virtual SGVec2i sizeHintImpl() const { return _size_hint; }
virtual SGVec2i minimumSizeImpl() const { return _min_size; }
virtual SGVec2i maximumSizeImpl() const { return _max_size; }
virtual void visibilityChanged(bool visible)
{
if( !visible )
_geom.set(0, 0, 0, 0);
}
};
class TestWidgetHFW:
@@ -332,6 +340,77 @@ BOOST_AUTO_TEST_CASE( boxlayout_insert_remove )
BOOST_CHECK( !w2->getParent() );
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( boxlayout_visibility )
{
sc::BoxLayoutRef hbox( new sc::HBoxLayout );
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
SGVec2i(32, 32) ) ),
w2( new TestWidget(*w1) ),
w3( new TestWidget(*w1) );
hbox->addItem(w1);
hbox->addItem(w2);
hbox->addItem(w3);
BOOST_REQUIRE_EQUAL(hbox->sizeHint().x(), 3 * 32 + 2 * hbox->spacing());
hbox->setGeometry(SGRecti(0, 0, 69, 32));
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 20, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(25, 0, 20, 32));
BOOST_CHECK_EQUAL(w3->geometry(), SGRecti(50, 0, 19, 32));
w2->setVisible(false);
BOOST_REQUIRE(hbox->isVisible());
BOOST_REQUIRE(w1->isVisible());
BOOST_REQUIRE(!w2->isVisible());
BOOST_REQUIRE(w2->isExplicitlyHidden());
BOOST_REQUIRE(w3->isVisible());
BOOST_CHECK_EQUAL(hbox->sizeHint().x(), 2 * 32 + 1 * hbox->spacing());
hbox->update();
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 32, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 0, 0, 0));
BOOST_CHECK_EQUAL(w3->geometry(), SGRecti(37, 0, 32, 32));
hbox->setVisible(false);
BOOST_REQUIRE(!hbox->isVisible());
BOOST_REQUIRE(hbox->isExplicitlyHidden());
BOOST_REQUIRE(!w1->isVisible());
BOOST_REQUIRE(!w1->isExplicitlyHidden());
BOOST_REQUIRE(!w2->isVisible());
BOOST_REQUIRE(w2->isExplicitlyHidden());
BOOST_REQUIRE(!w3->isVisible());
BOOST_REQUIRE(!w3->isExplicitlyHidden());
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 0, 0));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 0, 0, 0));
BOOST_CHECK_EQUAL(w3->geometry(), SGRecti(0, 0, 0, 0));
w2->setVisible(true);
BOOST_REQUIRE(!w2->isVisible());
BOOST_REQUIRE(!w2->isExplicitlyHidden());
hbox->setVisible(true);
BOOST_REQUIRE(hbox->isVisible());
BOOST_REQUIRE(w1->isVisible());
BOOST_REQUIRE(w2->isVisible());
BOOST_REQUIRE(w3->isVisible());
hbox->update();
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 20, 32));
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(25, 0, 20, 32));
BOOST_CHECK_EQUAL(w3->geometry(), SGRecti(50, 0, 19, 32));
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( boxlayout_hfw )
{
@@ -423,13 +502,30 @@ BOOST_AUTO_TEST_CASE( boxlayout_hfw )
BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 90, 24, 32));
}
// TODO extend to_nasal_helper for automatic argument conversion
static naRef f_Widget_visibilityChanged(nasal::CallContext ctx)
{
sc::NasalWidget* w = ctx.from_nasal<sc::NasalWidget*>(ctx.me);
if( !ctx.requireArg<bool>(0) )
w->setGeometry(SGRecti(0, 0, -1, -1));
return naNil();
}
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE( nasal_widget )
{
naContext c = naNewContext();
naRef me = naNewHash(c);
nasal::Context c;
nasal::Hash globals = c.newHash();
sc::NasalWidgetRef w( new sc::NasalWidget(me) );
nasal::Object::setupGhost();
nasal::Ghost<sc::LayoutItemRef>::init("LayoutItem");
sc::NasalWidget::setupGhost(globals);
nasal::Hash me = c.newHash();
me.set("visibilityChanged", &f_Widget_visibilityChanged);
sc::NasalWidgetRef w( new sc::NasalWidget(me.get_naRef()) );
// Default layout sizes (no user set values)
BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(16, 16));
@@ -463,5 +559,6 @@ BOOST_AUTO_TEST_CASE( nasal_widget )
BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(3, 22));
BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(4, 23));
naFreeContext(c);
w->setVisible(false);
BOOST_CHECK_EQUAL(w->geometry(), SGRecti(0, 0, -1, -1));
}