From: Thomas Geymayer Date: Wed, 28 May 2014 22:29:12 +0000 (+0200) Subject: PropertyBasedElement: extend HTML5 (Canvas) data props interface X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=6db07373eed04789e19a531b63cc50a7a4e69a7e;p=simgear.git PropertyBasedElement: extend HTML5 (Canvas) data props interface Allow check if property exists and removing properties. --- diff --git a/simgear/canvas/elements/CanvasElement.cxx b/simgear/canvas/elements/CanvasElement.cxx index 1c506fbc..61239d4d 100644 --- a/simgear/canvas/elements/CanvasElement.cxx +++ b/simgear/canvas/elements/CanvasElement.cxx @@ -635,74 +635,6 @@ namespace canvas return m; } - //---------------------------------------------------------------------------- - static const std::string DATA_PREFIX("data-"); - - //---------------------------------------------------------------------------- - std::string Element::dataPropToAttrName(const std::string& name) - { - // http://www.w3.org/TR/html5/dom.html#attr-data-* - // - // 3. Insert the string data- at the front of name. - - std::string attr_name; - for( std::string::const_iterator cur = name.begin(); - cur != name.end(); - ++cur ) - { - // If name contains a "-" (U+002D) character followed by a lowercase ASCII - // letter, throw a SyntaxError exception and abort these steps. - if( *cur == '-' ) - { - std::string::const_iterator next = cur + 1; - if( next != name.end() && islower(*next) ) - return std::string(); - } - - // For each uppercase ASCII letter in name, insert a "-" (U+002D) - // character before the character and replace the character with the same - // character converted to ASCII lowercase. - if( isupper(*cur) ) - { - attr_name.push_back('-'); - attr_name.push_back( tolower(*cur) ); - } - else - attr_name.push_back( *cur ); - } - return DATA_PREFIX + attr_name; - } - - //---------------------------------------------------------------------------- - std::string Element::attrToDataPropName(const std::string& name) - { - // http://www.w3.org/TR/html5/dom.html#attr-data-* - // - // For each "-" (U+002D) character in the name that is followed by a - // lowercase ASCII letter, remove the "-" (U+002D) character and replace the - // character that followed it by the same character converted to ASCII - // uppercase. - - if( !boost::starts_with(name, DATA_PREFIX) ) - return std::string(); - - std::string data_name; - for( std::string::const_iterator cur = name.begin() + DATA_PREFIX.length(); - cur != name.end(); - ++cur ) - { - std::string::const_iterator next = cur + 1; - if( *cur == '-' && next != name.end() && islower(*next) ) - { - data_name.push_back( toupper(*next) ); - cur = next; - } - else - data_name.push_back(*cur); - } - return data_name; - } - //---------------------------------------------------------------------------- Element::StyleSetters Element::_style_setters; diff --git a/simgear/canvas/elements/CanvasElement.hxx b/simgear/canvas/elements/CanvasElement.hxx index 73a18472..87548c68 100644 --- a/simgear/canvas/elements/CanvasElement.hxx +++ b/simgear/canvas/elements/CanvasElement.hxx @@ -107,30 +107,6 @@ namespace canvas bool addEventListener(const std::string& type, const EventListener& cb); virtual void clearEventListener(); - template - void setDataProp( const std::string& name, - const T& val ) - { - const std::string& attr = dataPropToAttrName(name); - if( !attr.empty() ) - set(attr, val); - else - SG_LOG(SG_GENERAL, SG_WARN, "Invalid data-prop name: " << name); - } - - template - T getDataProp( const std::string& name, - const T& def = T() ) - { - const std::string& attr = dataPropToAttrName(name); - if( !attr.empty() ) - return get(attr, def); - else - SG_LOG(SG_GENERAL, SG_WARN, "Invalid data-prop name: " << name); - - return def; - } - virtual bool accept(EventVisitor& visitor); virtual bool ascend(EventVisitor& visitor); virtual bool traverse(EventVisitor& visitor); @@ -228,9 +204,6 @@ namespace canvas return ElementPtr( new Derived(canvas, node, style, parent) ); } - static std::string dataPropToAttrName(const std::string& name); - static std::string attrToDataPropName(const std::string& name); - protected: enum Attributes diff --git a/simgear/canvas/elements/canvas_element_test.cpp b/simgear/canvas/elements/canvas_element_test.cpp index fe3c2ee7..444af4c1 100644 --- a/simgear/canvas/elements/canvas_element_test.cpp +++ b/simgear/canvas/elements/canvas_element_test.cpp @@ -51,4 +51,16 @@ BOOST_AUTO_TEST_CASE( attr_data ) el->setDataProp("myData", 3); BOOST_CHECK_EQUAL( el->getDataProp("myData"), 3 ); BOOST_CHECK_EQUAL( node->getIntValue("data-my-data"), 3 ); + + SGPropertyNode* prop = el->getDataProp("notExistingProp"); + BOOST_CHECK( !prop ); + prop = el->getDataProp("myData"); + BOOST_CHECK( prop ); + BOOST_CHECK_EQUAL( prop->getParent(), node ); + BOOST_CHECK_EQUAL( prop->getIntValue(), 3 ); + + BOOST_CHECK( el->hasDataProp("myData") ); + el->removeDataProp("myData"); + BOOST_CHECK( !el->hasDataProp("myData") ); + BOOST_CHECK_EQUAL( el->getDataProp("myData", 5), 5 ); } diff --git a/simgear/props/PropertyBasedElement.cxx b/simgear/props/PropertyBasedElement.cxx index 855829c7..348842b9 100644 --- a/simgear/props/PropertyBasedElement.cxx +++ b/simgear/props/PropertyBasedElement.cxx @@ -17,6 +17,7 @@ // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA #include "PropertyBasedElement.hxx" +#include namespace simgear { @@ -65,4 +66,86 @@ namespace simgear return _node; } + //---------------------------------------------------------------------------- + bool PropertyBasedElement::hasDataProp(const std::string& name) const + { + return getDataProp(name) != NULL; + } + + //---------------------------------------------------------------------------- + void PropertyBasedElement::removeDataProp(const std::string& name) + { + SGPropertyNode* node = getDataProp(name); + if( node ) + _node->removeChild(node); + } + + //---------------------------------------------------------------------------- + static const std::string DATA_PREFIX("data-"); + + //---------------------------------------------------------------------------- + std::string PropertyBasedElement::dataPropToAttrName(const std::string& name) + { + // http://www.w3.org/TR/html5/dom.html#attr-data-* + // + // 3. Insert the string data- at the front of name. + + std::string attr_name; + for( std::string::const_iterator cur = name.begin(); + cur != name.end(); + ++cur ) + { + // If name contains a "-" (U+002D) character followed by a lowercase ASCII + // letter, throw a SyntaxError exception and abort these steps. + if( *cur == '-' ) + { + std::string::const_iterator next = cur + 1; + if( next != name.end() && islower(*next) ) + return std::string(); + } + + // For each uppercase ASCII letter in name, insert a "-" (U+002D) + // character before the character and replace the character with the same + // character converted to ASCII lowercase. + if( isupper(*cur) ) + { + attr_name.push_back('-'); + attr_name.push_back( tolower(*cur) ); + } + else + attr_name.push_back( *cur ); + } + return DATA_PREFIX + attr_name; + } + + //---------------------------------------------------------------------------- + std::string PropertyBasedElement::attrToDataPropName(const std::string& name) + { + // http://www.w3.org/TR/html5/dom.html#attr-data-* + // + // For each "-" (U+002D) character in the name that is followed by a + // lowercase ASCII letter, remove the "-" (U+002D) character and replace the + // character that followed it by the same character converted to ASCII + // uppercase. + + if( !boost::starts_with(name, DATA_PREFIX) ) + return std::string(); + + std::string data_name; + for( std::string::const_iterator cur = name.begin() + DATA_PREFIX.length(); + cur != name.end(); + ++cur ) + { + std::string::const_iterator next = cur + 1; + if( *cur == '-' && next != name.end() && islower(*next) ) + { + data_name.push_back( toupper(*next) ); + cur = next; + } + else + data_name.push_back(*cur); + } + return data_name; + } + } // namespace simgear diff --git a/simgear/props/PropertyBasedElement.hxx b/simgear/props/PropertyBasedElement.hxx index dcaceaf0..52af9b30 100644 --- a/simgear/props/PropertyBasedElement.hxx +++ b/simgear/props/PropertyBasedElement.hxx @@ -76,8 +76,102 @@ namespace simgear // Unshadow what we have just hidden... using SGWeakReferenced::get; + + /** @brief Set a HTML5 like data property on this element. + * + * Set data-* properties on this element. A camel-case @a name will be + * converted to a hyphenated name with 'data-' prefixed. Setting a value + * with this method does not trigger an update of the canvas and is meant + * to store data related to this element (used eg. inside scripts). + * + * @code{cpp} + * // Set value + * my_element->setDataProp("mySpecialInt", 3); + * + * // Get value (with default value) + * int val = my_element->getDataProp("mySpecialInt"); // val == 3 + * val = my_element->getDataProp("notExisting", 5); // val == 5 + * + * // Check if value exists + * SGPropertyNode* node = + * my_element->getDataProp("mySpecialInt"); + * if( node ) + * val = node->getIntValue(); // node != NULL, val == 3 + * + * node = my_element->getDataProp("notExisting"); + * // node == NULL + * @endcode + */ + template + void setDataProp( const std::string& name, + const T& val ) + { + const std::string& attr = dataPropToAttrName(name); + if( !attr.empty() ) + set(attr, val); + else + SG_LOG(SG_GENERAL, SG_WARN, "Invalid data-prop name: " << name); + } + + /** @brief Get a HTML5 like data property on this element. + * + * Get value or default value. + * + * @see setDataProp + */ + template + typename boost::disable_if< + boost::is_same, + T + >::type getDataProp( const std::string& name, + const T& def = T() ) const + { + SGPropertyNode* node = getDataProp(name); + if( node ) + return getValue(node); + + return def; + } + + /** @brief Get a HTML5 like data property on this element. + * + * Use this variant to check if a property exists. + * + * @see setDataProp + */ + template + typename boost::enable_if< + boost::is_same, + T + >::type getDataProp( const std::string& name, + SGPropertyNode* = NULL ) const + { + const std::string& attr = dataPropToAttrName(name); + if( attr.empty() ) + { + SG_LOG(SG_GENERAL, SG_WARN, "Invalid data-prop name: " << name); + return NULL; + } + + return _node->getNode(attr); + } + + /** @brief Check whether a HTML5 like data property exists on this + * element. + * + */ + bool hasDataProp(const std::string& name) const; + + /** @brief Remove a HTML5 like data property (if it exists). + * + */ + void removeDataProp(const std::string& name); + virtual void onDestroy() {}; + static std::string dataPropToAttrName(const std::string& name); + static std::string attrToDataPropName(const std::string& name); + protected: SGPropertyNode_ptr _node; diff --git a/simgear/props/props.cxx b/simgear/props/props.cxx index d9c90a33..6d704722 100644 --- a/simgear/props/props.cxx +++ b/simgear/props/props.cxx @@ -1002,26 +1002,28 @@ SGPropertyNode::getChildren (const char * name) const return children; } +//------------------------------------------------------------------------------ +bool SGPropertyNode::removeChild(SGPropertyNode* node) +{ + if( node->_parent != this ) + return false; -/** - * Remove child by position. - */ -SGPropertyNode_ptr -SGPropertyNode::removeChild(int pos) + PropertyList::iterator it = + std::find(_children.begin(), _children.end(), node); + if( it == _children.end() ) + return false; + + eraseChild(it); + return true; +} + +//------------------------------------------------------------------------------ +SGPropertyNode_ptr SGPropertyNode::removeChild(int pos) { - SGPropertyNode_ptr node; if (pos < 0 || pos >= (int)_children.size()) - return node; - - PropertyList::iterator it = _children.begin(); - it += pos; - node = _children[pos]; - _children.erase(it); + return SGPropertyNode_ptr(); - node->setAttribute(REMOVED, true); - node->clearValue(); - fireChildRemoved(node); - return node; + return eraseChild(_children.begin() + pos); } @@ -2250,6 +2252,19 @@ SGPropertyNode::fireChildRemoved (SGPropertyNode * parent, _parent->fireChildRemoved(parent, child); } +//------------------------------------------------------------------------------ +SGPropertyNode_ptr +SGPropertyNode::eraseChild(simgear::PropertyList::iterator child) +{ + SGPropertyNode_ptr node = *child; + node->setAttribute(REMOVED, true); + node->clearValue(); + fireChildRemoved(node); + + _children.erase(child); + return node; +} + //////////////////////////////////////////////////////////////////////// // Implementation of SGPropertyChangeListener. //////////////////////////////////////////////////////////////////////// diff --git a/simgear/props/props.hxx b/simgear/props/props.hxx index c9e7c05d..59a8a43f 100644 --- a/simgear/props/props.hxx +++ b/simgear/props/props.hxx @@ -911,6 +911,13 @@ public: simgear::PropertyList getChildren (const std::string& name) const { return getChildren(name.c_str()); } + /** + * Remove child by pointer (if it is a child of this node). + * + * @return true, if the node was deleted. + */ + bool removeChild(SGPropertyNode* node); + // TODO do we need the removeXXX methods to return the deleted nodes? /** * Remove child by position. @@ -1701,6 +1708,8 @@ protected: void fireChildAdded (SGPropertyNode * parent, SGPropertyNode * child); void fireChildRemoved (SGPropertyNode * parent, SGPropertyNode * child); + SGPropertyNode_ptr eraseChild(simgear::PropertyList::iterator child); + /** * Protected constructor for making new nodes on demand. */