]> git.mxchange.org Git - flightgear.git/commitdiff
Canvas: CSS like property value inheritance.
authorThomas Geymayer <tomgey@gmail.com>
Thu, 23 Aug 2012 17:52:36 +0000 (19:52 +0200)
committerThomas Geymayer <tomgey@gmail.com>
Thu, 23 Aug 2012 18:00:13 +0000 (20:00 +0200)
 - Groups store property nodes for different styles and pass them
   to child elements on creation (No dynamic updates yet)
 - Use StyleSetter map instead of loads of if/else if statements
   for setting element styles.
 - Unify element style properties (Only use one property, instead
   of multiple like it has been with colors)
 - Fix: Create only one element per property node (Was two)

16 files changed:
src/Canvas/canvas.cxx
src/Canvas/elements/CanvasImage.cxx
src/Canvas/elements/CanvasImage.hxx
src/Canvas/elements/element.cxx
src/Canvas/elements/element.hxx
src/Canvas/elements/group.cxx
src/Canvas/elements/group.hxx
src/Canvas/elements/map.cxx
src/Canvas/elements/map.hxx
src/Canvas/elements/path.cxx
src/Canvas/elements/path.hxx
src/Canvas/elements/text.cxx
src/Canvas/elements/text.hxx
src/Canvas/property_helper.cxx
src/Canvas/property_helper.hxx
src/Canvas/window.cxx

index 25ce15e8c27440b75555656cb3543d78eec11e99..eca171c3892cfeef5f3c3da6ece87885efefa957 100644 (file)
@@ -102,6 +102,10 @@ Canvas::Canvas(SGPropertyNode* node):
   _root_group( new canvas::Group(node) ),
   _render_always(false)
 {
+  // Remove automatically created property listener as we forward them on our
+  // own
+  _root_group->removeListener();
+
   _status = 0;
   setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
 
@@ -317,6 +321,7 @@ void Canvas::valueChanged(SGPropertyNode* node)
   if( boost::starts_with(node->getNameString(), "status") )
     return;
 
+  bool handled = true;
   if( node->getParent()->getParent() == _node )
   {
     if(    !_color_background.empty()
@@ -335,6 +340,8 @@ void Canvas::valueChanged(SGPropertyNode* node)
 
       _dirty_placements.push_back(node->getParent());
     }
+    else
+      handled = false;
   }
   else if( node->getParent() == _node )
   {
@@ -358,9 +365,14 @@ void Canvas::valueChanged(SGPropertyNode* node)
       else if( node->getIndex() == 1 )
         setViewHeight( node->getIntValue() );
     }
+    else
+      handled = false;
   }
+  else
+    handled = false;
 
-  _root_group->valueChanged(node);
+  if( !handled )
+    _root_group->valueChanged(node);
 }
 
 //------------------------------------------------------------------------------
