// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "Canvas.hxx"
-#include <simgear/canvas/MouseEvent.hxx>
+#include "CanvasEventManager.hxx"
+#include "CanvasEventVisitor.hxx"
+#include "CanvasPlacement.hxx"
+#include <simgear/canvas/events/KeyboardEvent.hxx>
+#include <simgear/canvas/events/MouseEvent.hxx>
#include <simgear/scene/util/parse_color.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/foreach.hpp>
-#include <iostream>
namespace simgear
{
void Canvas::CullCallback::operator()( osg::Node* node,
osg::NodeVisitor* nv )
{
- if( (nv->getTraversalMask() & simgear::MODEL_BIT) && !_canvas.expired() )
- _canvas.lock()->enableRendering();
+ if( (nv->getTraversalMask() & simgear::MODEL_BIT) )
+ {
+ CanvasPtr canvas = _canvas.lock();
+ if( canvas )
+ canvas->enableRendering();
+ }
traverse(node, nv);
}
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),
{
_status = 0;
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
- }
- //----------------------------------------------------------------------------
- Canvas::~Canvas()
- {
+ _root_group.reset( new Group(this, _node) );
+ // Remove automatically created property listener as we forward them on our
+ // own
+ _root_group->removeListener();
+ _cull_callback = new CullCallback(this);
}
//----------------------------------------------------------------------------
- void Canvas::setSystemAdapter(const SystemAdapterPtr& system_adapter)
+ Canvas::~Canvas()
{
- _system_adapter = system_adapter;
- _texture.setSystemAdapter(system_adapter);
+
}
//----------------------------------------------------------------------------
- SystemAdapterPtr Canvas::getSystemAdapter() const
+ void Canvas::onDestroy()
{
- return _system_adapter;
+ if( _root_group )
+ {
+ _root_group->clearEventListener();
+ _root_group->onDestroy();
+ }
}
//----------------------------------------------------------------------------
}
//----------------------------------------------------------------------------
- void Canvas::addDependentCanvas(const CanvasWeakPtr& canvas)
+ bool Canvas::isInit() const
+ {
+ return _texture.serviceable();
+ }
+
+ //----------------------------------------------------------------------------
+ void Canvas::addParentCanvas(const CanvasWeakPtr& canvas)
{
if( canvas.expired() )
{
(
SG_GENERAL,
SG_WARN,
- "Canvas::addDependentCanvas: got an expired Canvas dependent on "
- << _node->getPath()
+ "Canvas::addParentCanvas(" << _node->getPath(true) << "): "
+ "got an expired parent!"
);
return;
}
- _dependent_canvases.insert(canvas);
+ _parent_canvases.insert(canvas);
}
//----------------------------------------------------------------------------
- void Canvas::removeDependentCanvas(const CanvasWeakPtr& canvas)
+ void Canvas::addChildCanvas(const CanvasWeakPtr& canvas)
{
- _dependent_canvases.erase(canvas);
+ if( canvas.expired() )
+ {
+ SG_LOG
+ (
+ SG_GENERAL,
+ SG_WARN,
+ "Canvas::addChildCanvas(" << _node->getPath(true) << "): "
+ " got an expired child!"
+ );
+ return;
+ }
+
+ _child_canvases.insert(canvas);
+ }
+
+ //----------------------------------------------------------------------------
+ void Canvas::removeParentCanvas(const CanvasWeakPtr& 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;
+ }
+
+ //----------------------------------------------------------------------------
+ void Canvas::setLayout(const LayoutRef& layout)
+ {
+ _layout = layout;
+ _layout->setCanvas(this);
+ }
+
+ //----------------------------------------------------------------------------
+ void Canvas::setFocusElement(const ElementPtr& el)
+ {
+ if( el && el->getCanvas().lock() != this )
+ {
+ SG_LOG(SG_GUI, SG_WARN, "setFocusElement: element not from this canvas");
+ return;
+ }
+
+ // TODO focus out/in events
+ _focus_element = el;
+ }
+
+ //----------------------------------------------------------------------------
+ void Canvas::clearFocusElement()
+ {
+ _focus_element.reset();
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
void Canvas::update(double delta_time_sec)
{
- if( !_texture.serviceable() )
- {
- if( _status != STATUS_OK )
- return;
+ if( _status & (CREATE_FAILED | MISSING_SIZE) )
+ 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();
+ // TODO Allow custom render order? For now just keep in order with
+ // property tree.
+ camera->setRenderOrder(osg::Camera::PRE_RENDER, _node->getIndex());
+
osg::Vec4 clear_color(0.0f, 0.0f, 0.0f , 1.0f);
parseColor(_node->getStringValue("background"), clear_color);
camera->setClearColor(clear_color);
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
{
}
}
+ if( _layout )
+ _layout->setGeometry(SGRecti(0, 0, _view_width, _view_height));
+
if( _visible || _render_always )
{
+ BOOST_FOREACH(CanvasWeakPtr canvas_weak, _child_canvases)
+ {
+ // TODO should we check if the image the child canvas is displayed
+ // within is really visible?
+ CanvasPtr canvas = canvas_weak.lock();
+ if( canvas )
+ canvas->_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_weak, _parent_canvases)
{
- if( !canvas.expired() )
- canvas.lock()->_render_dirty = true;
+ CanvasPtr canvas = canvas_weak.lock();
+ if( canvas )
+ canvas->_render_dirty = true;
}
}
if( placement_factory != _placement_factories.end() )
{
Placements& placements = _placements[ node->getIndex() ] =
- placement_factory->second
- (
- node,
- boost::static_pointer_cast<Canvas>(_self.lock())
- );
+ placement_factory->second(node, this);
node->setStringValue
(
"status-msg",
}
}
+ //----------------------------------------------------------------------------
+ bool Canvas::addEventListener( const std::string& type,
+ const EventListener& cb )
+ {
+ if( !_root_group.get() )
+ throw std::runtime_error("Canvas::addEventListener: no root group!");
+
+ return _root_group->addEventListener(type, cb);
+ }
+
+ //----------------------------------------------------------------------------
+ bool Canvas::dispatchEvent(const EventPtr& event)
+ {
+ if( !_root_group.get() )
+ throw std::runtime_error("Canvas::dispatchEvent: no root group!");
+
+ return _root_group->dispatchEvent(event);
+ }
+
//----------------------------------------------------------------------------
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);
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);
}
//----------------------------------------------------------------------------
- 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 )
return false;
+
+ EventVisitor visitor( EventVisitor::TRAVERSE_DOWN,
+ event->getClientPos(),
+ _root_group );
+ if( !_root_group->accept(visitor) )
+ return false;
+
+ return _event_manager->handleEvent(event, visitor.getPropagationPath());
+ }
+
+ //----------------------------------------------------------------------------
+ bool Canvas::handleKeyboardEvent(const KeyboardEventPtr& event)
+ {
+ ElementPtr target = _focus_element.lock();
+ if( !target )
+ target = _root_group;
+ if( !target )
+ return false;
+
+ return target->dispatchEvent(event);
+ }
+
+ //----------------------------------------------------------------------------
+ bool Canvas::propagateEvent( EventPtr const& event,
+ EventPropagationPath const& path )
+ {
+ return _event_manager->propagateEvent(event, path);
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
void Canvas::valueChanged(SGPropertyNode* node)
{
- if( boost::starts_with(node->getNameString(), "status")
- || node->getParent()->getNameString() == "bounding-box" )
+ const std::string& name = node->getNameString();
+
+ if( boost::starts_with(name, "status")
+ || boost::starts_with(name, "data-") )
return;
_render_dirty = true;
if( node->getParent()->getParent() == _node
&& node->getParent()->getNameString() == "placement" )
{
+ size_t index = node->getIndex();
+ if( index < _placements.size() )
+ {
+ Placements& placements = _placements[index];
+ if( !placements.empty() )
+ {
+ 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;
+ }
+ }
+
// prevent double updates...
for( size_t i = 0; i < _dirty_placements.size(); ++i )
{
}
else if( node->getParent() == _node )
{
- if( node->getNameString() == "background" )
+ if( name == "background" )
{
osg::Vec4 color;
if( _texture.getCamera() && parseColor(node->getStringValue(), color) )
_render_dirty = true;
}
}
- else if( node->getNameString() == "mipmapping"
- || node->getNameString() == "coverage-samples"
- || node->getNameString() == "color-samples" )
+ else if( name == "mipmapping"
+ || name == "coverage-samples"
+ || name == "color-samples" )
{
_sampling_dirty = true;
}
- else if( node->getNameString() == "render-always" )
+ else if( name == "additive-blend" )
+ {
+ _texture.useAdditiveBlend( node->getBoolValue() );
+ }
+ else if( name == "render-always" )
{
_render_always = node->getBoolValue();
}
- else if( node->getNameString() == "size" )
+ else if( name == "size" )
{
if( node->getIndex() == 0 )
setSizeX( node->getIntValue() );
else if( node->getIndex() == 1 )
setSizeY( node->getIntValue() );
}
- else if( node->getNameString() == "view" )
+ else if( name == "update" )
+ {
+ if( _root_group )
+ _root_group->update(0);
+ return update(0);
+ }
+ else if( name == "view" )
{
if( node->getIndex() == 0 )
setViewWidth( node->getIntValue() );
else if( node->getIndex() == 1 )
setViewHeight( node->getIntValue() );
}
- else if( node->getNameString() == "freeze" )
+ else if( name == "freeze" )
_texture.setRender( node->getBoolValue() );
else
handled = false;
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 )
(
SG_GENERAL,
SG_WARN,
- "Canvas::addPlacementFactory: replace existing factor for type " << type
+ "Canvas::addPlacementFactory: replace existing factory '" << type << "'"
);
_placement_factories[type] = factory;
}
//----------------------------------------------------------------------------
- void Canvas::setSelf(const PropertyBasedElementPtr& self)
+ void Canvas::removePlacementFactory(const std::string& type)
{
- PropertyBasedElement::setSelf(self);
-
- CanvasPtr canvas = boost::static_pointer_cast<Canvas>(self);
+ PlacementFactoryMap::iterator it = _placement_factories.find(type);
+ if( it == _placement_factories.end() )
+ SG_LOG
+ (
+ SG_GENERAL,
+ SG_WARN,
+ "Canvas::removePlacementFactory: no such factory '" << type << "'"
+ );
+ else
+ _placement_factories.erase(it);
+ }
- _root_group.reset( new Group(canvas, _node) );
- // Remove automatically created property listener as we forward them on our
- // own
- _root_group->removeListener();
+ //----------------------------------------------------------------------------
+ void Canvas::setSystemAdapter(const SystemAdapterPtr& system_adapter)
+ {
+ _system_adapter = system_adapter;
+ }
- _cull_callback = new CullCallback(canvas);
+ //----------------------------------------------------------------------------
+ SystemAdapterPtr Canvas::getSystemAdapter()
+ {
+ return _system_adapter;
}
//----------------------------------------------------------------------------
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";
_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";
//----------------------------------------------------------------------------
Canvas::PlacementFactoryMap Canvas::_placement_factories;
+ SystemAdapterPtr Canvas::_system_adapter;
} // namespace canvas
} // namespace simgear