]> git.mxchange.org Git - simgear.git/commitdiff
Canvas: rework style setter system.
authorThomas Geymayer <tomgey@gmail.com>
Fri, 19 Apr 2013 22:22:37 +0000 (00:22 +0200)
committerThomas Geymayer <tomgey@gmail.com>
Fri, 19 Apr 2013 22:22:37 +0000 (00:22 +0200)
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.

simgear/canvas/elements/CanvasElement.cxx
simgear/canvas/elements/CanvasElement.hxx
simgear/canvas/elements/CanvasGroup.cxx
simgear/canvas/elements/CanvasGroup.hxx
simgear/canvas/elements/CanvasImage.cxx
simgear/canvas/elements/CanvasPath.cxx
simgear/canvas/elements/CanvasPath.hxx
simgear/canvas/elements/CanvasText.cxx

index 2abba3a415ed12f04a0e8cd0db5f72f79fb7b5e6..0d3a0589d1a0120021d635d6797a6b2e0b5ed6ff 100644 (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);
+    }
   }
 
   //----------------------------------------------------------------------------
index e6cd3012106cc3ebde542d3647a6e08db0ec780c..3196182576f406b0aaa1a1203c7fab72ac33b629 100644 (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)
+      /**
+       * 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,
+                const std::string& type,
+                const boost::function<void (Derived&, T2)>& setter )
+      {
+        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 T,
+        class Derived
+      >
+      StyleSetter
+      addStyle( const std::string& name,
+                const std::string& type,
+                const boost::function<void (Derived&, T)>& setter )
+      {
+        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 _style_setters[ name ] =
-                 bindStyleSetter<T1>(name, setter, instance);
+        return addStyle<const char*, const std::string&>
+        (
+          name,
+          type,
+          boost::function<void (Derived&, const std::string&)>(setter)
+        );
       }
 
-      template<class C1, class C2>
-      Element::StyleSetter
+      template<
+        typename T,
+        class Derived,
+        class Other,
+        class OtherRef
+      >
+      StyleSetter
       addStyle( const std::string& name,
-                void (C1::*setter)(const std::string&),
-                C2 instance )
+                const std::string& type,
+                void (Other::*setter)(T),
+                OtherRef Derived::*instance_ref )
       {
-        return _style_setters[ name ] =
-                 bindStyleSetter<const char*>(name, setter, instance);
+        return addStyle<T, T>(name, type, bindOther(setter, instance_ref));
       }
 
-      template<typename T1, typename T2, class C1, class C2>
-      Element::StyleSetter
+      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,
-                       void (C1::*setter)(T2),
-                       C2 instance )
+                       const boost::function<void (Derived&, T2)>& setter )
       {
-        return boost::bind(setter, instance, boost::bind(&getValue<T1>, _1));
+        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
index 8263b079f34c8ef7a7612b28ce5afa56578d6ffd..9bfb8c2f26d3df44be309cc8815c591423dde953 100644 (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() )
     {
index 7eec76642fd0a12892e2ed90cbbb15e0417fd2b9..524b5dfbbdcd4d8651c6f5a18d2c17c94a901268 100644 (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);
index e9a683cbcb74193f624bbc1c81a2d9bf7531af36..7400833f19e5c11712792d0a42869e9fbdae878d 100644 (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?
 
index 9fde6782480117cce5eb7930edc271b18efc68d0..045aabf5e0ff6799cc11553b65f6c2be736067be 100644 (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();
   }
index 2cde2dc905b555935ccf2a904447bf98a4ae8037..706f8cd715f53dda5ce17f120c6af57f0a2d5040 100644 (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);
index 8c4028bf0eacfbea81021fceb52dccfc11aff696..a00cc1fa703f6b362a687034b6255dd273025c2d 100644 (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();
   }