index e0bbbb764d78e50ee166612b3f2d4b2a75017862..77ddbce13a2ec09d457309671dd3fb310287ac0a 100644 (file)
@@ -71,8 +71,8 @@ bool CullCallback::cull( osg::NodeVisitor* nv,
 namespace canvas
 {
   //----------------------------------------------------------------------------
-  Image::Image(SGPropertyNode_ptr node):
-    Element(node, COLOR_FILL | BOUNDING_BOX),
+  Image::Image(SGPropertyNode_ptr node, const Style& parent_style):
+    Element(node, parent_style, BOUNDING_BOX),
     _texture(new osg::Texture2D),
     _node_src_rect( node->getNode("source", 0, true) )
   {
@@ -103,6 +103,11 @@ namespace canvas
     _geom->addPrimitiveSet(prim);
 
     setDrawable(_geom);
+
+    addStyle("fill", &Image::setFill, this);
+    setFill("#ffffff"); // TODO how should we handle default values?
+
+    setupStyle();
   }
 
   //----------------------------------------------------------------------------
@@ -190,29 +195,18 @@ namespace canvas
   }
 
   //----------------------------------------------------------------------------
-  const Rect<float>& Image::getRegion() const
+  void Image::setFill(const std::string& fill)
   {
-    return _region;
+    osg::Vec4 color = parseColor(fill);
+    for( int i = 0; i < 4; ++i )
+      (*_colors)[i] = color;
+    _colors->dirty();
   }
 
   //----------------------------------------------------------------------------
-  void Image::valueChanged(SGPropertyNode *node)
+  const Rect<float>& Image::getRegion() const
   {
-    if( node->getParent() == _node_src_rect )
-    {
-      _attributes_dirty |= SRC_RECT;
-
-      if( node->getNameString() == "left" )
-        _src_rect.setLeft( node->getFloatValue() );
-      else if( node->getNameString() == "right" )
-        _src_rect.setRight( node->getFloatValue() );
-      else if( node->getNameString() == "top" )
-        _src_rect.setTop( node->getFloatValue() );
-      else if( node->getNameString() == "bottom" )
-        _src_rect.setBottom( node->getFloatValue() );
-    }
-    else
-      Element::valueChanged(node);
+    return _region;
   }
 
   //----------------------------------------------------------------------------
@@ -220,6 +214,24 @@ namespace canvas
   {
     const std::string& name = child->getNameString();
 
+    if( child->getParent() == _node_src_rect )
+    {
+      _attributes_dirty |= SRC_RECT;
+
+      if(      name == "left" )
+        _src_rect.setLeft( child->getFloatValue() );
+      else if( name == "right" )
+        _src_rect.setRight( child->getFloatValue() );
+      else if( name == "top" )
+        _src_rect.setTop( child->getFloatValue() );
+      else if( name == "bottom" )
+        _src_rect.setBottom( child->getFloatValue() );
+
+      return;
+    }
+    else if( child->getParent() != _node )
+      return;
+
     if( name == "x" )
     {
       _region.setX( child->getFloatValue() );
@@ -289,14 +301,6 @@ namespace canvas
     }
   }
 
-  //----------------------------------------------------------------------------
-  void Image::colorFillChanged(const osg::Vec4& color)
-  {
-    for( int i = 0; i < 4; ++i )
-      (*_colors)[i] = color;
-    _colors->dirty();
-  }
-
   //----------------------------------------------------------------------------
   void Image::setupDefaultDimensions()
   {
index d95268a6fa60727a628e638354acd505bcc254d4..b6ec6187b3db9e291a01168faa3dc4bac5c9e8a0 100644 (file)
@@ -39,8 +39,8 @@ namespace canvas
        *                  size[0-1]                     Dimensions of rectangle
        *                  [x,y]                         Position of rectangle
        */
-      Image(SGPropertyNode_ptr node);
-      ~Image();
+      Image(SGPropertyNode_ptr node, const Style& parent_style);
+      virtual ~Image();
 
       virtual void update(double dt);
 
@@ -48,14 +48,10 @@ namespace canvas
       CanvasWeakPtr getCanvas() const;
 
       void setImage(osg::Image *img);
+      void setFill(const std::string& fill);
 
       const Rect<float>& getRegion() const;
 
-      /**
-       * Callback for every changed child node
-       */
-      virtual void valueChanged(SGPropertyNode *node);
-
     protected:
 
       enum ImageAttributes
@@ -64,13 +60,7 @@ namespace canvas
         DEST_SIZE      = SRC_RECT << 1        // Element size
       };
 
-      /**
-       * Callback for changed direct child nodes
-       */
       virtual void childChanged(SGPropertyNode * child);
-      virtual void colorFillChanged(const osg::Vec4& color);
-
-      void handleHit(float x, float y);
 
       void setupDefaultDimensions();
       Rect<int> getTextureDimensions() const;
@@ -79,10 +69,10 @@ namespace canvas
       // TODO optionally forward events to canvas
       CanvasWeakPtr _canvas;
 
-      osg::Geometry *_geom;
-      osg::Vec3Array *_vertices;
-      osg::Vec2Array *_texCoords;
-      osg::Vec4Array* _colors;
+      osg::ref_ptr<osg::Geometry>  _geom;
+      osg::ref_ptr<osg::Vec3Array> _vertices;
+      osg::ref_ptr<osg::Vec2Array> _texCoords;
+      osg::ref_ptr<osg::Vec4Array> _colors;
 
       SGPropertyNode *_node_src_rect;
       Rect<float>     _src_rect,
index 040670a430e6b126c37963593d3dc7b9c24d565c..9b2d446a6aa1c8ddbf414f179914f8d325866b6b 100644 (file)
 #include <osg/Drawable>
 #include <osg/Geode>
 
+#include <boost/foreach.hpp>
+
 #include <cassert>
 #include <cstring>
 
 namespace canvas
 {
   const std::string NAME_TRANSFORM = "tf";
-  const std::string NAME_COLOR = "color";
-  const std::string NAME_COLOR_FILL = "color-fill";
+
+  //----------------------------------------------------------------------------
+  void Element::removeListener()
+  {
+    _node->removeChangeListener(this);
+  }
 
   //----------------------------------------------------------------------------
   Element::~Element()
   {
+    removeListener();
 
+    BOOST_FOREACH(osg::Group* parent, _transform->getParents())
+    {
+      parent->removeChild(_transform);
+    }
   }
 
   //----------------------------------------------------------------------------
@@ -95,24 +106,6 @@ namespace canvas
       _transform_dirty = false;
     }
 
-    if( _attributes_dirty & COLOR )
-    {
-      colorChanged( osg::Vec4( _color[0]->getFloatValue(),
-                               _color[1]->getFloatValue(),
-                               _color[2]->getFloatValue(),
-                               _color[3]->getFloatValue() ) );
-      _attributes_dirty &= ~COLOR;
-    }
-
-    if( _attributes_dirty & COLOR_FILL )
-    {
-      colorFillChanged( osg::Vec4( _color_fill[0]->getFloatValue(),
-                                   _color_fill[1]->getFloatValue(),
-                                   _color_fill[2]->getFloatValue(),
-                                   _color_fill[3]->getFloatValue() ) );
-      _attributes_dirty &= ~COLOR_FILL;
-    }
-
     if( !_bounding_box.empty() )
     {
       assert( _drawable );
@@ -159,18 +152,15 @@ namespace canvas
   //----------------------------------------------------------------------------
   void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
   {
-    if( parent == _node )
+    if(    parent == _node
+        && child->getNameString() == NAME_TRANSFORM )
     {
-      if( child->getNameString() == NAME_TRANSFORM )
-      {
-        if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
-          _transform_types.resize( child->getIndex() + 1 );
+      if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
+        _transform_types.resize( child->getIndex() + 1 );
 
-        _transform_types[ child->getIndex() ] = TT_NONE;
-        _transform_dirty = true;
-      }
-      else
-        childAdded(child);
+      _transform_types[ child->getIndex() ] = TT_NONE;
+      _transform_dirty = true;
+      return;
     }
     else if(    parent->getParent() == _node
              && parent->getNameString() == NAME_TRANSFORM )
@@ -191,16 +181,16 @@ namespace canvas
         type = TT_SCALE;
 
       _transform_dirty = true;
+      return;
     }
+
+    childAdded(child);
   }
 
   //----------------------------------------------------------------------------
   void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
   {
-    if( parent != _node )
-      return;
-
-    if( child->getNameString() == NAME_TRANSFORM )
+    if( parent == _node && child->getNameString() == NAME_TRANSFORM )
     {
       assert(child->getIndex() < static_cast<int>(_transform_types.size()));
       _transform_types[ child->getIndex() ] = TT_NONE;
@@ -209,54 +199,51 @@ namespace canvas
         _transform_types.pop_back();
 
       _transform_dirty = true;
+      return;
     }
-    else
-      childRemoved(child);
+
+    childRemoved(child);
   }
 
   //----------------------------------------------------------------------------
   void Element::valueChanged(SGPropertyNode* child)
   {
     SGPropertyNode *parent = child->getParent();
-    if( parent->getParent() == _node )
-    {
-      if( parent->getNameString() == NAME_TRANSFORM )
-        _transform_dirty = true;
-      else if( !_color.empty() && _color[0]->getParent() == parent )
-        _attributes_dirty |= COLOR;
-      else if( !_color_fill.empty() && _color_fill[0]->getParent() == parent )
-        _attributes_dirty |= COLOR_FILL;
-    }
-    else if( parent == _node )
+    if( parent == _node )
     {
-      if( child->getNameString() == "update" )
-        update(0);
+      if( setStyle(child) )
+        return;
+      else if( child->getNameString() == "update" )
+        return update(0);
       else if( child->getNameString() == "visible" )
         // TODO check if we need another nodemask
-        _transform->setNodeMask( child->getBoolValue() ? 0xffffffff : 0 );
-      else
-        childChanged(child);
+        return _transform->setNodeMask( child->getBoolValue() ? 0xffffffff : 0 );
+    }
+    else if(   parent->getParent() == _node
+            && parent->getNameString() == NAME_TRANSFORM )
+    {
+      _transform_dirty = true;
+      return;
     }
+
+    childChanged(child);
   }
 
   //----------------------------------------------------------------------------
-  Element::Element(SGPropertyNode_ptr node, uint32_t attributes_used):
+  Element::Element( SGPropertyNode_ptr node,
+                    const Style& parent_style,
+                    uint32_t attributes_used ):
     _attributes_used( attributes_used ),
     _attributes_dirty( attributes_used ),
     _transform_dirty( false ),
     _transform( new osg::MatrixTransform ),
     _node( node ),
+    _style( parent_style ),
     _drawable( 0 )
   {
     assert( _node );
     _node->addChangeListener(this);
 
-    if( _attributes_used & COLOR )
-      linkColorNodes("color", _node, _color, osg::Vec4f(0,0,0,1));
-
-    if( _attributes_used & COLOR_FILL )
-      linkColorNodes("color-fill", _node, _color_fill, osg::Vec4f(1,1,1,1));
-
     SG_LOG
     (
       SG_GL,
@@ -296,4 +283,23 @@ namespace canvas
     }
   }
 
+  //----------------------------------------------------------------------------
+  void Element::setupStyle()
+  {
+    BOOST_FOREACH( Style::value_type style, _style )
+      setStyle(style.second);
+  }
+
+  //----------------------------------------------------------------------------
+  bool Element::setStyle(const SGPropertyNode* child)
+  {
+    StyleSetters::const_iterator setter =
+      _style_setters.find(child->getNameString());
+    if( setter == _style_setters.end() )
+      return false;
+
+    setter->second(child);
+    return true;
+  }
+
 } // namespace canvas
