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:22:37 +01:00
parent 7d8fde82e6
commit 46442ef50c
15 changed files with 151 additions and 30 deletions
+9
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) void Canvas::setSizeX(int sx)
{ {
+2
View File
@@ -103,6 +103,8 @@ namespace canvas
void update(double delta_time_sec); void update(double delta_time_sec);
naRef addEventListener(const nasal::CallContext& ctx);
void setSizeX(int sx); void setSizeX(int sx);
void setSizeY(int sy); void setSizeY(int sy);
+35 -2
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> // Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
// //
@@ -25,7 +25,14 @@ namespace canvas
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
Event::Event(): Event::Event():
type(UNKNOWN) type(UNKNOWN),
propagation_stopped(false)
{
}
//----------------------------------------------------------------------------
Event::~Event()
{ {
} }
@@ -55,5 +62,31 @@ namespace canvas
return target; 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 canvas
} // namespace simgear } // namespace simgear
+11 -1
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> // Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
// //
@@ -42,11 +42,21 @@ namespace canvas
Type type; Type type;
ElementWeakPtr target; ElementWeakPtr target;
bool propagation_stopped;
Event(); Event();
// We need a vtable to allow nasal::Ghost to determine the dynamic type
// of the actual event instances.
virtual ~Event();
Type getType() const; Type getType() const;
std::string getTypeString() const; std::string getTypeString() const;
ElementWeakPtr getTarget() const; ElementWeakPtr getTarget() const;
void stopPropagation();
static Type strToType(const std::string& str);
}; };
+10 -6
View File
@@ -16,10 +16,11 @@
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasEvent.hxx"
#include "CanvasEventListener.hxx" #include "CanvasEventListener.hxx"
#include "CanvasSystemAdapter.hxx" #include "CanvasSystemAdapter.hxx"
#include <simgear/nasal/nasal.h> #include <simgear/nasal/cppbind/Ghost.hxx>
namespace simgear namespace simgear
{ {
@@ -52,13 +53,16 @@ namespace canvas
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void EventListener::call() void EventListener::call(const canvas::EventPtr& event)
{ {
const size_t num_args = 1; SystemAdapterPtr sys = _sys.lock();
naRef args[num_args] = {
naNil() 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());
} }
+1 -1
View File
@@ -34,7 +34,7 @@ namespace canvas
const SystemAdapterPtr& sys_adapter ); const SystemAdapterPtr& sys_adapter );
~EventListener(); ~EventListener();
void call(); void call(const canvas::EventPtr& event);
protected: protected:
naRef _code; naRef _code;
+2
View File
@@ -24,6 +24,8 @@ ENUM_MAPPING(MOUSE_DOWN, "mousedown")
ENUM_MAPPING(MOUSE_UP, "mouseup") ENUM_MAPPING(MOUSE_UP, "mouseup")
ENUM_MAPPING(CLICK, "click") ENUM_MAPPING(CLICK, "click")
ENUM_MAPPING(DBL_CLICK, "dblclick") ENUM_MAPPING(DBL_CLICK, "dblclick")
ENUM_MAPPING(DRAG, "drag")
ENUM_MAPPING(WHEEL, "wheel")
ENUM_MAPPING(MOUSE_MOVE, "mousemove") ENUM_MAPPING(MOUSE_MOVE, "mousemove")
ENUM_MAPPING(MOUSE_OVER, "mouseover") ENUM_MAPPING(MOUSE_OVER, "mouseover")
ENUM_MAPPING(MOUSE_OUT, "mouseout") ENUM_MAPPING(MOUSE_OUT, "mouseout")
+24 -4
View File
@@ -1,5 +1,5 @@
// Visitor for traversing a canvas element hierarchy similar to the traversal // 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> // 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) 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; return false;
const osg::Vec2f& delta = _target_path.back().local_delta; const osg::Vec2f& delta = _target_path.back().local_delta;
@@ -83,7 +86,7 @@ namespace canvas
EventTarget target = {&el, local_pos, local_delta}; EventTarget target = {&el, local_pos, local_delta};
_target_path.push_back(target); _target_path.push_back(target);
if( el.traverse(*this) ) if( el.traverse(*this) || _target_path.size() <= 2 )
return true; return true;
_target_path.pop_back(); _target_path.pop_back();
@@ -96,7 +99,10 @@ namespace canvas
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
bool EventVisitor::propagateEvent(const EventPtr& event) 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(); // for( EventTargets::iterator it = _target_path.begin();
// it != _target_path.end(); // it != _target_path.end();
// ++it ) // ++it )
@@ -106,6 +112,20 @@ namespace canvas
// << "(" << it->local_pos.x() << "|" << it->local_pos.y() << ")\n"; // << "(" << 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; return true;
} }
+1 -1
View File
@@ -1,5 +1,5 @@
// Visitor for traversing a canvas element hierarchy similar to the traversal // 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> // Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
// //
+3 -1
View File
@@ -20,7 +20,7 @@
#define SG_CANVAS_SYSTEM_ADAPTER_HXX_ #define SG_CANVAS_SYSTEM_ADAPTER_HXX_
#include "canvas_fwd.hxx" #include "canvas_fwd.hxx"
#include <simgear/nasal/naref.h> #include <simgear/nasal/nasal.h>
namespace simgear namespace simgear
{ {
@@ -37,6 +37,8 @@ namespace canvas
virtual void removeCamera(osg::Camera* camera) const = 0; virtual void removeCamera(osg::Camera* camera) const = 0;
virtual osg::Image* getImage(const std::string& path) 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 * Save passed reference to Nasal object from being deleted by the
* garbage collector. * garbage collector.
+6
View File
@@ -41,6 +41,12 @@ namespace canvas
osg::Vec3f getPos3() const { return osg::Vec3f(pos, 0); } osg::Vec3f getPos3() const { return osg::Vec3f(pos, 0); }
osg::Vec2f getDelta() const { return delta; } 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, osg::Vec2f pos,
delta; delta;
int button, //<! Button for this event int button, //<! Button for this event
+36 -9
View File
@@ -17,6 +17,8 @@
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasElement.hxx" #include "CanvasElement.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/canvas/CanvasEventListener.hxx>
#include <simgear/canvas/CanvasEventVisitor.hxx> #include <simgear/canvas/CanvasEventVisitor.hxx>
#include <simgear/canvas/MouseEvent.hxx> #include <simgear/canvas/MouseEvent.hxx>
@@ -24,6 +26,7 @@
#include <osg/Geode> #include <osg/Geode>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
@@ -112,7 +115,28 @@ namespace canvas
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
naRef Element::addEventListener(const nasal::CallContext& ctx) 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(); return naNil();
} }
@@ -148,6 +172,17 @@ namespace canvas
return true; 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 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 ) void Element::setDrawable( osg::Drawable* drawable )
{ {
+2 -2
View File
@@ -78,6 +78,8 @@ namespace canvas
virtual bool ascend(EventVisitor& visitor); virtual bool ascend(EventVisitor& visitor);
virtual bool traverse(EventVisitor& visitor); virtual bool traverse(EventVisitor& visitor);
void callListeners(const canvas::EventPtr& event);
virtual bool hitBound(const osg::Vec2f& pos) const; virtual bool hitBound(const osg::Vec2f& pos) const;
@@ -168,8 +170,6 @@ namespace canvas
return boost::bind(setter, instance, boost::bind(&getValue<T1>, _1)); return boost::bind(setter, instance, boost::bind(&getValue<T1>, _1));
} }
void callListeners(canvas::Event& event);
virtual void childAdded(SGPropertyNode * child) {} virtual void childAdded(SGPropertyNode * child) {}
virtual void childRemoved(SGPropertyNode * child){} virtual void childRemoved(SGPropertyNode * child){}
virtual void childChanged(SGPropertyNode * child){} virtual void childChanged(SGPropertyNode * child){}
+4 -2
View File
@@ -26,6 +26,7 @@
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <boost/call_traits.hpp>
#include <boost/function.hpp> #include <boost/function.hpp>
#include <boost/lambda/lambda.hpp> #include <boost/lambda/lambda.hpp>
#include <boost/utility/enable_if.hpp> #include <boost/utility/enable_if.hpp>
@@ -457,12 +458,13 @@ namespace nasal
template<class Var> template<class Var>
Ghost& member( const std::string& field, Ghost& member( const std::string& field,
Var (raw_type::*getter)() const, 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; member_t m;
if( getter ) 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&) // Getter signature: naRef(naContext, raw_type&)
m.getter = boost::bind(to_nasal_, _1, boost::bind(getter, _2)); m.getter = boost::bind(to_nasal_, _1, boost::bind(getter, _2));
+5 -1
View File
@@ -17,6 +17,9 @@ struct Base
{ {
naRef member(const nasal::CallContext&) { return naNil(); } naRef member(const nasal::CallContext&) { return naNil(); }
virtual ~Base(){}; virtual ~Base(){};
std::string getString() const { return ""; }
void setString(const std::string&) {}
}; };
struct Derived: struct Derived:
public Base public Base
@@ -96,7 +99,8 @@ int main(int argc, char* argv[])
mod.set("parent", hash); mod.set("parent", hash);
Ghost<Base>::init("Base") Ghost<Base>::init("Base")
.method<&Base::member>("member"); .method<&Base::member>("member")
.member("str", &Base::getString, &Base::setString);
Ghost<Derived>::init("Derived") Ghost<Derived>::init("Derived")
.bases<Base>() .bases<Base>()
.member("x", &Derived::getX, &Derived::setX) .member("x", &Derived::getX, &Derived::setX)