]> git.mxchange.org Git - simgear.git/blobdiff - simgear/canvas/elements/CanvasElement.hxx
Canvas: Respect clipping while event handling.
[simgear.git] / simgear / canvas / elements / CanvasElement.hxx
index d3a5bfa9e01f417c52f6d24c59b7d046b3cdf994..dc5e1bf7592985d125b588bc26470c5d4e770ead 100644 (file)
 #include <simgear/canvas/CanvasEvent.hxx>
 #include <simgear/props/PropertyBasedElement.hxx>
 #include <simgear/misc/stdint.hxx> // for uint32_t
-#include <simgear/nasal/cppbind/Ghost.hxx>
 
 #include <osg/BoundingBox>
 #include <osg/MatrixTransform>
 
 #include <boost/bind.hpp>
 #include <boost/function.hpp>
+#include <boost/type_traits/is_base_of.hpp>
 
 namespace osg
 {
@@ -45,24 +45,57 @@ namespace canvas
     public PropertyBasedElement
   {
     public:
-      typedef boost::function<void(const SGPropertyNode*)> StyleSetter;
-      typedef std::map<std::string, StyleSetter> StyleSetters;
 
       /**
-       * Remove the property listener of the element.
-       *
-       * You will need to call the appropriate methods (#childAdded,
-       * #childRemoved, #valueChanged) yourself to ensure the element still
-       * receives the needed events.
+       * Store pointer to window as user data
+       */
+      class OSGUserData:
+        public osg::Referenced
+      {
+        public:
+          ElementPtr element;
+          OSGUserData(ElementPtr element);
+      };
+
+      typedef boost::function<bool(Element&, const SGPropertyNode*)>
+              StyleSetterFunc;
+      typedef boost::function<void(Element&, const SGPropertyNode*)>
+              StyleSetterFuncUnchecked;
+      struct StyleSetter:
+        public SGReferenced
+      {
+        StyleSetterFunc func;
+        SGSharedPtr<StyleSetter> next;
+      };
+      struct StyleInfo
+      {
+        StyleSetter setter; ///!< Function(s) for setting this style
+        std::string type;   ///!< Interpolation type
+        bool inheritable;   ///!< Whether children can inherit this style from
+                            ///   their parents
+      };
+
+      /**
+       * Coordinate reference frame (eg. "clip" property)
        */
-      void removeListener();
+      enum ReferenceFrame
+      {
+        GLOBAL, ///!< Global coordinates
+        PARENT, ///!< Coordinates relative to parent coordinate frame
+        LOCAL   ///!< Coordinates relative to local coordinates (parent
+                ///   coordinates with local transformations applied)
+      };
 
       /**
        *
        */
       virtual ~Element() = 0;
 
+      virtual void setSelf(const PropertyBasedElementPtr& self);
+      virtual void onDestroy();
+
       ElementWeakPtr getWeakPtr() const;
+      ElementPtr getParent();
 
       /**
        * Called every frame to update internal state
@@ -71,18 +104,42 @@ namespace canvas
        */
       virtual void update(double dt);
 
-      naRef addEventListener(const nasal::CallContext& ctx);
+      bool addEventListener(const std::string& type, const EventListener& cb);
+      virtual void clearEventListener();
 
       virtual bool accept(EventVisitor& visitor);
       virtual bool ascend(EventVisitor& visitor);
       virtual bool traverse(EventVisitor& visitor);
 
-      void callListeners(const canvas::EventPtr& event);
+      virtual bool handleEvent(canvas::EventPtr event);
 
-      virtual bool hitBound(const osg::Vec2f& pos) const;
+      /**
+       *
+       * @param global_pos Position in global (canvas) coordinate frame
+       * @param parent_pos Position in parent coordinate frame
+       * @param local_pos  Position in local (element) coordinate frame
+       */
+      virtual bool hitBound( const osg::Vec2f& global_pos,
+                             const osg::Vec2f& parent_pos,
+                             const osg::Vec2f& local_pos ) const;
+
+      /**
+       * Set visibility of the element.
+       */
+      void setVisible(bool visible);
+
+      /**
+       * Get whether the element is visible or hidden.
+       */
+      bool isVisible() const;
 
+      osg::MatrixTransform* getMatrixTransform();
+      osg::MatrixTransform const* getMatrixTransform() const;
 
-      osg::ref_ptr<osg::MatrixTransform> getMatrixTransform();
+      /**
+       * Transform position to local coordinages.
+       */
+      osg::Vec2f posToLocal(const osg::Vec2f& pos) const;
 
       virtual void childAdded( SGPropertyNode * parent,
                                SGPropertyNode * child );
@@ -90,7 +147,21 @@ namespace canvas
                                  SGPropertyNode * child );
       virtual void valueChanged(SGPropertyNode * child);
 
-      virtual bool setStyle(const SGPropertyNode* child);
+      virtual bool setStyle( const SGPropertyNode* child,
+                             const StyleInfo* style_info = 0 );
+
+      /**
+       * Set clipping shape
+       *
+       * @note Only "rect(<top>, <right>, <bottom>, <left>)" is supported
+       * @see http://www.w3.org/TR/CSS21/visufx.html#propdef-clip
+       */
+      void setClip(const std::string& clip);
+
+      /**
+       * Clipping coordinates reference frame
+       */
+      void setClipFrame(ReferenceFrame rf);
 
       /**
        * Write the given bounding box to the property tree
@@ -102,11 +173,39 @@ namespace canvas
        */
       virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
 
+      /**
+       * Get the transformation matrix (product of all transforms)
+       */
+      osg::Matrix getMatrix() const;
+
+      /**
+       * Create an canvas Element
+       *
+       * @tparam Derived    Type of element (needs to derive from Element)
+       */
+      template<typename Derived>
+      static
+      typename boost::enable_if<
+        boost::is_base_of<Element, Derived>,
+        ElementPtr
+      >::type create( const CanvasWeakPtr& canvas,
+                      const SGPropertyNode_ptr& node,
+                      const Style& style,
+                      Element* parent )
+      {
+        ElementPtr el( new Derived(canvas, node, style, parent) );
+        el->setSelf(el);
+        return el;
+      }
+
     protected:
 
       enum Attributes
       {
-        LAST_ATTRIBUTE  = 0x0001
+        TRANSFORM       = 1,
+        BLEND_FUNC      = TRANSFORM << 1,
+        SCISSOR_COORDS  = BLEND_FUNC << 1,
+        LAST_ATTRIBUTE  = SCISSOR_COORDS << 1
       };
 
       enum TransformType
@@ -118,70 +217,342 @@ namespace canvas
         TT_SCALE
       };
 
