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
+15 -3
View File
@@ -324,8 +324,14 @@ namespace canvas
if( setter == _style_setters.end() ) if( setter == _style_setters.end() )
return false; return false;
setter->second(child); const StyleSetter* style_setter = &setter->second.setter;
return true; 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; return transformed;
} }
//----------------------------------------------------------------------------
Element::StyleSetters Element::_style_setters;
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
Element::Element( const CanvasWeakPtr& canvas, Element::Element( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node, const SGPropertyNode_ptr& node,
@@ -447,7 +456,10 @@ namespace canvas
"New canvas element " << node->getPath() "New canvas element " << node->getPath()
); );
addStyle("clip", &Element::setClip, this); if( !isInit<Element>() )
{
addStyle("clip", "", &Element::setClip);
}
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
+283 -28
View File
@@ -45,8 +45,21 @@ namespace canvas
public PropertyBasedElement public PropertyBasedElement
{ {
public: public:
typedef boost::function<void(const SGPropertyNode*)> StyleSetter; typedef boost::function<bool(Element&, const SGPropertyNode*)>
typedef std::map<std::string, StyleSetter> StyleSetters; 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. * Remove the property listener of the element.
@@ -142,7 +155,6 @@ namespace canvas
std::vector<TransformType> _transform_types; std::vector<TransformType> _transform_types;
Style _style; Style _style;
StyleSetters _style_setters;
std::vector<SGPropertyNode_ptr> _bounding_box; std::vector<SGPropertyNode_ptr> _bounding_box;
typedef std::vector<EventListenerPtr> Listener; typedef std::vector<EventListenerPtr> Listener;
@@ -150,44 +162,254 @@ namespace canvas
ListenerMap _listener; ListenerMap _listener;
typedef std::map<std::string, StyleInfo> StyleSetters;
static StyleSetters _style_setters;
Element( const CanvasWeakPtr& canvas, Element( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node, const SGPropertyNode_ptr& node,
const Style& parent_style, const Style& parent_style,
Element* parent ); Element* parent );
template<typename T, class C1, class C2> /**
Element::StyleSetter * Returns false on first call and true on any successive call. Use to
addStyle(const std::string& name, void (C1::*setter)(T), C2 instance) * 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 ] = static bool is_init = false;
bindStyleSetter<T>(name, setter, instance); if( is_init )
return true;
is_init = true;
return false;
} }
template<typename T1, typename T2, class C1, class C2> /**
Element::StyleSetter * Register a function for setting a style specified by the given property
addStyle(const std::string& name, void (C1::*setter)(T2), C2 instance) *
{ * @param name Property name
return _style_setters[ name ] = * @param type Interpolation type
bindStyleSetter<T1>(name, setter, instance); * @param setter Setter function
} *
* @tparam T1 Type of value used to retrieve value from property
template<class C1, class C2> * node
Element::StyleSetter * @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, addStyle( const std::string& name,
void (C1::*setter)(const std::string&), const std::string& type,
C2 instance ) const boost::function<void (Derived&, T2)>& setter )
{ {
return _style_setters[ name ] = StyleInfo& style_info = _style_setters[ name ];
bindStyleSetter<const char*>(name, setter, instance); 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> template<
Element::StyleSetter typename T,
bindStyleSetter( const std::string& name, class Derived
void (C1::*setter)(T2), >
C2 instance ) 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) {} virtual void childAdded(SGPropertyNode * child) {}
@@ -208,6 +430,39 @@ namespace canvas
osg::ref_ptr<osg::Drawable> _drawable; osg::ref_ptr<osg::Drawable> _drawable;
Element(const Element&);// = delete 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 } // namespace canvas
+12 -6
View File
@@ -46,6 +46,9 @@ namespace canvas
return el; return el;
} }
//----------------------------------------------------------------------------
ElementFactories Group::_child_factories;
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
Group::Group( const CanvasWeakPtr& canvas, Group::Group( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node, const SGPropertyNode_ptr& node,
@@ -53,11 +56,14 @@ namespace canvas
Element* parent ): Element* parent ):
Element(canvas, node, parent_style, parent) Element(canvas, node, parent_style, parent)
{ {
_child_factories["group"] = &createElement<Group>; if( !isInit<Group>() )
_child_factories["image"] = &createElement<Image>; {
_child_factories["map" ] = &createElement<Map >; _child_factories["group"] = &createElement<Group>;
_child_factories["path" ] = &createElement<Path >; _child_factories["image"] = &createElement<Image>;
_child_factories["text" ] = &createElement<Text >; _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 ) if( child->getParent() != _node )
return; return;
ChildFactories::iterator child_factory = ElementFactories::iterator child_factory =
_child_factories.find( child->getNameString() ); _child_factories.find( child->getNameString() );
if( child_factory != _child_factories.end() ) if( child_factory != _child_factories.end() )
{ {
+4 -4
View File
@@ -28,6 +28,8 @@ namespace simgear
namespace canvas namespace canvas
{ {
typedef std::map<std::string, ElementFactory> ElementFactories;
class Group: class Group:
public Element public Element
{ {
@@ -64,10 +66,8 @@ namespace canvas
protected: protected:
typedef std::map<std::string, ElementFactory> ChildFactories; static ElementFactories _child_factories;
ChildList _children;
ChildFactories _child_factories;
ChildList _children;
virtual void childAdded(SGPropertyNode * child); virtual void childAdded(SGPropertyNode * child);
virtual void childRemoved(SGPropertyNode * child); virtual void childRemoved(SGPropertyNode * child);
+7 -4
View File
@@ -127,10 +127,13 @@ namespace canvas
setDrawable(_geom); setDrawable(_geom);
addStyle("fill", &Image::setFill, this); if( !isInit<Image>() )
addStyle("slice", &Image::setSlice, this); {
addStyle("slice-width", &Image::setSliceWidth, this); addStyle("fill", "color", &Image::setFill);
addStyle("outset", &Image::setOutset, this); addStyle("slice", "", &Image::setSlice);
addStyle("slice-width", "numeric", &Image::setSliceWidth);
addStyle("outset", "", &Image::setOutset);
}
setFill("#ffffff"); // TODO how should we handle default values? setFill("#ffffff"); // TODO how should we handle default values?
+11 -7
View File
@@ -476,14 +476,18 @@ namespace canvas
_path( new PathDrawable(this) ) _path( new PathDrawable(this) )
{ {
setDrawable(_path); setDrawable(_path);
PathDrawable *path = _path.get();
addStyle("fill", &PathDrawable::setFill, path); if( !isInit<Path>() )
addStyle("fill-rule", &PathDrawable::setFillRule, path); {
addStyle("stroke", &PathDrawable::setStroke, path); PathDrawableRef Path::*path = &Path::_path;
addStyle("stroke-width", &PathDrawable::setStrokeWidth, path);
addStyle("stroke-dasharray", &PathDrawable::setStrokeDashArray, path); addStyle("fill", "color", &PathDrawable::setFill, path);
addStyle("stroke-linecap", &PathDrawable::setStrokeLinecap, 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(); setupStyle();
} }
+2 -1
View File
@@ -48,7 +48,8 @@ namespace canvas
}; };
class PathDrawable; class PathDrawable;
osg::ref_ptr<PathDrawable> _path; typedef osg::ref_ptr<PathDrawable> PathDrawableRef;
PathDrawableRef _path;
virtual void childRemoved(SGPropertyNode * child); virtual void childRemoved(SGPropertyNode * child);
virtual void childChanged(SGPropertyNode * child); virtual void childChanged(SGPropertyNode * child);
+26 -16
View File
@@ -254,22 +254,32 @@ namespace canvas
_text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION); _text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
_text->setRotation(osg::Quat(osg::PI, osg::X_AXIS)); _text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
addStyle("fill", &TextOSG::setFill, _text); if( !isInit<Text>() )
addStyle("background", &TextOSG::setBackgroundColor, _text); {
addStyle("character-size", osg::ref_ptr<TextOSG> Text::*text = &Text::_text;
static_cast<void (TextOSG::*)(float)>(&TextOSG::setCharacterSize),
_text); addStyle("fill", "color", &TextOSG::setFill, text);
addStyle("character-aspect-ratio", &TextOSG::setCharacterAspect, _text); addStyle("background", "color", &TextOSG::setBackgroundColor, text);
addStyle("padding", &TextOSG::setBoundingBoxMargin, _text); addStyle("character-size",
// TEXT = 1 default "numeric",
// BOUNDINGBOX = 2 static_cast<
// FILLEDBOUNDINGBOX = 4 void (TextOSG::*)(float)
// ALIGNMENT = 8 > (&TextOSG::setCharacterSize),
addStyle<int>("draw-mode", &TextOSG::setDrawMode, _text); text);
addStyle("max-width", &TextOSG::setMaximumWidth, _text); addStyle("character-aspect-ratio",
addStyle("font", &Text::setFont, this); "numeric",
addStyle("alignment", &Text::setAlignment, this); &TextOSG::setCharacterAspect, text);
addStyle("text", &Text::setText, this); 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(); setupStyle();
} }