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)
This commit is contained in:
parent
29e267f18a
commit
ced478cf95
16 changed files with 437 additions and 290 deletions
|
@ -102,6 +102,10 @@ Canvas::Canvas(SGPropertyNode* node):
|
||||||
_root_group( new canvas::Group(node) ),
|
_root_group( new canvas::Group(node) ),
|
||||||
_render_always(false)
|
_render_always(false)
|
||||||
{
|
{
|
||||||
|
// Remove automatically created property listener as we forward them on our
|
||||||
|
// own
|
||||||
|
_root_group->removeListener();
|
||||||
|
|
||||||
_status = 0;
|
_status = 0;
|
||||||
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
|
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
|
||||||
|
|
||||||
|
@ -317,6 +321,7 @@ void Canvas::valueChanged(SGPropertyNode* node)
|
||||||
if( boost::starts_with(node->getNameString(), "status") )
|
if( boost::starts_with(node->getNameString(), "status") )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
bool handled = true;
|
||||||
if( node->getParent()->getParent() == _node )
|
if( node->getParent()->getParent() == _node )
|
||||||
{
|
{
|
||||||
if( !_color_background.empty()
|
if( !_color_background.empty()
|
||||||
|
@ -335,6 +340,8 @@ void Canvas::valueChanged(SGPropertyNode* node)
|
||||||
|
|
||||||
_dirty_placements.push_back(node->getParent());
|
_dirty_placements.push_back(node->getParent());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
handled = false;
|
||||||
}
|
}
|
||||||
else if( node->getParent() == _node )
|
else if( node->getParent() == _node )
|
||||||
{
|
{
|
||||||
|
@ -358,9 +365,14 @@ void Canvas::valueChanged(SGPropertyNode* node)
|
||||||
else if( node->getIndex() == 1 )
|
else if( node->getIndex() == 1 )
|
||||||
setViewHeight( node->getIntValue() );
|
setViewHeight( node->getIntValue() );
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
handled = false;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
handled = false;
|
||||||
|
|
||||||
_root_group->valueChanged(node);
|
if( !handled )
|
||||||
|
_root_group->valueChanged(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
|
@ -71,8 +71,8 @@ bool CullCallback::cull( osg::NodeVisitor* nv,
|
||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Image::Image(SGPropertyNode_ptr node):
|
Image::Image(SGPropertyNode_ptr node, const Style& parent_style):
|
||||||
Element(node, COLOR_FILL | BOUNDING_BOX),
|
Element(node, parent_style, BOUNDING_BOX),
|
||||||
_texture(new osg::Texture2D),
|
_texture(new osg::Texture2D),
|
||||||
_node_src_rect( node->getNode("source", 0, true) )
|
_node_src_rect( node->getNode("source", 0, true) )
|
||||||
{
|
{
|
||||||
|
@ -103,6 +103,11 @@ namespace canvas
|
||||||
_geom->addPrimitiveSet(prim);
|
_geom->addPrimitiveSet(prim);
|
||||||
|
|
||||||
setDrawable(_geom);
|
setDrawable(_geom);
|
||||||
|
|
||||||
|
addStyle("fill", &Image::setFill, this);
|
||||||
|
setFill("#ffffff"); // TODO how should we handle default values?
|
||||||
|
|
||||||
|
setupStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
@ -189,37 +194,44 @@ namespace canvas
|
||||||
setupDefaultDimensions();
|
setupDefaultDimensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Image::setFill(const std::string& fill)
|
||||||
|
{
|
||||||
|
osg::Vec4 color = parseColor(fill);
|
||||||
|
for( int i = 0; i < 4; ++i )
|
||||||
|
(*_colors)[i] = color;
|
||||||
|
_colors->dirty();
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
const Rect<float>& Image::getRegion() const
|
const Rect<float>& Image::getRegion() const
|
||||||
{
|
{
|
||||||
return _region;
|
return _region;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
void Image::valueChanged(SGPropertyNode *node)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Image::childChanged(SGPropertyNode* child)
|
void Image::childChanged(SGPropertyNode* child)
|
||||||
{
|
{
|
||||||
const std::string& name = child->getNameString();
|
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" )
|
if( name == "x" )
|
||||||
{
|
{
|
||||||
_region.setX( child->getFloatValue() );
|
_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()
|
void Image::setupDefaultDimensions()
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,8 +39,8 @@ namespace canvas
|
||||||
* size[0-1] Dimensions of rectangle
|
* size[0-1] Dimensions of rectangle
|
||||||
* [x,y] Position of rectangle
|
* [x,y] Position of rectangle
|
||||||
*/
|
*/
|
||||||
Image(SGPropertyNode_ptr node);
|
Image(SGPropertyNode_ptr node, const Style& parent_style);
|
||||||
~Image();
|
virtual ~Image();
|
||||||
|
|
||||||
virtual void update(double dt);
|
virtual void update(double dt);
|
||||||
|
|
||||||
|
@ -48,14 +48,10 @@ namespace canvas
|
||||||
CanvasWeakPtr getCanvas() const;
|
CanvasWeakPtr getCanvas() const;
|
||||||
|
|
||||||
void setImage(osg::Image *img);
|
void setImage(osg::Image *img);
|
||||||
|
void setFill(const std::string& fill);
|
||||||
|
|
||||||
const Rect<float>& getRegion() const;
|
const Rect<float>& getRegion() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback for every changed child node
|
|
||||||
*/
|
|
||||||
virtual void valueChanged(SGPropertyNode *node);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
enum ImageAttributes
|
enum ImageAttributes
|
||||||
|
@ -64,13 +60,7 @@ namespace canvas
|
||||||
DEST_SIZE = SRC_RECT << 1 // Element size
|
DEST_SIZE = SRC_RECT << 1 // Element size
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback for changed direct child nodes
|
|
||||||
*/
|
|
||||||
virtual void childChanged(SGPropertyNode * child);
|
virtual void childChanged(SGPropertyNode * child);
|
||||||
virtual void colorFillChanged(const osg::Vec4& color);
|
|
||||||
|
|
||||||
void handleHit(float x, float y);
|
|
||||||
|
|
||||||
void setupDefaultDimensions();
|
void setupDefaultDimensions();
|
||||||
Rect<int> getTextureDimensions() const;
|
Rect<int> getTextureDimensions() const;
|
||||||
|
@ -79,10 +69,10 @@ namespace canvas
|
||||||
// TODO optionally forward events to canvas
|
// TODO optionally forward events to canvas
|
||||||
CanvasWeakPtr _canvas;
|
CanvasWeakPtr _canvas;
|
||||||
|
|
||||||
osg::Geometry *_geom;
|
osg::ref_ptr<osg::Geometry> _geom;
|
||||||
osg::Vec3Array *_vertices;
|
osg::ref_ptr<osg::Vec3Array> _vertices;
|
||||||
osg::Vec2Array *_texCoords;
|
osg::ref_ptr<osg::Vec2Array> _texCoords;
|
||||||
osg::Vec4Array* _colors;
|
osg::ref_ptr<osg::Vec4Array> _colors;
|
||||||
|
|
||||||
SGPropertyNode *_node_src_rect;
|
SGPropertyNode *_node_src_rect;
|
||||||
Rect<float> _src_rect,
|
Rect<float> _src_rect,
|
||||||
|
|
|
@ -23,19 +23,30 @@
|
||||||
#include <osg/Drawable>
|
#include <osg/Drawable>
|
||||||
#include <osg/Geode>
|
#include <osg/Geode>
|
||||||
|
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
const std::string NAME_TRANSFORM = "tf";
|
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()
|
Element::~Element()
|
||||||
{
|
{
|
||||||
|
removeListener();
|
||||||
|
|
||||||
|
BOOST_FOREACH(osg::Group* parent, _transform->getParents())
|
||||||
|
{
|
||||||
|
parent->removeChild(_transform);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
@ -95,24 +106,6 @@ namespace canvas
|
||||||
_transform_dirty = false;
|
_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() )
|
if( !_bounding_box.empty() )
|
||||||
{
|
{
|
||||||
assert( _drawable );
|
assert( _drawable );
|
||||||
|
@ -159,18 +152,15 @@ namespace canvas
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
|
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_types[ child->getIndex() ] = TT_NONE;
|
||||||
_transform_dirty = true;
|
_transform_dirty = true;
|
||||||
}
|
return;
|
||||||
else
|
|
||||||
childAdded(child);
|
|
||||||
}
|
}
|
||||||
else if( parent->getParent() == _node
|
else if( parent->getParent() == _node
|
||||||
&& parent->getNameString() == NAME_TRANSFORM )
|
&& parent->getNameString() == NAME_TRANSFORM )
|
||||||
|
@ -191,16 +181,16 @@ namespace canvas
|
||||||
type = TT_SCALE;
|
type = TT_SCALE;
|
||||||
|
|
||||||
_transform_dirty = true;
|
_transform_dirty = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
childAdded(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
|
void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
|
||||||
{
|
{
|
||||||
if( parent != _node )
|
if( parent == _node && child->getNameString() == NAME_TRANSFORM )
|
||||||
return;
|
|
||||||
|
|
||||||
if( child->getNameString() == NAME_TRANSFORM )
|
|
||||||
{
|
{
|
||||||
assert(child->getIndex() < static_cast<int>(_transform_types.size()));
|
assert(child->getIndex() < static_cast<int>(_transform_types.size()));
|
||||||
_transform_types[ child->getIndex() ] = TT_NONE;
|
_transform_types[ child->getIndex() ] = TT_NONE;
|
||||||
|
@ -209,54 +199,51 @@ namespace canvas
|
||||||
_transform_types.pop_back();
|
_transform_types.pop_back();
|
||||||
|
|
||||||
_transform_dirty = true;
|
_transform_dirty = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
childRemoved(child);
|
childRemoved(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Element::valueChanged(SGPropertyNode* child)
|
void Element::valueChanged(SGPropertyNode* child)
|
||||||
{
|
{
|
||||||
SGPropertyNode *parent = child->getParent();
|
SGPropertyNode *parent = child->getParent();
|
||||||
if( parent->getParent() == _node )
|
if( parent == _node )
|
||||||
{
|
{
|
||||||
if( parent->getNameString() == NAME_TRANSFORM )
|
if( setStyle(child) )
|
||||||
_transform_dirty = true;
|
return;
|
||||||
else if( !_color.empty() && _color[0]->getParent() == parent )
|
else if( child->getNameString() == "update" )
|
||||||
_attributes_dirty |= COLOR;
|
return update(0);
|
||||||
else if( !_color_fill.empty() && _color_fill[0]->getParent() == parent )
|
|
||||||
_attributes_dirty |= COLOR_FILL;
|
|
||||||
}
|
|
||||||
else if( parent == _node )
|
|
||||||
{
|
|
||||||
if( child->getNameString() == "update" )
|
|
||||||
update(0);
|
|
||||||
else if( child->getNameString() == "visible" )
|
else if( child->getNameString() == "visible" )
|
||||||
// TODO check if we need another nodemask
|
// TODO check if we need another nodemask
|
||||||
_transform->setNodeMask( child->getBoolValue() ? 0xffffffff : 0 );
|
return _transform->setNodeMask( child->getBoolValue() ? 0xffffffff : 0 );
|
||||||
else
|
|
||||||
childChanged(child);
|
|
||||||
}
|
}
|
||||||
|
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_used( attributes_used ),
|
||||||
_attributes_dirty( attributes_used ),
|
_attributes_dirty( attributes_used ),
|
||||||
_transform_dirty( false ),
|
_transform_dirty( false ),
|
||||||
_transform( new osg::MatrixTransform ),
|
_transform( new osg::MatrixTransform ),
|
||||||
_node( node ),
|
_node( node ),
|
||||||
|
_style( parent_style ),
|
||||||
_drawable( 0 )
|
_drawable( 0 )
|
||||||
{
|
{
|
||||||
assert( _node );
|
assert( _node );
|
||||||
_node->addChangeListener(this);
|
_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_LOG
|
||||||
(
|
(
|
||||||
SG_GL,
|
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
|
} // namespace canvas
|
||||||
|
|
|
@ -20,8 +20,11 @@
|
||||||
#define CANVAS_ELEMENT_HXX_
|
#define CANVAS_ELEMENT_HXX_
|
||||||
|
|
||||||
#include <simgear/props/props.hxx>
|
#include <simgear/props/props.hxx>
|
||||||
#include <osg/MatrixTransform>
|
|
||||||
#include <simgear/misc/stdint.hxx> // for uint32_t
|
#include <simgear/misc/stdint.hxx> // for uint32_t
|
||||||
|
#include <osg/MatrixTransform>
|
||||||
|
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
{
|
{
|
||||||
|
@ -36,6 +39,11 @@ namespace canvas
|
||||||
public SGPropertyChangeListener
|
public SGPropertyChangeListener
|
||||||
{
|
{
|
||||||
public:
|
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;
|
virtual ~Element() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,9 +67,7 @@ namespace canvas
|
||||||
|
|
||||||
enum Attributes
|
enum Attributes
|
||||||
{
|
{
|
||||||
COLOR = 0x0001,
|
BOUNDING_BOX = 0x0001,
|
||||||
COLOR_FILL = 0x0002,
|
|
||||||
BOUNDING_BOX = 0x0004,
|
|
||||||
LAST_ATTRIBUTE = BOUNDING_BOX
|
LAST_ATTRIBUTE = BOUNDING_BOX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -82,11 +88,48 @@ namespace canvas
|
||||||
std::vector<TransformType> _transform_types;
|
std::vector<TransformType> _transform_types;
|
||||||
|
|
||||||
SGPropertyNode_ptr _node;
|
SGPropertyNode_ptr _node;
|
||||||
std::vector<SGPropertyNode_ptr> _color,
|
Style _style;
|
||||||
_color_fill;
|
StyleSetters _style_setters;
|
||||||
std::vector<SGPropertyNode_ptr> _bounding_box;
|
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 bool handleLocalMouseEvent(const canvas::MouseEvent& event);
|
||||||
|
|
||||||
|
@ -94,14 +137,14 @@ namespace canvas
|
||||||
virtual void childRemoved(SGPropertyNode * child){}
|
virtual void childRemoved(SGPropertyNode * child){}
|
||||||
virtual void childChanged(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 setDrawable( osg::Drawable* drawable );
|
||||||
|
void setupStyle();
|
||||||
|
|
||||||
|
bool setStyle(const SGPropertyNode* child);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
osg::Drawable *_drawable;
|
osg::ref_ptr<osg::Drawable> _drawable;
|
||||||
|
|
||||||
Element(const Element&);// = delete
|
Element(const Element&);// = delete
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,8 +28,8 @@ namespace canvas
|
||||||
{
|
{
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Group::Group(SGPropertyNode_ptr node):
|
Group::Group(SGPropertyNode_ptr node, const Style& parent_style):
|
||||||
Element(node)
|
Element(node, parent_style)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -64,27 +64,33 @@ namespace canvas
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Group::childAdded(SGPropertyNode* child)
|
void Group::childAdded(SGPropertyNode* child)
|
||||||
{
|
{
|
||||||
|
if( child->getParent() != _node )
|
||||||
|
return;
|
||||||
|
|
||||||
boost::shared_ptr<Element> element;
|
boost::shared_ptr<Element> element;
|
||||||
|
|
||||||
// TODO create map of child factories and use also to check for element
|
// TODO create map of child factories and use also to check for element
|
||||||
// on deletion in ::childRemoved
|
// on deletion in ::childRemoved
|
||||||
if( child->getNameString() == "text" )
|
if( child->getNameString() == "text" )
|
||||||
element.reset( new Text(child) );
|
element.reset( new Text(child, _style) );
|
||||||
else if( child->getNameString() == "group" )
|
else if( child->getNameString() == "group" )
|
||||||
element.reset( new Group(child) );
|
element.reset( new Group(child, _style) );
|
||||||
else if( child->getNameString() == "map" )
|
else if( child->getNameString() == "map" )
|
||||||
element.reset( new Map(child) );
|
element.reset( new Map(child, _style) );
|
||||||
else if( child->getNameString() == "path" )
|
else if( child->getNameString() == "path" )
|
||||||
element.reset( new Path(child) );
|
element.reset( new Path(child, _style) );
|
||||||
else if( child->getNameString() == "image" )
|
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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Add to osg scene graph...
|
_style[ child->getNameString() ] = child;
|
||||||
_transform->addChild( element->getMatrixTransform() );
|
|
||||||
_children.push_back( ChildList::value_type(child, element) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
@ -107,6 +113,9 @@ namespace canvas
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Group::childRemoved(SGPropertyNode* node)
|
void Group::childRemoved(SGPropertyNode* node)
|
||||||
{
|
{
|
||||||
|
if( node->getParent() != _node )
|
||||||
|
return;
|
||||||
|
|
||||||
if( node->getNameString() == "text"
|
if( node->getNameString() == "text"
|
||||||
|| node->getNameString() == "group"
|
|| node->getNameString() == "group"
|
||||||
|| node->getNameString() == "map"
|
|| node->getNameString() == "map"
|
||||||
|
@ -130,6 +139,12 @@ namespace canvas
|
||||||
_children.erase(child);
|
_children.erase(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Style::iterator style = _style.find(node->getNameString());
|
||||||
|
if( style != _style.end() )
|
||||||
|
_style.erase(style);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "element.hxx"
|
#include "element.hxx"
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
|
@ -37,7 +38,7 @@ namespace canvas
|
||||||
>
|
>
|
||||||
> ChildList;
|
> ChildList;
|
||||||
|
|
||||||
Group(SGPropertyNode_ptr node);
|
Group(SGPropertyNode_ptr node, const Style& parent_style = Style());
|
||||||
virtual ~Group();
|
virtual ~Group();
|
||||||
|
|
||||||
virtual void update(double dt);
|
virtual void update(double dt);
|
||||||
|
|
|
@ -46,8 +46,8 @@ namespace canvas
|
||||||
const std::string GEO = "-geo";
|
const std::string GEO = "-geo";
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Map::Map(SGPropertyNode_ptr node):
|
Map::Map(SGPropertyNode_ptr node, const Style& parent_style):
|
||||||
Group(node),
|
Group(node, parent_style),
|
||||||
_projection_dirty(true)
|
_projection_dirty(true)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -177,6 +177,9 @@ namespace canvas
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Map::childChanged(SGPropertyNode * child)
|
void Map::childChanged(SGPropertyNode * child)
|
||||||
{
|
{
|
||||||
|
if( child->getParent() != _node )
|
||||||
|
return;
|
||||||
|
|
||||||
if( child->getNameString() == "ref-lat"
|
if( child->getNameString() == "ref-lat"
|
||||||
|| child->getNameString() == "ref-lon" )
|
|| child->getNameString() == "ref-lon" )
|
||||||
projection.setWorldPosition( _node->getDoubleValue("ref-lat"),
|
projection.setWorldPosition( _node->getDoubleValue("ref-lat"),
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace canvas
|
||||||
public Group
|
public Group
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Map(SGPropertyNode_ptr node);
|
Map(SGPropertyNode_ptr node, const Style& parent_style);
|
||||||
virtual ~Map();
|
virtual ~Map();
|
||||||
|
|
||||||
virtual void update(double dt);
|
virtual void update(double dt);
|
||||||
|
|
|
@ -40,9 +40,9 @@ namespace canvas
|
||||||
_paint(VG_INVALID_HANDLE),
|
_paint(VG_INVALID_HANDLE),
|
||||||
_paint_fill(VG_INVALID_HANDLE),
|
_paint_fill(VG_INVALID_HANDLE),
|
||||||
_attributes_dirty(~0),
|
_attributes_dirty(~0),
|
||||||
|
_mode(0),
|
||||||
_stroke_width(1),
|
_stroke_width(1),
|
||||||
_stroke_linecap(VG_CAP_BUTT),
|
_stroke_linecap(VG_CAP_BUTT)
|
||||||
_fill(false)
|
|
||||||
{
|
{
|
||||||
setSupportsDisplayList(false);
|
setSupportsDisplayList(false);
|
||||||
setDataVariance(Object::DYNAMIC);
|
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,
|
void setFill(const std::string& fill)
|
||||||
const std::vector<float> dash = std::vector<float>() )
|
{
|
||||||
|
if( fill == "none" )
|
||||||
|
{
|
||||||
|
_mode &= ~VG_FILL_PATH;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_fill_color = parseColor(fill);
|
||||||
|
_mode |= VG_FILL_PATH;
|
||||||
|
_attributes_dirty |= FILL_COLOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set path stroke paint ("none" if no stroke)
|
||||||
|
*/
|
||||||
|
void setStroke(const std::string& stroke)
|
||||||
|
{
|
||||||
|
if( stroke == "none" )
|
||||||
|
{
|
||||||
|
_mode &= ~VG_STROKE_PATH;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_stroke_color = parseColor(stroke);
|
||||||
|
_mode |= VG_STROKE_PATH;
|
||||||
|
_attributes_dirty |= STROKE_COLOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set stroke width
|
||||||
|
*/
|
||||||
|
void setStrokeWidth(float width)
|
||||||
{
|
{
|
||||||
_stroke_width = width;
|
_stroke_width = width;
|
||||||
_stroke_dash = dash;
|
|
||||||
|
|
||||||
_attributes_dirty |= BOUNDING_BOX;
|
_attributes_dirty |= BOUNDING_BOX;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the line color
|
* Set stroke dash (line stipple)
|
||||||
*/
|
*/
|
||||||
void setColor(const osg::Vec4& color)
|
void setStrokeDashArray(const std::string& dash)
|
||||||
{
|
{
|
||||||
for( size_t i = 0; i < 4; ++i )
|
_stroke_dash = splitAndConvert(",\t\n ", dash);
|
||||||
_stroke_color[i] = color[i];
|
|
||||||
_attributes_dirty |= STROKE_COLOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable/Disable filling of the path
|
|
||||||
*/
|
|
||||||
void enableFill(bool enable)
|
|
||||||
{
|
|
||||||
_fill = enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the line color
|
|
||||||
*/
|
|
||||||
void setColorFill(const osg::Vec4& color)
|
|
||||||
{
|
|
||||||
for( size_t i = 0; i < 4; ++i )
|
|
||||||
_fill_color[i] = color[i];
|
|
||||||
_attributes_dirty |= FILL_COLOR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -166,28 +177,25 @@ namespace canvas
|
||||||
if( _paint == VG_INVALID_HANDLE )
|
if( _paint == VG_INVALID_HANDLE )
|
||||||
_paint = vgCreatePaint();
|
_paint = vgCreatePaint();
|
||||||
|
|
||||||
vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _stroke_color);
|
vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _stroke_color._v);
|
||||||
|
|
||||||
_attributes_dirty &= ~STROKE_COLOR;
|
_attributes_dirty &= ~STROKE_COLOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize/update fill paint
|
// Initialize/update fill paint
|
||||||
if( _attributes_dirty & (FILL_COLOR | FILL) )
|
if( _attributes_dirty & FILL_COLOR )
|
||||||
{
|
{
|
||||||
if( _paint_fill == VG_INVALID_HANDLE )
|
if( _paint_fill == VG_INVALID_HANDLE )
|
||||||
_paint_fill = vgCreatePaint();
|
_paint_fill = vgCreatePaint();
|
||||||
|
|
||||||
if( _attributes_dirty & FILL_COLOR )
|
vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, _fill_color._v);
|
||||||
vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, _fill_color);
|
|
||||||
|
|
||||||
_attributes_dirty &= ~(FILL_COLOR | FILL);
|
_attributes_dirty &= ~FILL_COLOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect draw mode
|
// Setup paint
|
||||||
VGbitfield mode = 0;
|
if( _mode & VG_STROKE_PATH )
|
||||||
if( _stroke_width > 0 )
|
|
||||||
{
|
{
|
||||||
mode |= VG_STROKE_PATH;
|
|
||||||
vgSetPaint(_paint, VG_STROKE_PATH);
|
vgSetPaint(_paint, VG_STROKE_PATH);
|
||||||
|
|
||||||
vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width);
|
vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width);
|
||||||
|
@ -196,15 +204,14 @@ namespace canvas
|
||||||
_stroke_dash.size(),
|
_stroke_dash.size(),
|
||||||
_stroke_dash.empty() ? 0 : &_stroke_dash[0] );
|
_stroke_dash.empty() ? 0 : &_stroke_dash[0] );
|
||||||
}
|
}
|
||||||
if( _fill )
|
if( _mode & VG_FILL_PATH )
|
||||||
{
|
{
|
||||||
mode |= VG_FILL_PATH;
|
|
||||||
vgSetPaint(_paint_fill, VG_FILL_PATH);
|
vgSetPaint(_paint_fill, VG_FILL_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
// And finally draw the path
|
// And finally draw the path
|
||||||
if( mode )
|
if( _mode )
|
||||||
vgDrawPath(_path, mode);
|
vgDrawPath(_path, _mode);
|
||||||
|
|
||||||
VGErrorCode err = vgGetError();
|
VGErrorCode err = vgGetError();
|
||||||
if( err != VG_NO_ERROR )
|
if( err != VG_NO_ERROR )
|
||||||
|
@ -246,8 +253,7 @@ namespace canvas
|
||||||
PATH = 0x0001,
|
PATH = 0x0001,
|
||||||
STROKE_COLOR = PATH << 1,
|
STROKE_COLOR = PATH << 1,
|
||||||
FILL_COLOR = STROKE_COLOR << 1,
|
FILL_COLOR = STROKE_COLOR << 1,
|
||||||
FILL = FILL_COLOR << 1,
|
BOUNDING_BOX = FILL_COLOR << 1
|
||||||
BOUNDING_BOX = FILL << 1
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mutable VGPath _path;
|
mutable VGPath _path;
|
||||||
|
@ -258,14 +264,13 @@ namespace canvas
|
||||||
CmdList _cmds;
|
CmdList _cmds;
|
||||||
CoordList _coords;
|
CoordList _coords;
|
||||||
|
|
||||||
VGfloat _stroke_color[4];
|
VGbitfield _mode;
|
||||||
|
osg::Vec4f _fill_color;
|
||||||
|
osg::Vec4f _stroke_color;
|
||||||
VGfloat _stroke_width;
|
VGfloat _stroke_width;
|
||||||
std::vector<VGfloat> _stroke_dash;
|
std::vector<VGfloat> _stroke_dash;
|
||||||
VGCapStyle _stroke_linecap;
|
VGCapStyle _stroke_linecap;
|
||||||
|
|
||||||
bool _fill;
|
|
||||||
VGfloat _fill_color[4];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize/Update the OpenVG path
|
* Initialize/Update the OpenVG path
|
||||||
*/
|
*/
|
||||||
|
@ -339,11 +344,20 @@ namespace canvas
|
||||||
bool PathDrawable::_vg_initialized = false;
|
bool PathDrawable::_vg_initialized = false;
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Path::Path(SGPropertyNode_ptr node):
|
Path::Path(SGPropertyNode_ptr node, const Style& parent_style):
|
||||||
Element(node, COLOR | COLOR_FILL | BOUNDING_BOX),
|
Element(node, parent_style, BOUNDING_BOX),
|
||||||
_path( new PathDrawable() )
|
_path( new PathDrawable() )
|
||||||
{
|
{
|
||||||
setDrawable(_path);
|
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);
|
_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);
|
Element::update(dt);
|
||||||
}
|
}
|
||||||
|
@ -382,29 +386,13 @@ namespace canvas
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Path::childChanged(SGPropertyNode* child)
|
void Path::childChanged(SGPropertyNode* child)
|
||||||
{
|
{
|
||||||
|
if( child->getParent() != _node )
|
||||||
|
return;
|
||||||
|
|
||||||
if( child->getNameString() == "cmd" )
|
if( child->getNameString() == "cmd" )
|
||||||
_attributes_dirty |= CMDS;
|
_attributes_dirty |= CMDS;
|
||||||
else if( child->getNameString() == "coord" )
|
else if( child->getNameString() == "coord" )
|
||||||
_attributes_dirty |= COORDS;
|
_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
|
} // namespace canvas
|
||||||
|
|
|
@ -28,7 +28,7 @@ namespace canvas
|
||||||
public Element
|
public Element
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Path(SGPropertyNode_ptr node);
|
Path(SGPropertyNode_ptr node, const Style& parent_style);
|
||||||
virtual ~Path();
|
virtual ~Path();
|
||||||
|
|
||||||
virtual void update(double dt);
|
virtual void update(double dt);
|
||||||
|
@ -38,15 +38,12 @@ namespace canvas
|
||||||
enum PathAttributes
|
enum PathAttributes
|
||||||
{
|
{
|
||||||
CMDS = LAST_ATTRIBUTE << 1,
|
CMDS = LAST_ATTRIBUTE << 1,
|
||||||
COORDS = CMDS << 1,
|
COORDS = CMDS << 1
|
||||||
STROKE = COORDS << 1
|
|
||||||
};
|
};
|
||||||
|
|
||||||
osg::ref_ptr<PathDrawable> _path;
|
osg::ref_ptr<PathDrawable> _path;
|
||||||
|
|
||||||
virtual void childChanged(SGPropertyNode * child);
|
virtual void childChanged(SGPropertyNode * child);
|
||||||
virtual void colorChanged(const osg::Vec4& color);
|
|
||||||
virtual void colorFillChanged(const osg::Vec4& color);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
|
|
|
@ -30,11 +30,37 @@ namespace canvas
|
||||||
public osgText::Text
|
public osgText::Text
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
void setCharacterAspect(float aspect);
|
||||||
|
void setFill(const std::string& fill);
|
||||||
|
void setBackgroundColor(const std::string& fill);
|
||||||
|
|
||||||
osg::Vec2 handleHit(float x, float y);
|
osg::Vec2 handleHit(float x, float y);
|
||||||
|
|
||||||
virtual osg::BoundingBox computeBound() const;
|
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)
|
osg::Vec2 Text::TextOSG::handleHit(float x, float y)
|
||||||
{
|
{
|
||||||
|
@ -130,19 +156,33 @@ namespace canvas
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Text::Text(SGPropertyNode_ptr node):
|
Text::Text(SGPropertyNode_ptr node, const Style& parent_style):
|
||||||
Element(node, COLOR | COLOR_FILL | BOUNDING_BOX),
|
Element(node, parent_style, BOUNDING_BOX),
|
||||||
_text( new Text::TextOSG() ),
|
_text( new Text::TextOSG() )
|
||||||
_font_size( 0 ),
|
|
||||||
_font_aspect( 0 )
|
|
||||||
{
|
{
|
||||||
setDrawable(_text);
|
setDrawable(_text);
|
||||||
_text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
|
_text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
|
||||||
_text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
|
_text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
|
||||||
_text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
|
_text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
|
||||||
|
|
||||||
_font_size = getChildDefault<float>(_node, "character-size", 32);
|
addStyle("fill", &TextOSG::setFill, _text);
|
||||||
_font_aspect = getChildDefault<float>(_node, "character-aspect-ratio", 1);
|
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);
|
_text->setText(text, osgText::String::ENCODING_UTF8);
|
||||||
|
|
||||||
if( _attributes_dirty & FONT_SIZE )
|
|
||||||
{
|
|
||||||
_text->setCharacterSize
|
|
||||||
(
|
|
||||||
_font_size->getFloatValue(),
|
|
||||||
_font_aspect->getFloatValue()
|
|
||||||
);
|
|
||||||
|
|
||||||
_attributes_dirty &= ~FONT_SIZE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
@ -213,51 +242,20 @@ namespace canvas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Text::childChanged(SGPropertyNode* child)
|
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" )
|
if( name == "hit-y" )
|
||||||
handleHit
|
handleHit
|
||||||
(
|
(
|
||||||
_node->getFloatValue("hit-x"),
|
_node->getFloatValue("hit-x"),
|
||||||
_node->getFloatValue("hit-y")
|
_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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
|
@ -32,30 +32,19 @@ namespace canvas
|
||||||
public Element
|
public Element
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Text(SGPropertyNode_ptr node);
|
Text(SGPropertyNode_ptr node, const Style& parent_style);
|
||||||
~Text();
|
~Text();
|
||||||
|
|
||||||
virtual void update(double dt);
|
void setText(const char* text);
|
||||||
|
|
||||||
void setFont(const char* name);
|
void setFont(const char* name);
|
||||||
void setAlignment(const char* align);
|
void setAlignment(const char* align);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
enum TextAttributes
|
|
||||||
{
|
|
||||||
FONT_SIZE = LAST_ATTRIBUTE << 1, // Font size and aspect ration
|
|
||||||
};
|
|
||||||
|
|
||||||
class TextOSG;
|
class TextOSG;
|
||||||
osg::ref_ptr<TextOSG> _text;
|
osg::ref_ptr<TextOSG> _text;
|
||||||
|
|
||||||
SGPropertyNode_ptr _font_size,
|
|
||||||
_font_aspect;
|
|
||||||
|
|
||||||
virtual void childChanged(SGPropertyNode * child);
|
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);
|
void handleHit(float x, float y);
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,98 @@
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
#include "property_helper.hxx"
|
#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>
|
#include <cassert>
|
||||||
|
|
||||||
namespace canvas
|
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,
|
void linkColorNodes( const char* name,
|
||||||
SGPropertyNode* parent,
|
SGPropertyNode* parent,
|
||||||
|
|
|
@ -60,12 +60,26 @@ namespace canvas
|
||||||
return values;
|
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 name Name of color node
|
||||||
* @param parent Parent for color channel nodes
|
* @param parent Parent for color channel nodes
|
||||||
* @param nodes Vector to push color nodes into
|
* @param nodes Vector to push color nodes into
|
||||||
* @param def Default color
|
* @param def Default color
|
||||||
*
|
*
|
||||||
|
* @deprecated Use only a single property instead and parse with #parseColor
|
||||||
|
*
|
||||||
* <name>
|
* <name>
|
||||||
* <red type="float">def[0] or existing value</red>
|
* <red type="float">def[0] or existing value</red>
|
||||||
* <green type="float">def[1] or existing value</green>
|
* <green type="float">def[1] or existing value</green>
|
||||||
|
|
|
@ -28,8 +28,10 @@ namespace canvas
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Window::Window(SGPropertyNode* node):
|
Window::Window(SGPropertyNode* node):
|
||||||
PropertyBasedElement(node),
|
PropertyBasedElement(node),
|
||||||
_image(node)
|
_image(node, Element::Style())
|
||||||
{
|
{
|
||||||
|
_image.removeListener();
|
||||||
|
|
||||||
// TODO probably better remove default position and size
|
// TODO probably better remove default position and size
|
||||||
node->setFloatValue("x", 50);
|
node->setFloatValue("x", 50);
|
||||||
node->setFloatValue("y", 100);
|
node->setFloatValue("y", 100);
|
||||||
|
@ -44,10 +46,7 @@ namespace canvas
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Window::~Window()
|
Window::~Window()
|
||||||
{
|
{
|
||||||
BOOST_FOREACH(osg::Group* parent, getGroup()->getParents())
|
|
||||||
{
|
|
||||||
parent->removeChild(getGroup());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
Loading…
Add table
Reference in a new issue