+      class RelativeScissor;
+
       CanvasWeakPtr _canvas;
       Element      *_parent;
 
-      uint32_t _attributes_dirty;
+      mutable uint32_t _attributes_dirty;
 
-      bool _transform_dirty;
-      osg::ref_ptr<osg::MatrixTransform>    _transform;
-      std::vector<TransformType>            _transform_types;
+      osg::observer_ptr<osg::MatrixTransform> _transform;
+      std::vector<TransformType>              _transform_types;
 
       Style                             _style;
-      StyleSetters                      _style_setters;
       std::vector<SGPropertyNode_ptr>   _bounding_box;
+      RelativeScissor                  *_scissor;
 
-      typedef std::vector<EventListenerPtr> Listener;
+      typedef std::vector<EventListener> Listener;
       typedef std::map<Event::Type, Listener> ListenerMap;
 
       ListenerMap _listener;
 
+      typedef std::map<std::string, StyleInfo> StyleSetters;
+      static StyleSetters       _style_setters;
+
+      static void staticInit();
+
       Element( const CanvasWeakPtr& canvas,
                const SGPropertyNode_ptr& node,
                const Style& parent_style,
                Element* parent );
 
-      template<typename T, class C1, class C2>
-      Element::StyleSetter
-      addStyle(const std::string& name, void (C1::*setter)(T), C2 instance)
+      /**
+       * Returns false on first call and true on any successive call. Use to
+       * perform initialization tasks which are only required once per element
+       * type.
+       *
+       * @tparam Derived    (Derived) class type
+       */
+      template<class Derived>
+      static bool isInit()
       {
-        return _style_setters[ name ] =
-                 bindStyleSetter<T>(name, setter, instance);
+        static bool is_init = false;
+        if( is_init )
+          return true;
+
+        is_init = true;
+        return false;
       }
 
