From f28e3fc9bbbdade41053e024bbbc9d49cb7202f4 Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Sat, 15 Mar 2014 14:39:23 +0100 Subject: [PATCH] Canvas: fix position for drag events and multiclick handling. - Calculate local/client position for every drag event instead of just reporting the position of the initial mousedown event. - Only pressing the same button multiple times increases the mouse clickcount. --- simgear/canvas/Canvas.cxx | 1 - simgear/canvas/CanvasEventManager.cxx | 71 +++++++++++++---------- simgear/canvas/CanvasEventManager.hxx | 27 ++++++--- simgear/canvas/CanvasEventVisitor.cxx | 20 +------ simgear/canvas/CanvasEventVisitor.hxx | 2 - simgear/canvas/elements/CanvasElement.cxx | 12 ++++ simgear/canvas/elements/CanvasElement.hxx | 5 ++ 7 files changed, 79 insertions(+), 59 deletions(-) diff --git a/simgear/canvas/Canvas.cxx b/simgear/canvas/Canvas.cxx index 5a1e09cb..5f13e28c 100644 --- a/simgear/canvas/Canvas.cxx +++ b/simgear/canvas/Canvas.cxx @@ -414,7 +414,6 @@ namespace canvas EventVisitor visitor( EventVisitor::TRAVERSE_DOWN, event->getClientPos(), - event->getDelta(), _root_group ); if( !_root_group->accept(visitor) ) return false; diff --git a/simgear/canvas/CanvasEventManager.cxx b/simgear/canvas/CanvasEventManager.cxx index 0033ec62..21d97c78 100644 --- a/simgear/canvas/CanvasEventManager.cxx +++ b/simgear/canvas/CanvasEventManager.cxx @@ -61,9 +61,18 @@ namespace canvas return !path.empty() && time > 0; } + //---------------------------------------------------------------------------- + void EventManager::MouseEventInfo::set( const MouseEventPtr& event, + const EventPropagationPath& p ) + { + path = p; + time = event->time; + button = event->button; + pos = event->screen_pos; + } + //---------------------------------------------------------------------------- EventManager::EventManager(): - _last_button_down(0), _current_click_count(0) { @@ -77,8 +86,7 @@ namespace canvas switch( event->type ) { case Event::MOUSE_DOWN: - _last_mouse_down = StampedPropagationPath(path, event->getTime()); - _last_button_down = event->button; + _last_mouse_down.set(event, path); break; case Event::MOUSE_UP: { @@ -91,12 +99,12 @@ namespace canvas // normal mouseup handled |= propagateEvent(event, path); - if( _last_mouse_down.path.empty() ) + if( !_last_mouse_down.valid() ) // Ignore mouse up without any previous mouse down return handled; // now handle click/dblclick - if( checkClickDistance(path, _last_mouse_down.path) ) + if( checkClickDistance(event->screen_pos, _last_mouse_down.pos) ) handled |= handleClick(event, getCommonAncestor(_last_mouse_down.path, path)); @@ -110,7 +118,7 @@ namespace canvas else { // OSG does not set button for drag events. - event->button = _last_button_down; + event->button = _last_mouse_down.button; return propagateEvent(event, _last_mouse_down.path); } case Event::MOUSE_MOVE: @@ -152,8 +160,10 @@ namespace canvas if( _current_click_count > 1 ) { - // Reset current click count if moved too far - if( !checkClickDistance(path, _last_click.path) ) + // Reset current click count if moved too far or different button has + // been clicked + if( !checkClickDistance(event->screen_pos, _last_click.pos) + || _last_click.button != event->button ) _current_click_count = 1; } } @@ -173,7 +183,7 @@ namespace canvas handled |= propagateEvent( dbl_click, getCommonAncestor(_last_click.path, path) ); - _last_click = StampedPropagationPath(path, event->getTime()); + _last_click.set(event, path); return handled; } @@ -249,14 +259,24 @@ namespace canvas // Event propagation similar to DOM Level 3 event flow: // http://www.w3.org/TR/DOM-Level-3-Events/#event-flow - // Capturing phase -// for( EventPropagationPath::const_iterator it = path.begin(); -// it != path.end(); -// ++it ) -// { -// if( !it->element.expired() ) -// std::cout << it->element.lock()->getProps()->getPath() << std::endl; -// } + // Position update only needed for drag event (as event needs to be + // delivered to element of initial mousedown, but with update positions) + if( mouse_event && mouse_event->type == MouseEvent::DRAG ) + { + osg::Vec2f local_pos = mouse_event->client_pos; + + // Capturing phase (currently just update position) + for( EventPropagationPath::const_iterator it = path.begin(); + it != path.end(); + ++it ) + { + ElementPtr el = it->element.lock(); + if( !el ) + continue; + + it->local_pos = local_pos = el->posToLocal(local_pos); + } + } // Check if event supports bubbling const Event::Type types_no_bubbling[] = { @@ -294,17 +314,8 @@ namespace canvas // TODO provide functions to convert delta to local coordinates on demand. // Maybe also provide a clone method for events as local coordinates // might differ between different elements receiving the same event. - if( mouse_event ) //&& event->type != Event::DRAG ) - { - // TODO transform pos and delta for drag events. Maybe we should just - // store the global coordinates and convert to local coordinates - // on demand. - - // Position and delta are specified in local coordinate system of - // current element + if( mouse_event ) mouse_event->local_pos = it->local_pos; - //mouse_event->delta = it->local_delta; - } event->current_target = el; el->handleEvent(event); @@ -318,10 +329,10 @@ namespace canvas //---------------------------------------------------------------------------- bool - EventManager::checkClickDistance( const EventPropagationPath& path1, - const EventPropagationPath& path2 ) const + EventManager::checkClickDistance( const osg::Vec2f& pos1, + const osg::Vec2f& pos2 ) const { - osg::Vec2 delta = path1.front().local_pos - path2.front().local_pos; + osg::Vec2 delta = pos1 - pos2; return std::fabs(delta.x()) < drag_threshold && std::fabs(delta.y()) < drag_threshold; } diff --git a/simgear/canvas/CanvasEventManager.hxx b/simgear/canvas/CanvasEventManager.hxx index 3455f5f8..8ea44d9a 100644 --- a/simgear/canvas/CanvasEventManager.hxx +++ b/simgear/canvas/CanvasEventManager.hxx @@ -29,9 +29,10 @@ namespace canvas struct EventTarget { - ElementWeakPtr element; - osg::Vec2f local_pos, - local_delta; + ElementWeakPtr element; + + // Used as storage by EventManager during event propagation + mutable osg::Vec2f local_pos; }; typedef std::deque EventPropagationPath; inline bool operator==(const EventTarget& t1, const EventTarget& t2) @@ -62,12 +63,20 @@ namespace canvas // TODO if we really need the paths modify to not copy around the paths // that much. - StampedPropagationPath _last_mouse_down, - _last_click, - _last_mouse_over; - int _last_button_down; + StampedPropagationPath _last_mouse_over; size_t _current_click_count; + struct MouseEventInfo: + public StampedPropagationPath + { + int button; + osg::Vec2f pos; + + void set( const MouseEventPtr& event, + const EventPropagationPath& path ); + } _last_mouse_down, + _last_click; + /** * Propagate click event and handle multi-click (eg. create dblclick) */ @@ -88,8 +97,8 @@ namespace canvas * clicks) are inside a maximum distance to still create a click or * dblclick event respectively. */ - bool checkClickDistance( const EventPropagationPath& path1, - const EventPropagationPath& path2 ) const; + bool checkClickDistance( const osg::Vec2f& pos1, + const osg::Vec2f& pos2 ) const; EventPropagationPath getCommonAncestor( const EventPropagationPath& path1, const EventPropagationPath& path2 ) const; diff --git a/simgear/canvas/CanvasEventVisitor.cxx b/simgear/canvas/CanvasEventVisitor.cxx index 6ba90a9f..9fbae2c8 100644 --- a/simgear/canvas/CanvasEventVisitor.cxx +++ b/simgear/canvas/CanvasEventVisitor.cxx @@ -30,14 +30,13 @@ namespace canvas //---------------------------------------------------------------------------- EventVisitor::EventVisitor( TraverseMode mode, const osg::Vec2f& pos, - const osg::Vec2f& delta, const ElementPtr& root ): _traverse_mode( mode ), _root(root) { if( mode == TRAVERSE_DOWN ) { - EventTarget target = {ElementWeakPtr(), pos, delta}; + EventTarget target = {ElementWeakPtr(), pos}; _target_path.push_back(target); } } @@ -63,14 +62,8 @@ namespace canvas // We only need to check for hits while traversing down if( _traverse_mode == TRAVERSE_DOWN ) { - // Transform event to local coordinates - const osg::Matrix& m = el.getMatrixTransform()->getInverseMatrix(); const osg::Vec2f& pos = _target_path.back().local_pos; - const osg::Vec2f local_pos - ( - m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0), - m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1) - ); + const osg::Vec2f local_pos = el.posToLocal(pos); // Don't check specified root element for collision, as its purpose is to // catch all events which have no target. This allows for example calling @@ -79,14 +72,7 @@ namespace canvas if( _root.get() != &el && !el.hitBound(pos, local_pos) ) return false; - const osg::Vec2f& delta = _target_path.back().local_delta; - const osg::Vec2f local_delta - ( - m(0, 0) * delta[0] + m(1, 0) * delta[1], - m(0, 1) * delta[0] + m(1, 1) * delta[1] - ); - - EventTarget target = {el.getWeakPtr(), local_pos, local_delta}; + EventTarget target = {el.getWeakPtr(), local_pos}; _target_path.push_back(target); if( el.traverse(*this) || &el == _root.get() ) diff --git a/simgear/canvas/CanvasEventVisitor.hxx b/simgear/canvas/CanvasEventVisitor.hxx index a82f0928..84b304a5 100644 --- a/simgear/canvas/CanvasEventVisitor.hxx +++ b/simgear/canvas/CanvasEventVisitor.hxx @@ -42,12 +42,10 @@ namespace canvas * * @param mode * @param pos Mouse position - * @param delta Mouse movement since last mouse move event * @param root Element to dispatch events to if no element is hit */ EventVisitor( TraverseMode mode, const osg::Vec2f& pos, - const osg::Vec2f& delta, const ElementPtr& root = ElementPtr() ); virtual ~EventVisitor(); virtual bool traverse(Element& el); diff --git a/simgear/canvas/elements/CanvasElement.cxx b/simgear/canvas/elements/CanvasElement.cxx index 44dffc54..4b199f04 100644 --- a/simgear/canvas/elements/CanvasElement.cxx +++ b/simgear/canvas/elements/CanvasElement.cxx @@ -303,6 +303,18 @@ namespace canvas return _transform.get(); } + //---------------------------------------------------------------------------- + osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const + { + getMatrix(); + const osg::Matrix& m = _transform->getInverseMatrix(); + return osg::Vec2f + ( + m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0), + m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1) + ); + } + //---------------------------------------------------------------------------- void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child) { diff --git a/simgear/canvas/elements/CanvasElement.hxx b/simgear/canvas/elements/CanvasElement.hxx index 79a6265b..fb00d645 100644 --- a/simgear/canvas/elements/CanvasElement.hxx +++ b/simgear/canvas/elements/CanvasElement.hxx @@ -129,6 +129,11 @@ namespace canvas osg::MatrixTransform* getMatrixTransform(); osg::MatrixTransform const* getMatrixTransform() const; + /** + * Transform position to local coordinages. + */ + osg::Vec2f posToLocal(const osg::Vec2f& pos) const; + virtual void childAdded( SGPropertyNode * parent, SGPropertyNode * child ); virtual void childRemoved( SGPropertyNode * parent, -- 2.39.5