index 782b5d1fb6a989e5163e7961f04f3d2851ff205b..3aae173644bc3779622276af9c40180ab99ad17a 100644 (file)
 #define CANVAS_ELEMENT_HXX_
 
 #include <simgear/props/props.hxx>
-#include <osg/MatrixTransform>
 #include <simgear/misc/stdint.hxx> // for uint32_t
+#include <osg/MatrixTransform>
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
 
 namespace osg
 {
@@ -36,6 +39,11 @@ namespace canvas
     public SGPropertyChangeListener
   {
     public:
+      typedef std::map<std::string, const SGPropertyNode*> Style;
+      typedef boost::function<void(const SGPropertyNode*)> StyleSetter;
+      typedef std::map<std::string, StyleSetter> StyleSetters;
+
+      void removeListener();
       virtual ~Element() = 0;
 
       /**
@@ -59,9 +67,7 @@ namespace canvas
 
       enum Attributes
       {
-        COLOR           = 0x0001,
-        COLOR_FILL      = 0x0002,
-        BOUNDING_BOX    = 0x0004,
+        BOUNDING_BOX    = 0x0001,
         LAST_ATTRIBUTE  = BOUNDING_BOX
       };
 
@@ -82,11 +88,48 @@ namespace canvas
       std::vector<TransformType>            _transform_types;
 
       SGPropertyNode_ptr                _node;
-      std::vector<SGPropertyNode_ptr>   _color,
-                                        _color_fill;
+      Style                             _style;
+      StyleSetters                      _style_setters;
       std::vector<SGPropertyNode_ptr>   _bounding_box;
 
-      Element(SGPropertyNode_ptr node, uint32_t attributes_used = 0);
+      Element( SGPropertyNode_ptr node,
+               const Style& parent_style,
+               uint32_t attributes_used = 0 );
+
+      template<typename T, class C1, class C2>
+      Element::StyleSetter
+      addStyle(const std::string& name, void (C1::*setter)(T), C2 instance)
+      {
+        return _style_setters[ name ] =
+                 bindStyleSetter<T>(name, setter, instance);
+      }
+
+      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
+      addStyle( const std::string& name,
+                void (C1::*setter)(const std::string&),
+                C2 instance )
+      {
+        return _style_setters[ name ] =
+                 bindStyleSetter<const char*>(name, setter, instance);
+      }
+
+      template<typename T1, typename T2, class C1, class C2>
+      Element::StyleSetter
+      bindStyleSetter( const std::string& name,
+                       void (C1::*setter)(T2),
+                       C2 instance )
+      {
+        return boost::bind(setter, instance, boost::bind(&getValue<T1>, _1));
+      }
 
       virtual bool handleLocalMouseEvent(const canvas::MouseEvent& event);
 
@@ -94,14 +137,14 @@ namespace canvas
       virtual void childRemoved(SGPropertyNode * child){}
       virtual void childChanged(SGPropertyNode * child){}
 
-      virtual void colorChanged(const osg::Vec4& color)  {}
-      virtual void colorFillChanged(const osg::Vec4& color){}
-
       void setDrawable( osg::Drawable* drawable );
+      void setupStyle();
+
+      bool setStyle(const SGPropertyNode* child);
 
     private:
 
-      osg::Drawable  *_drawable;
+      osg::ref_ptr<osg::Drawable> _drawable;
 
       Element(const Element&);// = delete
   };
index eef81587c1ecc2a958b93bfa2cce67bbe20e59d6..7cd9119e7414456179fb0bd567aaa63c5d010f99 100644 (file)
@@ -28,8 +28,8 @@ namespace canvas
 {
 
   //----------------------------------------------------------------------------
-  Group::Group(SGPropertyNode_ptr node):
-    Element(node)
+  Group::Group(SGPropertyNode_ptr node, const Style& parent_style):
+    Element(node, parent_style)
   {
 
   }
@@ -64,27 +64,33 @@ namespace canvas
   //----------------------------------------------------------------------------
   void Group::childAdded(SGPropertyNode* child)
   {
+    if( child->getParent() != _node )
+      return;
+
     boost::shared_ptr<Element> element;
 
     // TODO create map of child factories and use also to check for element
     //      on deletion in ::childRemoved
     if( child->getNameString() == "text" )
-      element.reset( new Text(child) );
+      element.reset( new Text(child, _style) );
     else if( child->getNameString() == "group" )
-      element.reset( new Group(child) );
+      element.reset( new Group(child, _style) );
     else if( child->getNameString() == "map" )
-      element.reset( new Map(child) );
+      element.reset( new Map(child, _style) );
     else if( child->getNameString() == "path" )
-      element.reset( new Path(child) );
+      element.reset( new Path(child, _style) );
     else if( child->getNameString() == "image" )
-      element.reset( new Image(child) );
+      element.reset( new Image(child, _style) );
 
-    if( !element )
+    if( element )
+    {
+      // Add to osg scene graph...
+      _transform->addChild( element->getMatrixTransform() );
+      _children.push_back( ChildList::value_type(child, element) );
       return;
+    }
 
-    // Add to osg scene graph...
-    _transform->addChild( element->getMatrixTransform() );
-    _children.push_back( ChildList::value_type(child, element) );
+    _style[ child->getNameString() ] = child;
   }
 
   //----------------------------------------------------------------------------
@@ -107,6 +113,9 @@ namespace canvas
   //----------------------------------------------------------------------------
   void Group::childRemoved(SGPropertyNode* node)
   {
+    if( node->getParent() != _node )
+      return;
+
     if(    node->getNameString() == "text"
         || node->getNameString() == "group"
         || node->getNameString() == "map"
@@ -130,6 +139,12 @@ namespace canvas
         _children.erase(child);
       }
     }
+    else
+    {
+      Style::iterator style = _style.find(node->getNameString());
+      if( style != _style.end() )
+        _style.erase(style);
+    }
   }
 
 } // namespace canvas
index af64f70dc634a9659382bbb435c85e9be73c7483..84cafa5b5e4627f1cfae14b11ebb9d28037ae872 100644 (file)
@@ -22,6 +22,7 @@
 #include "element.hxx"
 #include <boost/shared_ptr.hpp>
 #include <list>
+#include <map>
 
 namespace canvas
 {
@@ -37,7 +38,7 @@ namespace canvas
                                   >
                        > ChildList;
 
-      Group(SGPropertyNode_ptr node);
+      Group(SGPropertyNode_ptr node, const Style& parent_style = Style());
       virtual ~Group();
 
       virtual void update(double dt);
index 97bb6e5d69042f5d9d6fe62df3de77658531d3b7..fd793b8d0e174f63e5445233dd2423b3affc6909 100644 (file)
@@ -46,8 +46,8 @@ namespace canvas
   const std::string GEO = "-geo";
 
   //----------------------------------------------------------------------------
-  Map::Map(SGPropertyNode_ptr node):
-    Group(node),
+  Map::Map(SGPropertyNode_ptr node, const Style& parent_style):
+    Group(node, parent_style),
     _projection_dirty(true)
   {
 
@@ -177,6 +177,9 @@ namespace canvas
   //----------------------------------------------------------------------------
   void Map::childChanged(SGPropertyNode * child)
   {
+    if( child->getParent() != _node )
+      return;
+
     if(    child->getNameString() == "ref-lat"
         || child->getNameString() == "ref-lon" )
       projection.setWorldPosition( _node->getDoubleValue("ref-lat"),
index c875eec1fa4e269eb55ea2189c84f44e7757e480..a5ae1fb828d2aefa0dbd94e59005c5369b71648f 100644 (file)
@@ -32,7 +32,7 @@ namespace canvas
     public Group
   {
     public:
-      Map(SGPropertyNode_ptr node);
+      Map(SGPropertyNode_ptr node, const Style& parent_style);
       virtual ~Map();
 
       virtual void update(double dt);
index 63b34f17000907e3028835131c4d9fbe3b83fcd0..bf800e076c5b5f11907e2fd3d3747bcb8dcc165b 100644 (file)
@@ -40,9 +40,9 @@ namespace canvas
         _paint(VG_INVALID_HANDLE),
         _paint_fill(VG_INVALID_HANDLE),
         _attributes_dirty(~0),
+        _mode(0),
         _stroke_width(1),
-        _stroke_linecap(VG_CAP_BUTT),
-        _fill(false)
+        _stroke_linecap(VG_CAP_BUTT)
       {
         setSupportsDisplayList(false);
         setDataVariance(Object::DYNAMIC);
@@ -76,43 +76,54 @@ namespace canvas
       }
 
       /**
-       * Set stroke width and dash (line stipple)
+       * Set path fill paint ("none" if not filled)
        */
-      void setStroke( float width,
-                      const std::vector<float> dash = std::vector<float>() )
+      void setFill(const std::string& fill)
       {
-        _stroke_width = width;
-        _stroke_dash = dash;
-
-        _attributes_dirty |= BOUNDING_BOX;
+        if( fill == "none" )
+        {
+          _mode &= ~VG_FILL_PATH;
+        }
+        else
+        {
+          _fill_color = parseColor(fill);
+          _mode |= VG_FILL_PATH;
+          _attributes_dirty |= FILL_COLOR;
+        }
       }
 
       /**
-       * Set the line color
+       * Set path stroke paint ("none" if no stroke)
        */
-      void setColor(const osg::Vec4& color)
+      void setStroke(const std::string& stroke)
       {
-        for( size_t i = 0; i < 4; ++i )
-          _stroke_color[i] = color[i];
-        _attributes_dirty |= STROKE_COLOR;
+        if( stroke == "none" )
+        {
+          _mode &= ~VG_STROKE_PATH;
+        }
+        else
+        {
+          _stroke_color = parseColor(stroke);
+          _mode |= VG_STROKE_PATH;
+          _attributes_dirty |= STROKE_COLOR;
+        }
       }
 
       /**
-       * Enable/Disable filling of the path
+       * Set stroke width
        */
-      void enableFill(bool enable)
+      void setStrokeWidth(float width)
       {
-        _fill = enable;
+        _stroke_width = width;
+        _attributes_dirty |= BOUNDING_BOX;
       }
 
       /**
-       * Set the line color
+       * Set stroke dash (line stipple)
        */
-      void setColorFill(const osg::Vec4& color)
+      void setStrokeDashArray(const std::string& dash)
       {
-        for( size_t i = 0; i < 4; ++i )
-          _fill_color[i] = color[i];
-        _attributes_dirty |= FILL_COLOR;
+        _stroke_dash = splitAndConvert(",\t\n ", dash);
       }
 
       /**
@@ -166,28 +177,25 @@ namespace canvas
           if( _paint == VG_INVALID_HANDLE )
             _paint = vgCreatePaint();
 
-          vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _stroke_color);
+          vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _stroke_color._v);
 
           _attributes_dirty &= ~STROKE_COLOR;
         }
 
         // Initialize/update fill paint
-        if( _attributes_dirty & (FILL_COLOR | FILL) )
+        if( _attributes_dirty & FILL_COLOR )
         {
           if( _paint_fill == VG_INVALID_HANDLE )
             _paint_fill = vgCreatePaint();
 
-          if( _attributes_dirty & FILL_COLOR )
-            vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, _fill_color);
+          vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, _fill_color._v);
 
-          _attributes_dirty &= ~(FILL_COLOR | FILL);
+          _attributes_dirty &= ~FILL_COLOR;
         }
 
-        // Detect draw mode
-        VGbitfield mode = 0;
-        if( _stroke_width > 0 )
+        // Setup paint
+        if( _mode & VG_STROKE_PATH )
         {
-          mode |= VG_STROKE_PATH;
           vgSetPaint(_paint, VG_STROKE_PATH);
 
           vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width);
@@ -196,15 +204,14 @@ namespace canvas
                    _stroke_dash.size(),
                    _stroke_dash.empty() ? 0 : &_stroke_dash[0] );
         }
-        if( _fill )
+        if( _mode & VG_FILL_PATH )
         {
-          mode |= VG_FILL_PATH;
           vgSetPaint(_paint_fill, VG_FILL_PATH);
         }
 
         // And finally draw the path
-        if( mode )
-          vgDrawPath(_path, mode);
+        if( _mode )
+          vgDrawPath(_path, _mode);
 
         VGErrorCode err = vgGetError();
         if( err != VG_NO_ERROR )
@@ -246,8 +253,7 @@ namespace canvas
         PATH            = 0x0001,
         STROKE_COLOR    = PATH << 1,
         FILL_COLOR      = STROKE_COLOR << 1,
-        FILL            = FILL_COLOR << 1,
-        BOUNDING_BOX    = FILL << 1
+        BOUNDING_BOX    = FILL_COLOR << 1
       };
 
       mutable VGPath    _path;
@@ -258,14 +264,13 @@ namespace canvas
       CmdList   _cmds;
       CoordList _coords;
 
-      VGfloat               _stroke_color[4];
+      VGbitfield            _mode;
+      osg::Vec4f            _fill_color;
+      osg::Vec4f            _stroke_color;
       VGfloat               _stroke_width;
       std::vector<VGfloat>  _stroke_dash;
       VGCapStyle            _stroke_linecap;
 
-      bool      _fill;
-      VGfloat   _fill_color[4];
-
       /**
        * Initialize/Update the OpenVG path
        */
@@ -339,11 +344,20 @@ namespace canvas
   bool PathDrawable::_vg_initialized = false;
 
   //----------------------------------------------------------------------------
-  Path::Path(SGPropertyNode_ptr node):
-    Element(node, COLOR | COLOR_FILL | BOUNDING_BOX),
+  Path::Path(SGPropertyNode_ptr node, const Style& parent_style):
+    Element(node, parent_style, BOUNDING_BOX),
     _path( new PathDrawable() )
   {
     setDrawable(_path);
+    PathDrawable *path = _path.get();
+
+    addStyle("fill", &PathDrawable::setFill, path);
+    addStyle("stroke", &PathDrawable::setStroke, path);
+    addStyle("stroke-width", &PathDrawable::setStrokeWidth, path);
+    addStyle("stroke-dasharray", &PathDrawable::setStrokeDashArray, path);
+    addStyle("stroke-linecap", &PathDrawable::setStrokeLinecap, path);
+
+    setupStyle();
   }
 
   //----------------------------------------------------------------------------
@@ -365,16 +379,6 @@ namespace canvas
 
       _attributes_dirty &= ~(CMDS | COORDS);
     }
-    if( _attributes_dirty & STROKE )
-    {
-      _path->setStroke
-      (
-        _node->getFloatValue("stroke-width", 1),
-        getVectorFromChildren<VGfloat, float>(_node, "stroke-dasharray")
-      );
-
-      _attributes_dirty &= ~STROKE;
-    }
 
     Element::update(dt);
   }
