Canvas: fix property inheritance.

- Do not override style of children if they have own values set.
 - Retrieve style of parent if own style value has been removed.
This commit is contained in:
Thomas Geymayer 2013-07-02 22:42:46 +02:00
parent 6f7c0c23d1
commit 6962de4b1f
6 changed files with 128 additions and 48 deletions

View File

@ -21,6 +21,7 @@
#include <simgear/canvas/CanvasEventListener.hxx>
#include <simgear/canvas/CanvasEventVisitor.hxx>
#include <simgear/canvas/MouseEvent.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/scene/material/parseBlendFunc.hxx>
#include <osg/Drawable>
@ -315,29 +316,38 @@ namespace canvas
//----------------------------------------------------------------------------
void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
{
if( parent == _node && child->getNameString() == NAME_TRANSFORM )
if( parent == _node )
{
if( !_transform.valid() )
return;
if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
if( child->getNameString() == NAME_TRANSFORM )
{
SG_LOG
(
SG_GENERAL,
SG_WARN,
"Element::childRemoved: unknown transform: " << child->getPath()
);
if( !_transform.valid() )
return;
if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
{
SG_LOG
(
SG_GENERAL,
SG_WARN,
"Element::childRemoved: unknown transform: " << child->getPath()
);
return;
}
_transform_types[ child->getIndex() ] = TT_NONE;
while( !_transform_types.empty() && _transform_types.back() == TT_NONE )
_transform_types.pop_back();
_transform_dirty = true;
return;
}
else if( StyleSetter const* setter =
getStyleSetter(child->getNameString()) )
{
setStyle(getParentStyle(child), setter);
return;
}
_transform_types[ child->getIndex() ] = TT_NONE;
while( !_transform_types.empty() && _transform_types.back() == TT_NONE )
_transform_types.pop_back();
_transform_dirty = true;
return;
}
childRemoved(child);
@ -350,8 +360,17 @@ namespace canvas
if( parent == _node )
{
const std::string& name = child->getNameString();
if( setStyle(child) )
if( StyleSetter const* setter = getStyleSetter(name) )
{
SGPropertyNode const* style = child;
if( isStyleEmpty(child) )
{
child->clearValue();
style = getParentStyle(child);
}
setStyle(style, setter);
return;
}
else if( name == "update" )
return update(0);
else if( name == "visible" )
@ -371,21 +390,10 @@ namespace canvas
}
//----------------------------------------------------------------------------
bool Element::setStyle(const SGPropertyNode* child)
bool Element::setStyle( const SGPropertyNode* child,
const StyleSetter* setter )
{
StyleSetters::const_iterator setter =
_style_setters.find(child->getNameString());
if( setter == _style_setters.end() )
return false;
const StyleSetter* style_setter = &setter->second.setter;
while( style_setter )
{
if( style_setter->func(*this, child) )
return true;
style_setter = style_setter->next;
}
return false;
return canApplyStyle(child) && setStyleImpl(child, setter);
}
//----------------------------------------------------------------------------
@ -516,6 +524,67 @@ namespace canvas
}
}
//----------------------------------------------------------------------------
bool Element::isStyleEmpty(const SGPropertyNode* child) const
{
return !child
|| simgear::strutils::strip(child->getStringValue()).empty();
}
//----------------------------------------------------------------------------
bool Element::canApplyStyle(const SGPropertyNode* child) const
{
if( _node == child->getParent() )
return true;
// Parent values do not override if element has own value
return isStyleEmpty( _node->getChild(child->getName()) );
}
//----------------------------------------------------------------------------
bool Element::setStyleImpl( const SGPropertyNode* child,
const StyleSetter* setter )
{
const StyleSetter* style_setter = setter
? setter
: getStyleSetter(child->getNameString());
while( style_setter )
{
if( style_setter->func(*this, child) )
return true;
style_setter = style_setter->next;
}
return false;
}
//----------------------------------------------------------------------------
const Element::StyleSetter*
Element::getStyleSetter(const std::string& name) const
{
StyleSetters::const_iterator setter = _style_setters.find(name);
if( setter == _style_setters.end() )
return 0;
return &setter->second.setter;
}
//----------------------------------------------------------------------------
const SGPropertyNode*
Element::getParentStyle(const SGPropertyNode* child) const
{
// Try to get value from parent...
if( _parent )
{
Style::const_iterator style =
_parent->_style.find(child->getNameString());
if( style != _parent->_style.end() )
return style->second;
}
// ...or reset to default if none is available
return child; // TODO somehow get default value for each style?
}
//----------------------------------------------------------------------------
void Element::setDrawable( osg::Drawable* drawable )
{

View File

@ -117,7 +117,8 @@ namespace canvas
SGPropertyNode * child );
virtual void valueChanged(SGPropertyNode * child);
virtual bool setStyle(const SGPropertyNode* child);
virtual bool setStyle( const SGPropertyNode* child,
const StyleSetter* setter = 0 );
/**
* Set clipping shape
@ -441,6 +442,14 @@ namespace canvas
);
}
bool isStyleEmpty(const SGPropertyNode* child) const;
bool canApplyStyle(const SGPropertyNode* child) const;
bool setStyleImpl( const SGPropertyNode* child,
const StyleSetter* setter = 0 );
const StyleSetter* getStyleSetter(const std::string& name) const;
const SGPropertyNode* getParentStyle(const SGPropertyNode* child) const;
virtual void childAdded(SGPropertyNode * child) {}
virtual void childRemoved(SGPropertyNode * child){}
virtual void childChanged(SGPropertyNode * child){}

View File

@ -174,20 +174,20 @@ namespace canvas
}
//----------------------------------------------------------------------------
bool Group::setStyle(const SGPropertyNode* style)
bool Group::setStyle( const SGPropertyNode* style,
const StyleSetter* setter )
{
// Don't propagate styles directly applicable to this group
if( Element::setStyle(style) )
return true;
if( style->getParent() != _node
&& _style.find(style->getNameString()) != _style.end() )
if( !canApplyStyle(style) )
return false;
// Don't propagate styles directly applicable to this group
if( setStyleImpl(style, setter) )
return true;
bool handled = false;
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
{
if( getChildByIndex(i)->setStyle(style) )
if( getChildByIndex(i)->setStyle(style, setter) )
handled = true;
}

View File

@ -92,7 +92,8 @@ namespace canvas
virtual bool traverse(EventVisitor& visitor);
virtual bool setStyle(const SGPropertyNode* child);
virtual bool setStyle( const SGPropertyNode* child,
const StyleSetter* setter = 0 );
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;

View File

@ -400,8 +400,9 @@ namespace canvas
//----------------------------------------------------------------------------
void Image::setFill(const std::string& fill)
{
osg::Vec4 color;
if( !parseColor(fill, color) )
osg::Vec4 color(1,1,1,1);
if( !fill.empty() // If no color is given default to white
&& !parseColor(fill, color) )
return;
_colors->front() = color;

View File

@ -110,7 +110,7 @@ namespace canvas
*/
void setFill(const std::string& fill)
{
if( fill == "none" )
if( fill.empty() || fill == "none" )
{
_mode &= ~VG_FILL_PATH;
}
@ -150,7 +150,7 @@ namespace canvas
*/
void setStroke(const std::string& stroke)
{
if( stroke == "none" )
if( stroke.empty() || stroke == "none" )
{
_mode &= ~VG_STROKE_PATH;
}