First working version of DOM like Canvas event handling

- Now it is possible to attach listeners to canvas
   elements and also the canvas itself, which get
   called upon different mouse events (Currently
   only basic mouse events supported. click/dblclick
   etc. are missing)
This commit is contained in:
Thomas Geymayer
2012-12-02 13:19:35 +01:00
parent 7d8fde82e6
commit 46442ef50c
15 changed files with 151 additions and 30 deletions

View File

@@ -256,6 +256,15 @@ 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)
{

View File

@@ -103,6 +103,8 @@ namespace canvas
void update(double delta_time_sec);
naRef addEventListener(const nasal::CallContext& ctx);
void setSizeX(int sx);
void setSizeY(int sy);

View File

@@ -1,4 +1,4 @@
// Canvas Event for event model similar to DOM Level 2 Event Model
// Canvas Event for event model similar to DOM Level 3 Event Model
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -25,7 +25,14 @@ namespace canvas
//----------------------------------------------------------------------------
Event::Event():
type(UNKNOWN)
type(UNKNOWN),
propagation_stopped(false)
{
}
//----------------------------------------------------------------------------
Event::~Event()
{
}
@@ -55,5 +62,31 @@ namespace canvas
return target;
}
//----------------------------------------------------------------------------
void Event::stopPropagation()
{
propagation_stopped = true;
}
//----------------------------------------------------------------------------
Event::Type Event::strToType(const std::string& str)
{
typedef std::map<std::string, Type> TypeMap;
static TypeMap type_map;
if( type_map.empty() )
{
# define ENUM_MAPPING(type, str) type_map[ str ] = type;
# include "CanvasEventTypes.hxx"
# undef ENUM_MAPPING
}
TypeMap::const_iterator it = type_map.find(str);
if( it == type_map.end() )
return UNKNOWN;
return it->second;
}
} // namespace canvas
} // namespace simgear

View File

@@ -1,4 +1,4 @@
// Canvas Event for event model similar to DOM Level 2 Event Model
// Canvas Event for event model similar to DOM Level 3 Event Model
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -42,11 +42,21 @@ namespace canvas
Type type;
ElementWeakPtr target;
bool propagation_stopped;
Event();
// We need a vtable to allow nasal::Ghost to determine the dynamic type
// of the actual event instances.
virtual ~Event();
Type getType() const;
std::string getTypeString() const;
ElementWeakPtr getTarget() const;
void stopPropagation();
static Type strToType(const std::string& str);
};

View File

@@ -16,10 +16,11 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasEvent.hxx"
#include "CanvasEventListener.hxx"
#include "CanvasSystemAdapter.hxx"
#include <simgear/nasal/nasal.h>
#include <simgear/nasal/cppbind/Ghost.hxx>
namespace simgear
{
@@ -52,13 +53,16 @@ namespace canvas
}
//------------------------------------------------------------------------------
void EventListener::call()
void EventListener::call(const canvas::EventPtr& event)
{
const size_t num_args = 1;
naRef args[num_args] = {
naNil()
SystemAdapterPtr sys = _sys.lock();
naRef args[] = {
nasal::Ghost<EventPtr>::create(sys->getNasalContext(), event)
};
_sys.lock()->callMethod(_code, naNil(), num_args, args, naNil());
const int num_args = sizeof(args)/sizeof(args[0]);
sys->callMethod(_code, naNil(), num_args, args, naNil());
}

View File

@@ -34,7 +34,7 @@ namespace canvas
const SystemAdapterPtr& sys_adapter );
~EventListener();
void call();
void call(const canvas::EventPtr& event);
protected:
naRef _code;

View File

@@ -24,6 +24,8 @@ ENUM_MAPPING(MOUSE_DOWN, "mousedown")
ENUM_MAPPING(MOUSE_UP, "mouseup")
ENUM_MAPPING(CLICK, "click")
ENUM_MAPPING(DBL_CLICK, "dblclick")
ENUM_MAPPING(DRAG, "drag")
ENUM_MAPPING(WHEEL, "wheel")
ENUM_MAPPING(MOUSE_MOVE, "mousemove")
ENUM_MAPPING(MOUSE_OVER, "mouseover")
ENUM_MAPPING(MOUSE_OUT, "mouseout")

View File

@@ -1,5 +1,5 @@
// Visitor for traversing a canvas element hierarchy similar to the traversal
// of the DOM Level 2 Event Model
// of the DOM Level 3 Event Model
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
@@ -70,7 +70,10 @@ namespace canvas
m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1)
);
if( !el.hitBound(local_pos) )
// Don't check collision with root element (2nd element in _target_path)
// do event listeners attached to the canvas itself (its root group)
// always get called even if no element has been hit.
if( _target_path.size() > 2 && !el.hitBound(local_pos) )
return false;
const osg::Vec2f& delta = _target_path.back().local_delta;
@@ -83,7 +86,7 @@ namespace canvas
EventTarget target = {&el, local_pos, local_delta};
_target_path.push_back(target);
if( el.traverse(*this) )
if( el.traverse(*this) || _target_path.size() <= 2 )
return true;
_target_path.pop_back();
@@ -96,7 +99,10 @@ namespace canvas
//----------------------------------------------------------------------------
bool EventVisitor::propagateEvent(const EventPtr& event)
{
// std::cout << "Propagate event " << event->getTypeString() << "\n";
// Event propagation similar to DOM Level 3 event flow:
// http://www.w3.org/TR/DOM-Level-3-Events/#event-flow
// Capturing phase
// for( EventTargets::iterator it = _target_path.begin();
// it != _target_path.end();
// ++it )
@@ -106,6 +112,20 @@ namespace canvas
// << "(" << it->local_pos.x() << "|" << it->local_pos.y() << ")\n";
// }
// Bubbling phase
for( EventTargets::reverse_iterator it = _target_path.rbegin();
it != _target_path.rend();
++it )
{
if( !it->element )
continue;
it->element->callListeners(event);
if( event->propagation_stopped )
return true;
}
return true;
}