@@ -382,29 +386,13 @@ namespace canvas
   //----------------------------------------------------------------------------
   void Path::childChanged(SGPropertyNode* child)
   {
+    if( child->getParent() != _node )
+      return;
+
     if( child->getNameString() == "cmd" )
       _attributes_dirty |= CMDS;
     else if( child->getNameString() == "coord" )
       _attributes_dirty |= COORDS;
-    else if(    child->getNameString() == "stroke-width"
-             || child->getNameString() == "stroke-dasharray" )
-      _attributes_dirty |= STROKE;
-    else if( child->getNameString() == "stroke-linecap" )
-      _path->setStrokeLinecap( child->getStringValue() );
-    else if( child->getNameString() == "fill" )
-      _path->enableFill( child->getBoolValue() );
-  }
-
-  //----------------------------------------------------------------------------
-  void Path::colorChanged(const osg::Vec4& color)
-  {
-    _path->setColor(color);
-  }
-
-  //----------------------------------------------------------------------------
-  void Path::colorFillChanged(const osg::Vec4& color)
-  {
-    _path->setColorFill(color);
   }
 
 } // namespace canvas
index a2bd8e5acfd4a4a3298d18a152b10a67c8641b63..6a93f25fe2c60b1ada24d81d0dc1f3a3ddd8b8ba 100644 (file)
@@ -28,7 +28,7 @@ namespace canvas
     public Element
   {
     public:
-      Path(SGPropertyNode_ptr node);
+      Path(SGPropertyNode_ptr node, const Style& parent_style);
       virtual ~Path();
 
       virtual void update(double dt);
@@ -38,15 +38,12 @@ namespace canvas
       enum PathAttributes
       {
         CMDS       = LAST_ATTRIBUTE << 1,
-        COORDS     = CMDS << 1,
-        STROKE     = COORDS << 1
+        COORDS     = CMDS << 1
       };
 
       osg::ref_ptr<PathDrawable> _path;
 
       virtual void childChanged(SGPropertyNode * child);
-      virtual void colorChanged(const osg::Vec4& color);
-      virtual void colorFillChanged(const osg::Vec4& color);
   };
 
 }  // namespace canvas