-      template<typename T1, typename T2, class C1, class C2>
-      Element::StyleSetter
-      addStyle(const std::string& name, void (C1::*setter)(T2), C2 instance)
+      /**
+       * Register a function for setting a style specified by the given property
+       *
+       * @param name    Property name
+       * @param type    Interpolation type
+       * @param setter  Setter function
+       *
+       * @tparam T1         Type of value used to retrieve value from property
+       *                    node
+       * @tparam T2         Type of value the setter function expects
+       * @tparam Derived    Type of class the setter can be applied to
+       *
+       * @note T1 needs to be convertible to T2
+       */
+      template<
+        typename T1,
+        typename T2,
+        class Derived
+      >
+      static
+      StyleSetter
+      addStyle( const std::string& name,
+                const std::string& type,
+                const boost::function<void (Derived&, T2)>& setter,
+                bool inheritable = true )
       {
-        return _style_setters[ name ] =
-                 bindStyleSetter<T1>(name, setter, instance);
+        StyleInfo& style_info = _style_setters[ name ];
+        if( !type.empty() )
+        {
+          if( !style_info.type.empty() && type != style_info.type )
+            SG_LOG
+            (
+              SG_GENERAL,
+              SG_WARN,
+              "changing animation type for '" << name << "': "
+                << style_info.type << " -> " << type
+            );
+
+          style_info.type = type;
+        }
+        // TODO check if changed?
+        style_info.inheritable = inheritable;
+
+        StyleSetter* style = &style_info.setter;
+        while( style->next )
+          style = style->next;
+        if( style->func )
+          style = style->next = new StyleSetter;
+
+        style->func = boost::bind
+        (
+          &type_match<Derived>::call,
+          _1,
+          _2,
+          bindStyleSetter<T1>(name, setter)
+        );
+        return *style;
       }
 
-      template<class C1, class C2>
-      Element::StyleSetter
+      template<
+        typename T,
+        class Derived
+      >
+      static
+      StyleSetter
       addStyle( const std::string& name,
-                void (C1::*setter)(const std::string&),
-                C2 instance )
+                const std::string& type,
+                const boost::function<void (Derived&, T)>& setter,
+                bool inheritable = true )
       {
-        return _style_setters[ name ] =
-                 bindStyleSetter<const char*>(name, setter, instance);
+        return addStyle<T, T>(name, type, setter, inheritable);
       }
 
-      template<typename T1, typename T2, class C1, class C2>
-      Element::StyleSetter
+      template<
+        typename T,
+        class Derived
+      >
+      static
+      StyleSetter
+      addStyle( const std::string& name,
+                const std::string& type,
+                void (Derived::*setter)(T),
+                bool inheritable = true )
+      {
+        return addStyle<T, T>
+        (
+          name,
+          type,
+          boost::function<void (Derived&, T)>(setter),
+          inheritable
+        );
+      }
+
+      template<
+        typename T1,
+        typename T2,
+        class Derived
+      >
+      static
+      StyleSetterFunc
+      addStyle( const std::string& name,
+                const std::string& type,
+                void (Derived::*setter)(T2),
+                bool inheritable = true )
+      {
+        return addStyle<T1>
+        (
+          name,
+          type,
+          boost::function<void (Derived&, T2)>(setter),
+          inheritable
+        );
+      }
+
+      template<
+        class Derived
+      >
+      static
+      StyleSetter
+      addStyle( const std::string& name,
+                const std::string& type,
+                void (Derived::*setter)(const std::string&),
+                bool inheritable = true )
+      {
+        return addStyle<const char*, const std::string&>
+        (
+          name,
+          type,
+          boost::function<void (Derived&, const std::string&)>(setter),
+          inheritable
+        );
+      }
+
+      template<
+        typename T,
+        class Derived,
+        class Other,
+        class OtherRef
+      >
+      static
+      StyleSetter
+      addStyle( const std::string& name,
+                const std::string& type,
+                void (Other::*setter)(T),
+                OtherRef Derived::*instance_ref,
+                bool inheritable = true )
+      {
+        return addStyle<T, T>
+        (
+          name,
+          type,
+          bindOther(setter, instance_ref),
+          inheritable
+        );
+      }
+
+      template<
+        typename T1,
+        typename T2,
+        class Derived,
+        class Other,
+        class OtherRef
+      >
+      static
+      StyleSetter
+      addStyle( const std::string& name,
+                const std::string& type,
+                void (Other::*setter)(T2),
+                OtherRef Derived::*instance_ref,
+                bool inheritable = true )
+      {
+        return addStyle<T1>
+        (
+          name,
+          type,
+          bindOther(setter, instance_ref),
+          inheritable
+        );
+      }
+
+      template<
+        typename T1,
+        typename T2,
+        class Derived,
+        class Other,
+        class OtherRef
+      >
+      static
+      StyleSetter
+      addStyle( const std::string& name,
+                const std::string& type,
+                const boost::function<void (Other&, T2)>& setter,
+                OtherRef Derived::*instance_ref,
+                bool inheritable = true )
+      {
+        return addStyle<T1>
+        (
+          name,
+          type,
+          bindOther(setter, instance_ref),
+          inheritable
+        );
+      }
+
+      template<
+        class Derived,
+        class Other,
+        class OtherRef
+      >
+      static
+      StyleSetter
+      addStyle( const std::string& name,
+                const std::string& type,
+                void (Other::*setter)(const std::string&),
+                OtherRef Derived::*instance_ref,
+                bool inheritable = true )
+      {
+        return addStyle<const char*, const std::string&>
+        (
+          name,
+          type,
+          boost::function<void (Other&, const std::string&)>(setter),
+          instance_ref,
+          inheritable
+        );
+      }
+
+      template<typename T, class Derived, class Other, class OtherRef>
+      static
+      boost::function<void (Derived&, T)>
+      bindOther( void (Other::*setter)(T), OtherRef Derived::*instance_ref )
+      {
+        return boost::bind(setter, boost::bind(instance_ref, _1), _2);
+      }
+
+      template<typename T, class Derived, class Other, class OtherRef>
+      static
+      boost::function<void (Derived&, T)>
+      bindOther( const boost::function<void (Other&, T)>& setter,
+                 OtherRef Derived::*instance_ref )
+      {
+        return boost::bind
+        (
+          setter,
+          boost::bind
+          (
+            &reference_from_pointer<Other, OtherRef>,
+            boost::bind(instance_ref, _1)
+          ),
+          _2
+        );
+      }
+
+      template<typename T1, typename T2, class Derived>
+      static
+      StyleSetterFuncUnchecked
       bindStyleSetter( const std::string& name,
-                       void (C1::*setter)(T2),
-                       C2 instance )
+                       const boost::function<void (Derived&, T2)>& setter )
       {
-        return boost::bind(setter, instance, boost::bind(&getValue<T1>, _1));
+        return boost::bind
+        (
+          setter,
+          // We will only call setters with Derived instances, so we can safely
+          // cast here.
+          boost::bind(&derived_cast<Derived>, _1),
+          boost::bind(&getValue<T1>, _2)
+        );
       }
 
