]> git.mxchange.org Git - simgear.git/blobdiff - simgear/canvas/Canvas.cxx
Canvas::Text: add line-height property.
[simgear.git] / simgear / canvas / Canvas.cxx
index 3112fcb3b9133eb7f5628c4b10adbdf9e8b1f61c..fa6712389c22ecaf2357cfb9b225f98f090ed63d 100644 (file)
@@ -17,6 +17,8 @@
 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
 
 #include "Canvas.hxx"
+#include "CanvasEventManager.hxx"
+#include "CanvasEventVisitor.hxx"
 #include <simgear/canvas/MouseEvent.hxx>
 #include <simgear/canvas/CanvasPlacement.hxx>
 #include <simgear/scene/util/parse_color.hxx>
@@ -29,7 +31,6 @@
 
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/foreach.hpp>
-#include <iostream>
 
 namespace simgear
 {
@@ -57,21 +58,13 @@ namespace canvas
   Canvas::Canvas(SGPropertyNode* node):
     PropertyBasedElement(node),
     _canvas_mgr(0),
+    _event_manager(new EventManager),
     _size_x(-1),
     _size_y(-1),
     _view_width(-1),
     _view_height(-1),
     _status(node, "status"),
     _status_msg(node, "status-msg"),
-    _mouse_x(node, "mouse/x"),
-    _mouse_y(node, "mouse/y"),
-    _mouse_dx(node, "mouse/dx"),
-    _mouse_dy(node, "mouse/dy"),
-    _mouse_button(node, "mouse/button"),
-    _mouse_state(node, "mouse/state"),
-    _mouse_mod(node, "mouse/mod"),
-    _mouse_scroll(node, "mouse/scroll"),
-    _mouse_event(node, "mouse/event"),
     _sampling_dirty(false),
     _render_dirty(true),
     _visible(true),
@@ -82,9 +75,13 @@ namespace canvas
   }
 
   //----------------------------------------------------------------------------
-  Canvas::~Canvas()
+  void Canvas::onDestroy()
   {
-
+    if( _root_group )
+    {
+      _root_group->clearEventListener();
+      _root_group->onDestroy();
+    }
   }
 
   //----------------------------------------------------------------------------
@@ -113,7 +110,31 @@ namespace canvas
   }
 
   //----------------------------------------------------------------------------