index 3171acc7f852ca1017a1e2cc792b68fe16533405..3159d8c7c9b6284712f12635c26ca93238ebe955 100644 (file)
@@ -30,11 +30,37 @@ namespace canvas
     public osgText::Text
   {
     public:
+
+      void setCharacterAspect(float aspect);
+      void setFill(const std::string& fill);
+      void setBackgroundColor(const std::string& fill);
+
       osg::Vec2 handleHit(float x, float y);
 
       virtual osg::BoundingBox computeBound() const;
   };
 
+  //----------------------------------------------------------------------------
+  void Text::TextOSG::setCharacterAspect(float aspect)
+  {
+    setCharacterSize(getCharacterHeight(), aspect);
+  }
+
+  //----------------------------------------------------------------------------
+  void Text::TextOSG::setFill(const std::string& fill)
+  {
+//    if( fill == "none" )
+//      TODO No text
+//    else
+      setColor( parseColor(fill) );
+  }
+
+  //----------------------------------------------------------------------------
+  void Text::TextOSG::setBackgroundColor(const std::string& fill)
+  {
+    setBoundingBoxColor( parseColor(fill) );
+  }
+
   //----------------------------------------------------------------------------
   osg::Vec2 Text::TextOSG::handleHit(float x, float y)
   {
@@ -130,19 +156,33 @@ namespace canvas
   }
 
   //----------------------------------------------------------------------------
