From: Thomas Geymayer Date: Thu, 23 Aug 2012 17:52:36 +0000 (+0200) Subject: Canvas: CSS like property value inheritance. X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=ced478cf95934816256a5c35c90960ca6ba9c472;p=flightgear.git Canvas: CSS like property value inheritance. - 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) --- diff --git a/src/Canvas/canvas.cxx b/src/Canvas/canvas.cxx index 25ce15e8c..eca171c38 100644 --- a/src/Canvas/canvas.cxx +++ b/src/Canvas/canvas.cxx @@ -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); } //------------------------------------------------------------------------------ diff --git a/src/Canvas/elements/CanvasImage.cxx b/src/Canvas/elements/CanvasImage.cxx index e0bbbb764..77ddbce13 100644 --- a/src/Canvas/elements/CanvasImage.cxx +++ b/src/Canvas/elements/CanvasImage.cxx @@ -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& 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& 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() { diff --git a/src/Canvas/elements/CanvasImage.hxx b/src/Canvas/elements/CanvasImage.hxx index d95268a6f..b6ec6187b 100644 --- a/src/Canvas/elements/CanvasImage.hxx +++ b/src/Canvas/elements/CanvasImage.hxx @@ -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& 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 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 _geom; + osg::ref_ptr _vertices; + osg::ref_ptr _texCoords; + osg::ref_ptr _colors; SGPropertyNode *_node_src_rect; Rect _src_rect, diff --git a/src/Canvas/elements/element.cxx b/src/Canvas/elements/element.cxx index 040670a43..9b2d446a6 100644 --- a/src/Canvas/elements/element.cxx +++ b/src/Canvas/elements/element.cxx @@ -23,19 +23,30 @@ #include #include +#include + #include #include 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(_transform_types.size()) ) - _transform_types.resize( child->getIndex() + 1 ); + if( child->getIndex() >= static_cast(_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(_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 diff --git a/src/Canvas/elements/element.hxx b/src/Canvas/elements/element.hxx index 782b5d1fb..3aae17364 100644 --- a/src/Canvas/elements/element.hxx +++ b/src/Canvas/elements/element.hxx @@ -20,8 +20,11 @@ #define CANVAS_ELEMENT_HXX_ #include -#include #include // for uint32_t +#include + +#include +#include namespace osg { @@ -36,6 +39,11 @@ namespace canvas public SGPropertyChangeListener { public: + typedef std::map Style; + typedef boost::function StyleSetter; + typedef std::map 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 _transform_types; SGPropertyNode_ptr _node; - std::vector _color, - _color_fill; + Style _style; + StyleSetters _style_setters; std::vector _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 + Element::StyleSetter + addStyle(const std::string& name, void (C1::*setter)(T), C2 instance) + { + return _style_setters[ name ] = + bindStyleSetter(name, setter, instance); + } + + template + Element::StyleSetter + addStyle(const std::string& name, void (C1::*setter)(T2), C2 instance) + { + return _style_setters[ name ] = + bindStyleSetter(name, setter, instance); + } + + template + Element::StyleSetter + addStyle( const std::string& name, + void (C1::*setter)(const std::string&), + C2 instance ) + { + return _style_setters[ name ] = + bindStyleSetter(name, setter, instance); + } + + template + Element::StyleSetter + bindStyleSetter( const std::string& name, + void (C1::*setter)(T2), + C2 instance ) + { + return boost::bind(setter, instance, boost::bind(&getValue, _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 _drawable; Element(const Element&);// = delete }; diff --git a/src/Canvas/elements/group.cxx b/src/Canvas/elements/group.cxx index eef81587c..7cd9119e7 100644 --- a/src/Canvas/elements/group.cxx +++ b/src/Canvas/elements/group.cxx @@ -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; // 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 diff --git a/src/Canvas/elements/group.hxx b/src/Canvas/elements/group.hxx index af64f70dc..84cafa5b5 100644 --- a/src/Canvas/elements/group.hxx +++ b/src/Canvas/elements/group.hxx @@ -22,6 +22,7 @@ #include "element.hxx" #include #include +#include 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); diff --git a/src/Canvas/elements/map.cxx b/src/Canvas/elements/map.cxx index 97bb6e5d6..fd793b8d0 100644 --- a/src/Canvas/elements/map.cxx +++ b/src/Canvas/elements/map.cxx @@ -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"), diff --git a/src/Canvas/elements/map.hxx b/src/Canvas/elements/map.hxx index c875eec1f..a5ae1fb82 100644 --- a/src/Canvas/elements/map.hxx +++ b/src/Canvas/elements/map.hxx @@ -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); diff --git a/src/Canvas/elements/path.cxx b/src/Canvas/elements/path.cxx index 63b34f170..bf800e076 100644 --- a/src/Canvas/elements/path.cxx +++ b/src/Canvas/elements/path.cxx @@ -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 dash = std::vector() ) + 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 _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(_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 diff --git a/src/Canvas/elements/path.hxx b/src/Canvas/elements/path.hxx index a2bd8e5ac..6a93f25fe 100644 --- a/src/Canvas/elements/path.hxx +++ b/src/Canvas/elements/path.hxx @@ -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 _path; virtual void childChanged(SGPropertyNode * child); - virtual void colorChanged(const osg::Vec4& color); - virtual void colorFillChanged(const osg::Vec4& color); }; } // namespace canvas diff --git a/src/Canvas/elements/text.cxx b/src/Canvas/elements/text.cxx index 3171acc7f..3159d8c7c 100644 --- a/src/Canvas/elements/text.cxx +++ b/src/Canvas/elements/text.cxx @@ -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(_node, "character-size", 32); - _font_aspect = getChildDefault(_node, "character-aspect-ratio", 1); + addStyle("fill", &TextOSG::setFill, _text); + addStyle("background", &TextOSG::setBackgroundColor, _text); + addStyle("character-size", + static_cast(&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("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); } //---------------------------------------------------------------------------- diff --git a/src/Canvas/elements/text.hxx b/src/Canvas/elements/text.hxx index 630e717cf..42e79f947 100644 --- a/src/Canvas/elements/text.hxx +++ b/src/Canvas/elements/text.hxx @@ -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 _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); diff --git a/src/Canvas/property_helper.cxx b/src/Canvas/property_helper.cxx index 795dd72d0..ce5add9dd 100644 --- a/src/Canvas/property_helper.cxx +++ b/src/Canvas/property_helper.cxx @@ -17,10 +17,98 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "property_helper.hxx" + +#include +#include +#include +#include + #include namespace canvas { + //---------------------------------------------------------------------------- + std::vector splitAndConvert(const char del[], const std::string& str) + { + std::vector 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 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 > tokenizer; + const boost::char_separator 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(*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, diff --git a/src/Canvas/property_helper.hxx b/src/Canvas/property_helper.hxx index dbafdd77e..a92c04666 100644 --- a/src/Canvas/property_helper.hxx +++ b/src/Canvas/property_helper.hxx @@ -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 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 + * * * def[0] or existing value * def[1] or existing value diff --git a/src/Canvas/window.cxx b/src/Canvas/window.cxx index e6db4c30a..b49b7279a 100644 --- a/src/Canvas/window.cxx +++ b/src/Canvas/window.cxx @@ -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()); - } + } //----------------------------------------------------------------------------