Canvas: rework style setter system.

Style setters are now only setup once for each element type and
not for each element instance as before. A static map holds the
setters for each element type. Also an animation type is stored
which will later allow to animate properties of Canvas elements
without specifying and animation type.
This commit is contained in:
Thomas Geymayer
2013-04-20 00:22:37 +02:00
parent 17369a20de
commit 1f43132363
8 changed files with 360 additions and 69 deletions

View File

@@ -324,8 +324,14 @@ namespace canvas
if( setter == _style_setters.end() )
return false;
setter->second(child);
return true;
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;
}
//----------------------------------------------------------------------------
@@ -426,6 +432,9 @@ namespace canvas
return transformed;
}
//----------------------------------------------------------------------------
Element::StyleSetters Element::_style_setters;
//----------------------------------------------------------------------------
Element::Element( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
@@ -447,7 +456,10 @@ namespace canvas
"New canvas element " << node->getPath()
);
addStyle("clip", &Element::setClip, this);
if( !isInit<Element>() )
{
addStyle("clip", "", &Element::setClip);
}
}
//----------------------------------------------------------------------------

View File

@@ -45,8 +45,21 @@ namespace canvas
public PropertyBasedElement
{
public:
typedef boost::function<void(const SGPropertyNode*)> StyleSetter;
typedef std::map<std::string, StyleSetter> StyleSetters;
typedef boost::function<bool(Element&, const SGPropertyNode*)>
StyleSetterFunc;
typedef boost::function<void(Element&, const SGPropertyNode*)>
StyleSetterFuncUnchecked;
struct StyleSetter:
public SGReferenced
{
StyleSetterFunc func;
SGSharedPtr<StyleSetter> next;
};
struct StyleInfo
{
StyleSetter setter; ///!< Function(s) for setting this style
std::string type; ///!< Interpolation type
};
/**
* Remove the property listener of the element.
@@ -142,7 +155,6 @@ namespace canvas
std::vector<TransformType> _transform_types;
Style _style;
StyleSetters _style_setters;
std::vector<SGPropertyNode_ptr> _bounding_box;
typedef std::vector<EventListenerPtr> Listener;
@@ -150,44 +162,254 @@ namespace canvas
ListenerMap _listener;
typedef std::map<std::string, StyleInfo> StyleSetters;
static StyleSetters _style_setters;
Element( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style,
Element* parent );
template<typename T, class C1, class C2>
Element::StyleSetter
addStyle(const std::string& name, void (C1::*setter)(T), C2 instance)
/**
* Returns false on first call and true on any successive call. Use to
* perform initialization tasks which are only required once per element
* type.
*
* @tparam Derived (Derived) class type
*/
template<class Derived>
bool isInit() const
{
return _style_setters[ name ] =
bindStyleSetter<T>(name, setter, instance);
static bool is_init = false;
if( is_init )
return true;
is_init = true;
return false;
}
template<typename T1, typename T2, class C1, class C2>
Element::StyleSetter
addStyle(const std::string& name, void (C1::*setter)(T2), C2 instance)
{
return _style_setters[ name ] =
bindStyleSetter<T1>(name, setter, instance);
}
template<class C1, class C2>
Element::StyleSetter
/**
* Register a function for setting a style specified by the given property
*
* @param name Property name
* @param type Interpolation type
* @param setter Setter function
*
* @tparam T1 Type of value used to retrieve value from property
* node
* @tparam T2 Type of value the setter function expects
* @tparam Derived Type of class the setter can be applied to
*
* @note T1 needs to be convertible to T2
*/
template<
typename T1,
typename T2,
class Derived
>
StyleSetter
addStyle( const std::string& name,
void (C1::*setter)(const std::string&),
C2 instance )
const std::string& type,
const boost::function<void (Derived&, T2)>& setter )
{
return _style_setters[ name ] =
bindStyleSetter<const char*>(name, setter, instance);
StyleInfo& style_info = _style_setters[ name ];
if( !type.empty() )
{
if( !style_info.type.empty() && type != style_info.type )
SG_LOG
(
SG_GENERAL,
SG_WARN,
"changing animation type for '" << name << "': "
<< style_info.type << " -> " << type
);
style_info.type = type;
}
StyleSetter* style = &style_info.setter;
while( style->next )
style = style->next;
if( style->func )
style = style->next = new StyleSetter;
style->func = boost::bind
(
&type_match<Derived>::call,
_1,
_2,
bindStyleSetter<T1>(name, setter)
);
return *style;
}
template<typename T1, typename T2, class C1, class C2>
Element::StyleSetter
bindStyleSetter( const std::string& name,
void (C1::*setter)(T2),
C2 instance )
template<
typename T,
class Derived
>
StyleSetter
addStyle( const std::string& name,
const std::string& type,
const boost::function<void (Derived&, T)>& setter )
{
return boost::bind(setter, instance, boost::bind(&getValue<T1>, _1));
return addStyle<T, T>(name, type, setter);
}
template<
typename T,
class Derived
>
StyleSetter
addStyle( const std::string& name,
const std::string& type,
void (Derived::*setter)(T) )
{
return addStyle<T, T>
(
name,
type,
boost::function<void (Derived&, T)>(setter)
);
}
template<
typename T1,
typename T2,
class Derived
>
StyleSetterFunc
addStyle( const std::string& name,
const std::string& type,
void (Derived::*setter)(T2) )
{
return addStyle<T1>
(
name,
type,
boost::function<void (Derived&, T2)>(setter)
);
}
template<
class Derived
>
StyleSetter
addStyle( const std::string& name,
const std::string& type,
void (Derived::*setter)(const std::string&) )
{
return addStyle<const char*, const std::string&>
(
name,
type,
boost::function<void (Derived&, const std::string&)>(setter)
);
}
template<
typename T,
class Derived,
class Other,
class OtherRef
>
StyleSetter
addStyle( const std::string& name,
const std::string& type,
void (Other::*setter)(T),
OtherRef Derived::*instance_ref )
{
return addStyle<T, T>(name, type, bindOther(setter, instance_ref));
}
template<
typename T1,
typename T2,
class Derived,
class Other,
class OtherRef
>
StyleSetter
addStyle( const std::string& name,
const std::string& type,
void (Other::*setter)(T2),
OtherRef Derived::*instance_ref )
{
return addStyle<T1>(name, type, bindOther(setter, instance_ref));
}
template<
typename T1,
typename T2,
class Derived,
class Other,
class OtherRef
>
StyleSetter
addStyle( const std::string& name,
const std::string& type,
const boost::function<void (Other&, T2)>& setter,
OtherRef Derived::*instance_ref )
{
return addStyle<T1>(name, type, bindOther(setter, instance_ref));
}
template<
class Derived,
class Other,
class OtherRef
>
StyleSetter
addStyle( const std::string& name,
const std::string& type,
void (Other::*setter)(const std::string&),
OtherRef Derived::*instance_ref )
{
return addStyle<const char*, const std::string&>
(
name,
type,
boost::function<void (Other&, const std::string&)>(setter),
instance_ref
);
}
template<typename T, class Derived, class Other, class OtherRef>
boost::function<void (Derived&, T)>
bindOther( void (Other::*setter)(T), OtherRef Derived::*instance_ref )
{
return boost::bind(setter, boost::bind(instance_ref, _1), _2);
}
template<typename T, class Derived, class Other, class OtherRef>
boost::function<void (Derived&, T)>
bindOther( const boost::function<void (Other&, T)>& setter,
OtherRef Derived::*instance_ref )
{
return boost::bind
(
setter,
boost::bind
(
&reference_from_pointer<Other, OtherRef>,
boost::bind(instance_ref, _1)
),
_2
);
}
template<typename T1, typename T2, class Derived>
StyleSetterFuncUnchecked
bindStyleSetter( const std::string& name,
const boost::function<void (Derived&, T2)>& setter )
{
return boost::bind
(
setter,
// We will only call setters with Derived instances, so we can safely
// cast here.
boost::bind(&derived_cast<Derived>, _1),
boost::bind(&getValue<T1>, _2)
);
}
virtual void childAdded(SGPropertyNode * child) {}
@@ -208,6 +430,39 @@ namespace canvas
osg::ref_ptr<osg::Drawable> _drawable;
Element(const Element&);// = delete
template<class Derived>
static Derived& derived_cast(Element& el)
{
return static_cast<Derived&>(el);
}
template<class T, class SharedPtr>
static T& reference_from_pointer(const SharedPtr& p)
{
return *get_pointer(p);
}
/**
* Helper to call a function only of the element type can be converted to
* the required type.
*
* @return Whether the function has been called
*/
template<class Derived>
struct type_match
{
static bool call( Element& el,
const SGPropertyNode* prop,
const StyleSetterFuncUnchecked& func )
{
Derived* d = dynamic_cast<Derived*>(&el);
if( !d )
return false;
func(*d, prop);
return true;
}
};
};
} // namespace canvas