-  Text::Text(SGPropertyNode_ptr node):
-    Element(node, COLOR | COLOR_FILL | BOUNDING_BOX),
-    _text( new Text::TextOSG() ),
-    _font_size( 0 ),
-    _font_aspect( 0 )
+  Text::Text(SGPropertyNode_ptr node, const Style& parent_style):
+    Element(node, parent_style, BOUNDING_BOX),
+    _text( new Text::TextOSG() )
   {
     setDrawable(_text);
     _text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
     _text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
     _text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
 
-    _font_size = getChildDefault<float>(_node, "character-size", 32);
-    _font_aspect = getChildDefault<float>(_node, "character-aspect-ratio", 1);
+    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);
+
+    setupStyle();
   }
 
   //----------------------------------------------------------------------------
@@ -152,20 +192,9 @@ namespace canvas
   }
 
   //----------------------------------------------------------------------------
-  void Text::update(double dt)
+  void Text::setText(const char* text)
   {
-    Element::update(dt);
-
-    if( _attributes_dirty & FONT_SIZE )
-    {
-      _text->setCharacterSize
-      (
-        _font_size->getFloatValue(),
-        _font_aspect->getFloatValue()
-      );
-
-      _attributes_dirty &= ~FONT_SIZE;
-    }
+    _text->setText(text, osgText::String::ENCODING_UTF8);
   }
 
   //----------------------------------------------------------------------------
