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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace canvas
|
||||
const SystemAdapterPtr& sys_adapter );
|
||||
~EventListener();
|
||||
|
||||
void call();
|
||||
void call(const canvas::EventPtr& event);
|
||||
|
||||
protected:
|
||||
naRef _code;
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
//
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 )
|
||||
{
|
||||
|
||||
@@ -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){}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user