View File

@@ -46,6 +46,9 @@ namespace canvas
return el;
}
//----------------------------------------------------------------------------
ElementFactories Group::_child_factories;
//----------------------------------------------------------------------------
Group::Group( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
@@ -53,11 +56,14 @@ namespace canvas
Element* parent ):
Element(canvas, node, parent_style, parent)
{
_child_factories["group"] = &createElement<Group>;
_child_factories["image"] = &createElement<Image>;
_child_factories["map" ] = &createElement<Map >;
_child_factories["path" ] = &createElement<Path >;
_child_factories["text" ] = &createElement<Text >;
if( !isInit<Group>() )
{
_child_factories["group"] = &createElement<Group>;
_child_factories["image"] = &createElement<Image>;
_child_factories["map" ] = &createElement<Map >;
_child_factories["path" ] = &createElement<Path >;
_child_factories["text" ] = &createElement<Text >;
}
}
//----------------------------------------------------------------------------
@@ -183,7 +189,7 @@ namespace canvas
if( child->getParent() != _node )
return;
ChildFactories::iterator child_factory =
ElementFactories::iterator child_factory =
_child_factories.find( child->getNameString() );
if( child_factory != _child_factories.end() )
{

View File

@@ -28,6 +28,8 @@ namespace simgear
namespace canvas
{
typedef std::map<std::string, ElementFactory> ElementFactories;
class Group:
public Element
{
@@ -64,10 +66,8 @@ namespace canvas
protected:
typedef std::map<std::string, ElementFactory> ChildFactories;
ChildFactories _child_factories;
ChildList _children;
static ElementFactories _child_factories;
ChildList _children;
virtual void childAdded(SGPropertyNode * child);
virtual void childRemoved(SGPropertyNode * child);

View File

@@ -127,10 +127,13 @@ namespace canvas
setDrawable(_geom);
addStyle("fill", &Image::setFill, this);
addStyle("slice", &Image::setSlice, this);
addStyle("slice-width", &Image::setSliceWidth, this);
addStyle("outset", &Image::setOutset, this);
if( !isInit<Image>() )
{
addStyle("fill", "color", &Image::setFill);
addStyle("slice", "", &Image::setSlice);
addStyle("slice-width", "numeric", &Image::setSliceWidth);
addStyle("outset", "", &Image::setOutset);
}
setFill("#ffffff"); // TODO how should we handle default values?

View File

@@ -476,14 +476,18 @@ namespace canvas
_path( new PathDrawable(this) )
{
setDrawable(_path);
PathDrawable *path = _path.get();
addStyle("fill", &PathDrawable::setFill, path);
addStyle("fill-rule", &PathDrawable::setFillRule, path);
addStyle("stroke", &PathDrawable::setStroke, path);
addStyle("stroke-width", &PathDrawable::setStrokeWidth, path);
addStyle("stroke-dasharray", &PathDrawable::setStrokeDashArray, path);
addStyle("stroke-linecap", &PathDrawable::setStrokeLinecap, path);
if( !isInit<Path>() )
{
PathDrawableRef Path::*path = &Path::_path;
addStyle("fill", "color", &PathDrawable::setFill, path);
addStyle("fill-rule", "", &PathDrawable::setFillRule, path);
addStyle("stroke", "color", &PathDrawable::setStroke, path);
addStyle("stroke-width", "numeric", &PathDrawable::setStrokeWidth, path);
addStyle("stroke-dasharray", "", &PathDrawable::setStrokeDashArray, path);
addStyle("stroke-linecap", "", &PathDrawable::setStrokeLinecap, path);
}
setupStyle();
}

View File

@@ -48,7 +48,8 @@ namespace canvas
};
class PathDrawable;
osg::ref_ptr<PathDrawable> _path;
typedef osg::ref_ptr<PathDrawable> PathDrawableRef;
PathDrawableRef _path;
virtual void childRemoved(SGPropertyNode * child);
virtual void childChanged(SGPropertyNode * child);

View File

@@ -254,22 +254,32 @@ namespace canvas
_text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
_text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
addStyle("fill", &TextOSG::setFill, _text);
addStyle("background", &TextOSG::setBackgroundColor, _text);
addStyle("character-size",
static_cast<void (TextOSG::*)(float)>(&TextOSG::setCharacterSize),
_text);
addStyle("character-aspect-ratio", &TextOSG::setCharacterAspect, _text);
addStyle("padding", &TextOSG::setBoundingBoxMargin, _text);
// TEXT = 1 default
// BOUNDINGBOX = 2
// FILLEDBOUNDINGBOX = 4
// ALIGNMENT = 8
addStyle<int>("draw-mode", &TextOSG::setDrawMode, _text);
addStyle("max-width", &TextOSG::setMaximumWidth, _text);
addStyle("font", &Text::setFont, this);
addStyle("alignment", &Text::setAlignment, this);
addStyle("text", &Text::setText, this);
if( !isInit<Text>() )
{
osg::ref_ptr<TextOSG> Text::*text = &Text::_text;
addStyle("fill", "color", &TextOSG::setFill, text);
addStyle("background", "color", &TextOSG::setBackgroundColor, text);
addStyle("character-size",
"numeric",
static_cast<
void (TextOSG::*)(float)
> (&TextOSG::setCharacterSize),
text);
addStyle("character-aspect-ratio",
"numeric",
&TextOSG::setCharacterAspect, text);
addStyle("padding", "numeric", &TextOSG::setBoundingBoxMargin, text);
// TEXT = 1 default
// BOUNDINGBOX = 2
// FILLEDBOUNDINGBOX = 4
// ALIGNMENT = 8
addStyle<int>("draw-mode", "", &TextOSG::setDrawMode, text);
addStyle("max-width", "numeric", &TextOSG::setMaximumWidth, text);
addStyle("font", "", &Text::setFont);
addStyle("alignment", "", &Text::setAlignment);
addStyle("text", "", &Text::setText);
}
setupStyle();
}