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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() )
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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?
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user