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:
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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,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>
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 )
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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){}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user