@@ -213,51 +242,20 @@ namespace canvas
     }
   }
 #endif
+
   //----------------------------------------------------------------------------
   void Text::childChanged(SGPropertyNode* child)
   {
-    const std::string& name = child->getNameString();
+    if( child->getParent() != _node )
+      return;
 
+    const std::string& name = child->getNameString();
     if( name == "hit-y" )
       handleHit
       (
         _node->getFloatValue("hit-x"),
         _node->getFloatValue("hit-y")
       );
-    else if( _font_size == child || _font_aspect == child )
-      _attributes_dirty |= FONT_SIZE;
-    else if( name == "text" )
-      _text->setText
-      (
-        osgText::String( child->getStringValue(),
-                         osgText::String::ENCODING_UTF8 )
-      );
-    else if( name == "padding" )
-      _text->setBoundingBoxMargin( child->getFloatValue() );
-    else if( name == "draw-mode" )
-      //  TEXT              = 1 default
-      //  BOUNDINGBOX       = 2
-      //  FILLEDBOUNDINGBOX = 4
-      //  ALIGNMENT         = 8
-      _text->setDrawMode( child->getIntValue() );
-    else if( name == "max-width" )
-      _text->setMaximumWidth( child->getFloatValue() );
-    else if( name == "font" )
-      setFont( child->getStringValue() );
-    else if( name == "alignment" )
-      setAlignment( child->getStringValue() );
-  }
-
-  //----------------------------------------------------------------------------
-  void Text::colorChanged(const osg::Vec4& color)
-  {
-    _text->setColor(color);
-  }
-
-  //----------------------------------------------------------------------------
-  void Text::colorFillChanged(const osg::Vec4& color)
-  {
-    _text->setBoundingBoxColor(color);
   }
 
   //----------------------------------------------------------------------------