-  void Canvas::addDependentCanvas(const CanvasWeakPtr& canvas)
+  bool Canvas::isInit() const
+  {
+    return _texture.serviceable();
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::addParentCanvas(const CanvasWeakPtr& canvas)
+  {
+    if( canvas.expired() )
+    {
+      SG_LOG
+      (
+        SG_GENERAL,
+        SG_WARN,
+        "Canvas::addParentCanvas(" << _node->getPath(true) << "): "
+        "got an expired parent!"
+      );
+      return;
+    }
+
+    _parent_canvases.insert(canvas);
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::addChildCanvas(const CanvasWeakPtr& canvas)
   {
     if( canvas.expired() )
     {
@@ -121,28 +142,49 @@ namespace canvas
       (
         SG_GENERAL,
         SG_WARN,
-        "Canvas::addDependentCanvas: got an expired Canvas dependent on "
-        << _node->getPath()
+        "Canvas::addChildCanvas(" << _node->getPath(true) << "): "
+        " got an expired child!"
       );
       return;
     }
 
-    _dependent_canvases.insert(canvas);
+    _child_canvases.insert(canvas);
   }
 
   //----------------------------------------------------------------------------
-  void Canvas::removeDependentCanvas(const CanvasWeakPtr& canvas)
+  void Canvas::removeParentCanvas(const CanvasWeakPtr& canvas)
   {
-    _dependent_canvases.erase(canvas);
+    _parent_canvases.erase(canvas);
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::removeChildCanvas(const CanvasWeakPtr& canvas)
+  {
+    _child_canvases.erase(canvas);
   }
 
   //----------------------------------------------------------------------------
   GroupPtr Canvas::createGroup(const std::string& name)
   {
-    return boost::dynamic_pointer_cast<Group>
-    (
-      _root_group->createChild("group", name)
-    );
+    return _root_group->createChild<Group>(name);
+  }
+
+  //----------------------------------------------------------------------------
+  GroupPtr Canvas::getGroup(const std::string& name)
+  {
+    return _root_group->getChild<Group>(name);
+  }
+
+  //----------------------------------------------------------------------------
+  GroupPtr Canvas::getOrCreateGroup(const std::string& name)
+  {
+    return _root_group->getOrCreateChild<Group>(name);
+  }
+
+  //----------------------------------------------------------------------------
+  GroupPtr Canvas::getRootGroup()
+  {
+    return _root_group;
   }
 
   //----------------------------------------------------------------------------
@@ -156,15 +198,26 @@ namespace canvas
   //----------------------------------------------------------------------------
   void Canvas::update(double delta_time_sec)
   {
-    if( !_texture.serviceable() )
-    {
-      if( _status != STATUS_OK )
-        return;
+    if(    (!_texture.serviceable() && _status != STATUS_DIRTY)
+        || (_status & CREATE_FAILED) )
+      return;
 
+    if( _status == STATUS_DIRTY )
+    {
       _texture.setSize(_size_x, _size_y);
-      _texture.useImageCoords(true);
-      _texture.useStencil(true);
-      _texture.allocRT(/*_camera_callback*/);
+
+      if( !_texture.serviceable() )
+      {
+        _texture.useImageCoords(true);
+        _texture.useStencil(true);
+        _texture.allocRT(/*_camera_callback*/);
+      }
+      else
+      {
+        // Resizing causes a new texture to be created so we need to reapply all
+        // existing placements
+        reloadPlacements();
+      }
 
       osg::Camera* camera = _texture.getCamera();
 
@@ -174,12 +227,11 @@ namespace canvas
 
       camera->addChild(_root_group->getMatrixTransform());
 
-      // Ensure objects are drawn in order of traversal
-      camera->getOrCreateStateSet()->setBinName("TraversalOrderBin");
-
       if( _texture.serviceable() )
       {
         setStatusFlags(STATUS_OK);
+        setStatusFlags(STATUS_DIRTY, false);
+        _render_dirty = true;
       }
       else
       {
@@ -190,10 +242,18 @@ namespace canvas
 
     if( _visible || _render_always )
     {
+      BOOST_FOREACH(CanvasWeakPtr canvas, _child_canvases)
+      {
+        // TODO should we check if the image the child canvas is displayed
+        //      within is really visible?
+        if( !canvas.expired() )
+          canvas.lock()->_visible = true;
+      }
+
       if( _render_dirty )
       {
-        // Also mark all dependent (eg. recursively used) canvases as dirty
-        BOOST_FOREACH(CanvasWeakPtr canvas, _dependent_canvases)
+        // Also mark all canvases this canvas is displayed within as dirty
+        BOOST_FOREACH(CanvasWeakPtr canvas, _parent_canvases)
         {
           if( !canvas.expired() )
             canvas.lock()->_render_dirty = true;
@@ -255,14 +315,22 @@ namespace canvas
     }
   }
 
+  //----------------------------------------------------------------------------
+  naRef Canvas::addEventListener(const nasal::CallContext& ctx)
+  {
+    if( !_root_group.get() )
+      naRuntimeError(ctx.c, "Canvas: No root group!");
+
+    return _root_group->addEventListener(ctx);
+  }
+
   //----------------------------------------------------------------------------
   void Canvas::setSizeX(int sx)
   {
     if( _size_x == sx )
       return;
     _size_x = sx;
-
-    // TODO resize if texture already allocated
+    setStatusFlags(STATUS_DIRTY);
 
     if( _size_x <= 0 )
       setStatusFlags(MISSING_SIZE_X);
@@ -279,8 +347,7 @@ namespace canvas
     if( _size_y == sy )
       return;
     _size_y = sy;
-
-    // TODO resize if texture already allocated
+    setStatusFlags(STATUS_DIRTY);
 
     if( _size_y <= 0 )
       setStatusFlags(MISSING_SIZE_Y);
@@ -324,23 +391,37 @@ namespace canvas
   }
 
   //----------------------------------------------------------------------------
-  bool Canvas::handleMouseEvent(const MouseEvent& event)
+  int Canvas::getViewWidth() const
   {
-    _mouse_x = event.x;
-    _mouse_y = event.y;
-    _mouse_dx = event.dx;
-    _mouse_dy = event.dy;
-    _mouse_button = event.button;
-    _mouse_state = event.state;
-    _mouse_mod = event.mod;
-    _mouse_scroll = event.scroll;
-    // Always set event type last because all listeners are attached to it
-    _mouse_event = event.type;
+    return _texture.getViewSize().x();
+  }
 
-    if( _root_group.get() )
-      return _root_group->handleMouseEvent(event);
-    else
+  //----------------------------------------------------------------------------
+  int Canvas::getViewHeight() const
+  {
+    return _texture.getViewSize().y();
+  }
+
+  //----------------------------------------------------------------------------
+  SGRect<int> Canvas::getViewport() const
+  {
+    return SGRect<int>(0, 0, getViewWidth(), getViewHeight());
+  }
+
+  //----------------------------------------------------------------------------
+  bool Canvas::handleMouseEvent(const MouseEventPtr& event)
+  {
+    if( !_root_group.get() )
       return false;
+
+    EventVisitor visitor( EventVisitor::TRAVERSE_DOWN,
+                          event->getClientPos(),
+                          event->getDelta(),
+                          _root_group );
+    if( !_root_group->accept(visitor) )
+      return false;
+
+    return _event_manager->handleEvent(event, visitor.getPropagationPath());
   }
 
   //----------------------------------------------------------------------------
@@ -383,21 +464,26 @@ namespace canvas
     if(    node->getParent()->getParent() == _node
         && node->getParent()->getNameString() == "placement" )
     {
-      bool placement_dirty = false;
-      BOOST_FOREACH(Placements& placements, _placements)
+      size_t index = node->getIndex();
+      if( index < _placements.size() )
       {
-        BOOST_FOREACH(PlacementPtr& placement, placements)
+        Placements& placements = _placements[index];
+        if( !placements.empty() )
         {
-          // check if change can be directly handled by placement
-          if(    placement->getProps() == node->getParent()
-              && !placement->childChanged(node) )
-            placement_dirty = true;
+          bool placement_dirty = false;
+          BOOST_FOREACH(PlacementPtr& placement, placements)
+          {
+            // check if change can be directly handled by placement
+            if(    placement->getProps() == node->getParent()
+                && !placement->childChanged(node) )
+              placement_dirty = true;
+          }
+
+          if( !placement_dirty )
+            return;
         }
       }
 
-      if( !placement_dirty )
-        return;
-
       // prevent double updates...
       for( size_t i = 0; i < _dirty_placements.size(); ++i )
       {
@@ -424,6 +510,10 @@ namespace canvas
       {
         _sampling_dirty = true;
       }
+      else if( node->getNameString() == "additive-blend" )
+      {
+        _texture.useAdditiveBlend( node->getBoolValue() );
+      }
       else if( node->getNameString() == "render-always" )
       {
         _render_always = node->getBoolValue();
@@ -466,6 +556,24 @@ namespace canvas
     return _cull_callback;
   }
 
+  //----------------------------------------------------------------------------
+  void Canvas::reloadPlacements(const std::string& type)
+  {
+    for(size_t i = 0; i < _placements.size(); ++i)
+    {
+      if( _placements[i].empty() )
+        continue;
+
+      SGPropertyNode* child = _placements[i].front()->getProps();
+      if(    type.empty()
+             // reload if type matches or no type specified
+          || child->getStringValue("type", type.c_str()) == type )
+      {
+        _dirty_placements.push_back(child);
+      }
+    }
+  }
+
   //----------------------------------------------------------------------------
   void Canvas::addPlacementFactory( const std::string& type,
                                     PlacementFactory factory )
@@ -489,6 +597,7 @@ namespace canvas
     CanvasPtr canvas = boost::static_pointer_cast<Canvas>(self);
 
     _root_group.reset( new Group(canvas, _node) );
+    _root_group->setSelf(_root_group);
 
     // Remove automatically created property listener as we forward them on our
     // own
@@ -501,10 +610,9 @@ namespace canvas
   void Canvas::setStatusFlags(unsigned int flags, bool set)
   {
     if( set )
-      _status = _status | flags;
+      _status |= flags;
     else
-      _status = _status & ~flags;
-    // TODO maybe extend simgear::PropertyObject to allow |=, &= etc.
+      _status &= ~flags;
 
     if( (_status & MISSING_SIZE_X) && (_status & MISSING_SIZE_Y) )
       _status_msg = "Missing size";
@@ -514,7 +622,7 @@ namespace canvas
       _status_msg = "Missing size-y";
     else if( _status & CREATE_FAILED )
       _status_msg = "Creating render target failed";
-    else if( _status == STATUS_OK && !_texture.serviceable() )
+    else if( _status == STATUS_DIRTY )
       _status_msg = "Creation pending...";
     else
       _status_msg = "Ok";