canvas::Layout: support for hiding items.
This commit is contained in:
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -122,6 +122,8 @@ namespace canvas
|
||||
virtual SGVec2i maximumSizeImpl() const;
|
||||
|
||||
virtual void doLayout(const SGRecti& geom);
|
||||
|
||||
virtual void visibilityChanged(bool visible);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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]");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -118,6 +118,8 @@ namespace canvas
|
||||
virtual SGVec2i minimumSizeImpl() const;
|
||||
virtual SGVec2i maximumSizeImpl() const;
|
||||
|
||||
virtual void visibilityChanged(bool visible);
|
||||
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<NasalWidget> NasalWidgetRef;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user