+      bool isStyleEmpty(const SGPropertyNode* child) const;
+      bool canApplyStyle(const SGPropertyNode* child) const;
+      bool setStyleImpl( const SGPropertyNode* child,
+                         const StyleInfo* style_info = 0 );
+
+      const StyleInfo* getStyleInfo(const std::string& name) const;
+      const StyleSetter* getStyleSetter(const std::string& name) const;
+      const SGPropertyNode* getParentStyle(const SGPropertyNode* child) const;
+
       virtual void childAdded(SGPropertyNode * child)  {}
       virtual void childRemoved(SGPropertyNode * child){}
       virtual void childChanged(SGPropertyNode * child){}
 
       void setDrawable(osg::Drawable* drawable);
 
+      /**
+       * Get stateset of drawable if available or use transform otherwise
+       */
+      osg::StateSet* getOrCreateStateSet();
+
       void setupStyle();
 
     private:
@@ -189,9 +560,63 @@ namespace canvas
       osg::ref_ptr<osg::Drawable> _drawable;
 
       Element(const Element&);// = delete
+
+      template<class Derived>
+      static Derived& derived_cast(Element& el)
+      {
+        return static_cast<Derived&>(el);
+      }
+
+      template<class T, class SharedPtr>
+      static T& reference_from_pointer(const SharedPtr& p)
+      {
+        return *get_pointer(p);
+      }
+
+      /**
+       * Helper to call a function only of the element type can be converted to
+       * the required type.
+       *
+       * @return Whether the function has been called
+       */
+      template<class Derived>
+      struct type_match
+      {
+        static bool call( Element& el,
+                          const SGPropertyNode* prop,
+                          const StyleSetterFuncUnchecked& func )
+        {
+          Derived* d = dynamic_cast<Derived*>(&el);
+          if( !d )
+            return false;
+          func(*d, prop);
+          return true;
+        }
+      };
   };
 
 } // namespace canvas
+
+  template<>
+  struct enum_traits<canvas::Element::ReferenceFrame>
+  {
+    static const char* name()
+    {
+      return "canvas::Element::ReferenceFrame";
+    }
+
+    static canvas::Element::ReferenceFrame defVal()
+    {
+      return canvas::Element::GLOBAL;
+    }
+
+    static bool validate(int frame)
+    {
+      return frame >= canvas::Element::GLOBAL
+          && frame <= canvas::Element::LOCAL;
+    }
+  };
+
 } // namespace simgear
 
 #endif /* CANVAS_ELEMENT_HXX_ */