_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);
if( boost::starts_with(node->getNameString(), "status") )
return;
+ bool handled = true;
if( node->getParent()->getParent() == _node )
{
if( !_color_background.empty()
_dirty_placements.push_back(node->getParent());
}
+ else
+ handled = false;
}
else if( node->getParent() == _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);
}
//------------------------------------------------------------------------------
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) )
{
_geom->addPrimitiveSet(prim);
setDrawable(_geom);
+
+ addStyle("fill", &Image::setFill, this);
+ setFill("#ffffff"); // TODO how should we handle default values?
+
+ setupStyle();
}
//----------------------------------------------------------------------------
}
//----------------------------------------------------------------------------
- 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;
}
//----------------------------------------------------------------------------
{
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() );
}
}
- //----------------------------------------------------------------------------
- void Image::colorFillChanged(const osg::Vec4& color)
- {
- for( int i = 0; i < 4; ++i )
- (*_colors)[i] = color;
- _colors->dirty();
- }
-
//----------------------------------------------------------------------------
void Image::setupDefaultDimensions()
{
* 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);
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
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;
// 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,
#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);
+ }
}
//----------------------------------------------------------------------------
_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 );
//----------------------------------------------------------------------------
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 )
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;
_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,
}
}
+ //----------------------------------------------------------------------------
+ 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
#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
{
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;
/**
enum Attributes
{
- COLOR = 0x0001,
- COLOR_FILL = 0x0002,
- BOUNDING_BOX = 0x0004,
+ BOUNDING_BOX = 0x0001,
LAST_ATTRIBUTE = BOUNDING_BOX
};
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);
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
};
{
//----------------------------------------------------------------------------
- Group::Group(SGPropertyNode_ptr node):
- Element(node)
+ Group::Group(SGPropertyNode_ptr node, const Style& parent_style):
+ Element(node, parent_style)
{
}
//----------------------------------------------------------------------------
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;
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
void Group::childRemoved(SGPropertyNode* node)
{
+ if( node->getParent() != _node )
+ return;
+
if( node->getNameString() == "text"
|| node->getNameString() == "group"
|| node->getNameString() == "map"
_children.erase(child);
}
}
+ else
+ {
+ Style::iterator style = _style.find(node->getNameString());
+ if( style != _style.end() )
+ _style.erase(style);
+ }
}
} // namespace canvas
#include "element.hxx"
#include <boost/shared_ptr.hpp>
#include <list>
+#include <map>
namespace canvas
{
>
> ChildList;
- Group(SGPropertyNode_ptr node);
+ Group(SGPropertyNode_ptr node, const Style& parent_style = Style());
virtual ~Group();
virtual void update(double dt);
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)
{
//----------------------------------------------------------------------------
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"),
public Group
{
public:
- Map(SGPropertyNode_ptr node);
+ Map(SGPropertyNode_ptr node, const Style& parent_style);
virtual ~Map();
virtual void update(double dt);
_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);
}
/**
- * 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);
}
/**
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);
_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 )
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;
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
*/
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();
}
//----------------------------------------------------------------------------
_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);
}
//----------------------------------------------------------------------------
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
public Element
{
public:
- Path(SGPropertyNode_ptr node);
+ Path(SGPropertyNode_ptr node, const Style& parent_style);
virtual ~Path();
virtual void update(double dt);
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
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)
{
}
//----------------------------------------------------------------------------
- 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();
}
//----------------------------------------------------------------------------
}
//----------------------------------------------------------------------------
- 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);
}
//----------------------------------------------------------------------------
}
}
#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);
}
//----------------------------------------------------------------------------
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);
// 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,
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>
//----------------------------------------------------------------------------
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);
//----------------------------------------------------------------------------
Window::~Window()
{
- BOOST_FOREACH(osg::Group* parent, getGroup()->getParents())
- {
- parent->removeChild(getGroup());
- }
+
}
//----------------------------------------------------------------------------