View File

@@ -1,5 +1,5 @@
// Visitor for traversing a canvas element hierarchy similar to the traversal
// of the DOM Level 2 Event Model
// of the DOM Level 3 Event Model
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//

View File

@@ -20,7 +20,7 @@
#define SG_CANVAS_SYSTEM_ADAPTER_HXX_
#include "canvas_fwd.hxx"
#include <simgear/nasal/naref.h>
#include <simgear/nasal/nasal.h>
namespace simgear
{
@@ -37,6 +37,8 @@ namespace canvas
virtual void removeCamera(osg::Camera* camera) const = 0;
virtual osg::Image* getImage(const std::string& path) const = 0;
virtual naContext getNasalContext() const = 0;
/**
* Save passed reference to Nasal object from being deleted by the
* garbage collector.

View File

@@ -41,6 +41,12 @@ namespace canvas
osg::Vec3f getPos3() const { return osg::Vec3f(pos, 0); }
osg::Vec2f getDelta() const { return delta; }
float getPosX() const { return pos.x(); }
float getPosY() const { return pos.y(); }
float getDeltaX() const { return delta.x(); }
float getDeltaY() const { return delta.y(); }
osg::Vec2f pos,
delta;
int button, //<! Button for this event

View File

@@ -17,6 +17,8 @@
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasElement.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/canvas/CanvasEventListener.hxx>
#include <simgear/canvas/CanvasEventVisitor.hxx>
#include <simgear/canvas/MouseEvent.hxx>
@@ -24,6 +26,7 @@
#include <osg/Geode>
#include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
#include <cassert>
#include <cstring>
@@ -112,7 +115,28 @@ namespace canvas
//----------------------------------------------------------------------------
naRef Element::addEventListener(const nasal::CallContext& ctx)
{
std::cout << "addEventListener " << _node->getPath() << std::endl;
const std::string type_str = ctx.requireArg<std::string>(0);
naRef code = ctx.requireArg<naRef>(1);
SG_LOG
(
SG_NASAL,
SG_INFO,
"addEventListener(" << _node->getPath() << ", " << type_str << ")"
);
Event::Type type = Event::strToType(type_str);
if( type == Event::UNKNOWN )
naRuntimeError( ctx.c,
"addEventListener: Unknown event type %s",
type_str.c_str() );
_listener[ type ].push_back
(
boost::make_shared<EventListener>( code,
_canvas.lock()->getSystemAdapter() )
);
return naNil();
}
@@ -148,6 +172,17 @@ namespace canvas
return true;
}
//----------------------------------------------------------------------------
void Element::callListeners(const canvas::EventPtr& event)
{
ListenerMap::iterator listeners = _listener.find(event->getType());
if( listeners == _listener.end() )
return;
BOOST_FOREACH(EventListenerPtr listener, listeners->second)
listener->call(event);
}
//----------------------------------------------------------------------------
bool Element::hitBound(const osg::Vec2f& pos) const
{
@@ -295,14 +330,6 @@ namespace canvas
);
}
//----------------------------------------------------------------------------
void Element::callListeners(canvas::Event& event)
{
ListenerMap::iterator listeners = _listener.find(event.getType());
if( listeners == _listener.end() )
return;
}
//----------------------------------------------------------------------------
void Element::setDrawable( osg::Drawable* drawable )
{

View File

@@ -78,6 +78,8 @@ namespace canvas
virtual bool ascend(EventVisitor& visitor);
virtual bool traverse(EventVisitor& visitor);
void callListeners(const canvas::EventPtr& event);
virtual bool hitBound(const osg::Vec2f& pos) const;
@@ -168,8 +170,6 @@ namespace canvas
return boost::bind(setter, instance, boost::bind(&getValue<T1>, _1));
}
void callListeners(canvas::Event& event);
virtual void childAdded(SGPropertyNode * child) {}
virtual void childRemoved(SGPropertyNode * child){}
virtual void childChanged(SGPropertyNode * child){}

View File

@@ -26,6 +26,7 @@
#include <simgear/debug/logstream.hxx>
#include <boost/bind.hpp>
#include <boost/call_traits.hpp>
#include <boost/function.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/utility/enable_if.hpp>
@@ -457,12 +458,13 @@ namespace nasal
template<class Var>
Ghost& member( const std::string& field,
Var (raw_type::*getter)() const,
void (raw_type::*setter)(Var) = 0 )
void (raw_type::*setter)(typename boost::call_traits<Var>::param_type) = 0 )
{
member_t m;
if( getter )
{
naRef (*to_nasal_)(naContext, Var) = &nasal::to_nasal;
typedef typename boost::call_traits<Var>::param_type param_type;
naRef (*to_nasal_)(naContext, param_type) = &nasal::to_nasal;
// Getter signature: naRef(naContext, raw_type&)
m.getter = boost::bind(to_nasal_, _1, boost::bind(getter, _2));

View File

@@ -17,6 +17,9 @@ struct Base
{
naRef member(const nasal::CallContext&) { return naNil(); }
virtual ~Base(){};
std::string getString() const { return ""; }
void setString(const std::string&) {}
};
struct Derived:
public Base
@@ -96,7 +99,8 @@ int main(int argc, char* argv[])
mod.set("parent", hash);
Ghost<Base>::init("Base")
.method<&Base::member>("member");
.method<&Base::member>("member")
.member("str", &Base::getString, &Base::setString);
Ghost<Derived>::init("Derived")
.bases<Base>()
.member("x", &Derived::getX, &Derived::setX)