index 630e717cfddadb6bf5335b702e292bd9dcf0cc9b..42e79f9472e3dac69fa4b9ba5dc39faa3d986e82 100644 (file)
@@ -32,30 +32,19 @@ namespace canvas
     public Element
   {
     public:
-      Text(SGPropertyNode_ptr node);
+      Text(SGPropertyNode_ptr node, const Style& parent_style);
       ~Text();
 
-      virtual void update(double dt);
-
+      void setText(const char* text);
       void setFont(const char* name);
       void setAlignment(const char* align);
 
     protected:
 
-      enum TextAttributes
-      {
-        FONT_SIZE       = LAST_ATTRIBUTE << 1, // Font size and aspect ration
-      };
-
       class TextOSG;
       osg::ref_ptr<TextOSG> _text;
 
-      SGPropertyNode_ptr  _font_size,
-                          _font_aspect;
-
       virtual void childChanged(SGPropertyNode * child);
-      virtual void colorChanged(const osg::Vec4& color);
-      virtual void colorFillChanged(const osg::Vec4& color);
 
       void handleHit(float x, float y);
 
index 795dd72d0cc1b3d2992af6fc126872a0a56f4d00..ce5add9dd70301595b5bb44c39ae0269948b8479 100644 (file)
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 #include "property_helper.hxx"
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/trim.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/tokenizer.hpp>
+
 #include <cassert>
 
 namespace canvas
 {
+  //----------------------------------------------------------------------------
+  std::vector<float> splitAndConvert(const char del[], const std::string& str)
+  {
+    std::vector<float> values;
+    size_t pos = 0;
+    for(;;)
+    {
+      pos = str.find_first_not_of(del, pos);
+      if( pos == std::string::npos )
+        break;
+
+      char *end = 0;
+      float val = strtod(&str[pos], &end);
+      if( end == &str[pos] || !end )
+        break;
+
+      values.push_back(val);
+      pos = end - &str[0];
+    }
+    return values;
+  }
+
+  //----------------------------------------------------------------------------
+  osg::Vec4 parseColor(std::string str)
+  {
+    boost::trim(str);
+    osg::Vec4 color(0,0,0,1);
+
+    if( str.empty() )
+      return color;
+
+    // #rrggbb
+    if( str[0] == '#' )
+    {
+      const int offsets[] = {2,2,2};
+      const boost::offset_separator hex_separator( boost::begin(offsets),
+                                                   boost::end(offsets) );
+      typedef boost::tokenizer<boost::offset_separator> offset_tokenizer;
+      offset_tokenizer tokens(str.begin() + 1, str.end(), hex_separator);
+
+      int comp = 0;
+      for( offset_tokenizer::const_iterator tok = tokens.begin();
+           tok != tokens.end() && comp < 4;
+           ++tok, ++comp )
+      {
+        color._v[comp] = strtol(std::string(*tok).c_str(), 0, 16) / 255.f;
+      }
+    }
+    // rgb(r,g,b)
+    // rgba(r,g,b,a)
+    else if( boost::ends_with(str, ")") )
+    {
+      const std::string RGB = "rgb(",
+                        RGBA = "rgba(";
+      size_t pos;
+      if( boost::starts_with(str, RGB) )
+        pos = RGB.length();
+      else if( boost::starts_with(str, RGBA) )
+        pos = RGBA.length();
+      else
+        return color;
+
+      typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+      const boost::char_separator<char> del(", \t\n");
+
+      tokenizer tokens(str.begin() + pos, str.end() - 1, del);
+      int comp = 0;
+      for( tokenizer::const_iterator tok = tokens.begin();
+           tok != tokens.end() && comp < 4;
+           ++tok, ++comp )
+      {
+        color._v[comp] = boost::lexical_cast<float>(*tok)
+                       // rgb = [0,255], a = [0,1]
+                       / (comp < 3 ? 255 : 1);
+      }
+    }
+    else
+      SG_LOG(SG_GENERAL, SG_WARN, "Unknown color: " << str);
+
+    return color;
+  }
+
   //----------------------------------------------------------------------------
   void linkColorNodes( const char* name,
                        SGPropertyNode* parent,
index dbafdd77eb279634f49bea0bf8929ea953d04738..a92c0466660d7da4979ae9aa3047c9a7c5deb46a 100644 (file)
@@ -60,12 +60,26 @@ namespace canvas
     return values;
   }
 
+  /**
+   * Split a string by a delimter and convert the values to float
+   *
+   * TODO do we need other types than float?
+   */
+  std::vector<float> splitAndConvert(const char del[], const std::string& str);
+
+  /**
+   * Parse a (CSS) color
+   */
+  osg::Vec4 parseColor(std::string str);
+
   /**
    * @param name    Name of color node
    * @param parent  Parent for color channel nodes
    * @param nodes   Vector to push color nodes into
    * @param def     Default color
    *
+   * @deprecated Use only a single property instead and parse with #parseColor
+   *
    * <name>
    *   <red type="float">def[0] or existing value</red>
    *   <green type="float">def[1] or existing value</green>
index e6db4c30adef56af61625f5102d045a8273cabb0..b49b7279a4ae7cbccc83de28ef0b8101bbe3e1f7 100644 (file)
@@ -28,8 +28,10 @@ namespace canvas
   //----------------------------------------------------------------------------
   Window::Window(SGPropertyNode* node):
     PropertyBasedElement(node),
-    _image(node)
+    _image(node, Element::Style())
   {
+    _image.removeListener();
+
     // TODO probably better remove default position and size
     node->setFloatValue("x", 50);
     node->setFloatValue("y", 100);
@@ -44,10 +46,7 @@ namespace canvas
   //----------------------------------------------------------------------------
   Window::~Window()
   {
-    BOOST_FOREACH(osg::Group* parent, getGroup()->getParents())
-    {
-      parent->removeChild(getGroup());
-    }
+
   }
 
   //----------------------------------------------------------------------------