From 46442ef50c5a2b7c8e41e5c025f86c1cd35e6e15 Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Sun, 2 Dec 2012 13:19:35 +0100 Subject: [PATCH] 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) --- simgear/canvas/Canvas.cxx | 9 +++++ simgear/canvas/Canvas.hxx | 2 + simgear/canvas/CanvasEvent.cxx | 37 ++++++++++++++++++- simgear/canvas/CanvasEvent.hxx | 12 +++++- simgear/canvas/CanvasEventListener.cxx | 16 +++++--- simgear/canvas/CanvasEventListener.hxx | 2 +- simgear/canvas/CanvasEventTypes.hxx | 2 + simgear/canvas/CanvasEventVisitor.cxx | 28 ++++++++++++-- simgear/canvas/CanvasEventVisitor.hxx | 2 +- simgear/canvas/CanvasSystemAdapter.hxx | 4 +- simgear/canvas/MouseEvent.hxx | 6 +++ simgear/canvas/elements/CanvasElement.cxx | 45 ++++++++++++++++++----- simgear/canvas/elements/CanvasElement.hxx | 4 +- simgear/nasal/cppbind/Ghost.hxx | 6 ++- simgear/nasal/cppbind/cppbind_test.cxx | 6 ++- 15 files changed, 151 insertions(+), 30 deletions(-) diff --git a/simgear/canvas/Canvas.cxx b/simgear/canvas/Canvas.cxx index d2dc2358..58c115a8 100644 --- a/simgear/canvas/Canvas.cxx +++ b/simgear/canvas/Canvas.cxx @@ -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) { diff --git a/simgear/canvas/Canvas.hxx b/simgear/canvas/Canvas.hxx index 4ad23527..594d67cf 100644 --- a/simgear/canvas/Canvas.hxx +++ b/simgear/canvas/Canvas.hxx @@ -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); diff --git a/simgear/canvas/CanvasEvent.cxx b/simgear/canvas/CanvasEvent.cxx index 36b73ecf..1e9815e8 100644 --- a/simgear/canvas/CanvasEvent.cxx +++ b/simgear/canvas/CanvasEvent.cxx @@ -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 // @@ -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 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 diff --git a/simgear/canvas/CanvasEvent.hxx b/simgear/canvas/CanvasEvent.hxx index b8babfb5..9ac9d475 100644 --- a/simgear/canvas/CanvasEvent.hxx +++ b/simgear/canvas/CanvasEvent.hxx @@ -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 // @@ -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); }; diff --git a/simgear/canvas/CanvasEventListener.cxx b/simgear/canvas/CanvasEventListener.cxx index b94928b2..70f458fb 100644 --- a/simgear/canvas/CanvasEventListener.cxx +++ b/simgear/canvas/CanvasEventListener.cxx @@ -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 +#include 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::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()); } diff --git a/simgear/canvas/CanvasEventListener.hxx b/simgear/canvas/CanvasEventListener.hxx index 022b76a1..b8e39c50 100644 --- a/simgear/canvas/CanvasEventListener.hxx +++ b/simgear/canvas/CanvasEventListener.hxx @@ -34,7 +34,7 @@ namespace canvas const SystemAdapterPtr& sys_adapter ); ~EventListener(); - void call(); + void call(const canvas::EventPtr& event); protected: naRef _code; diff --git a/simgear/canvas/CanvasEventTypes.hxx b/simgear/canvas/CanvasEventTypes.hxx index 6e428ee5..78c772a7 100644 --- a/simgear/canvas/CanvasEventTypes.hxx +++ b/simgear/canvas/CanvasEventTypes.hxx @@ -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") diff --git a/simgear/canvas/CanvasEventVisitor.cxx b/simgear/canvas/CanvasEventVisitor.cxx index 9970d8d9..b74ee171 100644 --- a/simgear/canvas/CanvasEventVisitor.cxx +++ b/simgear/canvas/CanvasEventVisitor.cxx @@ -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 // @@ -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; } diff --git a/simgear/canvas/CanvasEventVisitor.hxx b/simgear/canvas/CanvasEventVisitor.hxx index 415641b0..e12b9088 100644 --- a/simgear/canvas/CanvasEventVisitor.hxx +++ b/simgear/canvas/CanvasEventVisitor.hxx @@ -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 // diff --git a/simgear/canvas/CanvasSystemAdapter.hxx b/simgear/canvas/CanvasSystemAdapter.hxx index 6a608d9a..a2bb1fe2 100644 --- a/simgear/canvas/CanvasSystemAdapter.hxx +++ b/simgear/canvas/CanvasSystemAdapter.hxx @@ -20,7 +20,7 @@ #define SG_CANVAS_SYSTEM_ADAPTER_HXX_ #include "canvas_fwd.hxx" -#include +#include 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. diff --git a/simgear/canvas/MouseEvent.hxx b/simgear/canvas/MouseEvent.hxx index 39a6404d..3193f278 100644 --- a/simgear/canvas/MouseEvent.hxx +++ b/simgear/canvas/MouseEvent.hxx @@ -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, // +#include #include #include @@ -24,6 +26,7 @@ #include #include +#include #include #include @@ -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(0); + naRef code = ctx.requireArg(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( 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 ) { diff --git a/simgear/canvas/elements/CanvasElement.hxx b/simgear/canvas/elements/CanvasElement.hxx index bf71a4cd..0f4b31ed 100644 --- a/simgear/canvas/elements/CanvasElement.hxx +++ b/simgear/canvas/elements/CanvasElement.hxx @@ -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, _1)); } - void callListeners(canvas::Event& event); - virtual void childAdded(SGPropertyNode * child) {} virtual void childRemoved(SGPropertyNode * child){} virtual void childChanged(SGPropertyNode * child){} diff --git a/simgear/nasal/cppbind/Ghost.hxx b/simgear/nasal/cppbind/Ghost.hxx index bc32f9ed..d2a5d22c 100644 --- a/simgear/nasal/cppbind/Ghost.hxx +++ b/simgear/nasal/cppbind/Ghost.hxx @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -457,12 +458,13 @@ namespace nasal template 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::param_type) = 0 ) { member_t m; if( getter ) { - naRef (*to_nasal_)(naContext, Var) = &nasal::to_nasal; + typedef typename boost::call_traits::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)); diff --git a/simgear/nasal/cppbind/cppbind_test.cxx b/simgear/nasal/cppbind/cppbind_test.cxx index ef1b4674..47a437bc 100644 --- a/simgear/nasal/cppbind/cppbind_test.cxx +++ b/simgear/nasal/cppbind/cppbind_test.cxx @@ -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::init("Base") - .method<&Base::member>("member"); + .method<&Base::member>("member") + .member("str", &Base::getString, &Base::setString); Ghost::init("Derived") .bases() .member("x", &Derived::getX, &Derived::setX) -- 2.39.5