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.
This commit is contained in:
Thomas Geymayer
2014-03-15 14:53:08 +01:00
parent 80e77b8372
commit f28e3fc9bb
7 changed files with 79 additions and 59 deletions
-1
View File
@@ -414,7 +414,6 @@ namespace canvas
EventVisitor visitor( EventVisitor::TRAVERSE_DOWN, EventVisitor visitor( EventVisitor::TRAVERSE_DOWN,
event->getClientPos(), event->getClientPos(),
event->getDelta(),
_root_group ); _root_group );
if( !_root_group->accept(visitor) ) if( !_root_group->accept(visitor) )
return false; return false;
+41 -30
View File
@@ -61,9 +61,18 @@ namespace canvas
return !path.empty() && time > 0; 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(): EventManager::EventManager():
_last_button_down(0),
_current_click_count(0) _current_click_count(0)
{ {
@@ -77,8 +86,7 @@ namespace canvas
switch( event->type ) switch( event->type )
{ {
case Event::MOUSE_DOWN: case Event::MOUSE_DOWN:
_last_mouse_down = StampedPropagationPath(path, event->getTime()); _last_mouse_down.set(event, path);
_last_button_down = event->button;
break; break;
case Event::MOUSE_UP: case Event::MOUSE_UP:
{ {
@@ -91,12 +99,12 @@ namespace canvas
// normal mouseup // normal mouseup
handled |= propagateEvent(event, path); handled |= propagateEvent(event, path);
if( _last_mouse_down.path.empty() ) if( !_last_mouse_down.valid() )
// Ignore mouse up without any previous mouse down // Ignore mouse up without any previous mouse down
return handled; return handled;
// now handle click/dblclick // now handle click/dblclick
if( checkClickDistance(path, _last_mouse_down.path) ) if( checkClickDistance(event->screen_pos, _last_mouse_down.pos) )
handled |= handled |=
handleClick(event, getCommonAncestor(_last_mouse_down.path, path)); handleClick(event, getCommonAncestor(_last_mouse_down.path, path));
@@ -110,7 +118,7 @@ namespace canvas
else else
{ {
// OSG does not set button for drag events. // 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); return propagateEvent(event, _last_mouse_down.path);
} }
case Event::MOUSE_MOVE: case Event::MOUSE_MOVE:
@@ -152,8 +160,10 @@ namespace canvas
if( _current_click_count > 1 ) if( _current_click_count > 1 )
{ {
// Reset current click count if moved too far // Reset current click count if moved too far or different button has
if( !checkClickDistance(path, _last_click.path) ) // been clicked
if( !checkClickDistance(event->screen_pos, _last_click.pos)
|| _last_click.button != event->button )
_current_click_count = 1; _current_click_count = 1;
} }
} }
@@ -173,7 +183,7 @@ namespace canvas
handled |= propagateEvent( dbl_click, handled |= propagateEvent( dbl_click,
getCommonAncestor(_last_click.path, path) ); getCommonAncestor(_last_click.path, path) );
_last_click = StampedPropagationPath(path, event->getTime()); _last_click.set(event, path);
return handled; return handled;
} }
@@ -249,14 +259,24 @@ namespace canvas
// Event propagation similar to DOM Level 3 event flow: // Event propagation similar to DOM Level 3 event flow:
// http://www.w3.org/TR/DOM-Level-3-Events/#event-flow // http://www.w3.org/TR/DOM-Level-3-Events/#event-flow
// Capturing phase // Position update only needed for drag event (as event needs to be
// for( EventPropagationPath::const_iterator it = path.begin(); // delivered to element of initial mousedown, but with update positions)
// it != path.end(); if( mouse_event && mouse_event->type == MouseEvent::DRAG )
// ++it ) {
// { osg::Vec2f local_pos = mouse_event->client_pos;
// if( !it->element.expired() )
// std::cout << it->element.lock()->getProps()->getPath() << std::endl; // 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 // Check if event supports bubbling
const Event::Type types_no_bubbling[] = { const Event::Type types_no_bubbling[] = {
@@ -294,17 +314,8 @@ namespace canvas
// TODO provide functions to convert delta to local coordinates on demand. // TODO provide functions to convert delta to local coordinates on demand.
// Maybe also provide a clone method for events as local coordinates // Maybe also provide a clone method for events as local coordinates
// might differ between different elements receiving the same event. // might differ between different elements receiving the same event.
if( mouse_event ) //&& event->type != Event::DRAG ) if( mouse_event )
{
// 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
mouse_event->local_pos = it->local_pos; mouse_event->local_pos = it->local_pos;
//mouse_event->delta = it->local_delta;
}
event->current_target = el; event->current_target = el;
el->handleEvent(event); el->handleEvent(event);
@@ -318,10 +329,10 @@ namespace canvas
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
bool bool
EventManager::checkClickDistance( const EventPropagationPath& path1, EventManager::checkClickDistance( const osg::Vec2f& pos1,
const EventPropagationPath& path2 ) const 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 return std::fabs(delta.x()) < drag_threshold
&& std::fabs(delta.y()) < drag_threshold; && std::fabs(delta.y()) < drag_threshold;
} }
+18 -9
View File
@@ -29,9 +29,10 @@ namespace canvas
struct EventTarget struct EventTarget
{ {
ElementWeakPtr element; ElementWeakPtr element;
osg::Vec2f local_pos,
local_delta; // Used as storage by EventManager during event propagation
mutable osg::Vec2f local_pos;
}; };
typedef std::deque<EventTarget> EventPropagationPath; typedef std::deque<EventTarget> EventPropagationPath;
inline bool operator==(const EventTarget& t1, const EventTarget& t2) 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 // TODO if we really need the paths modify to not copy around the paths
// that much. // that much.
StampedPropagationPath _last_mouse_down, StampedPropagationPath _last_mouse_over;
_last_click,
_last_mouse_over;
int _last_button_down;
size_t _current_click_count; 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) * 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 * clicks) are inside a maximum distance to still create a click or
* dblclick event respectively. * dblclick event respectively.
*/ */
bool checkClickDistance( const EventPropagationPath& path1, bool checkClickDistance( const osg::Vec2f& pos1,
const EventPropagationPath& path2 ) const; const osg::Vec2f& pos2 ) const;
EventPropagationPath EventPropagationPath
getCommonAncestor( const EventPropagationPath& path1, getCommonAncestor( const EventPropagationPath& path1,
const EventPropagationPath& path2 ) const; const EventPropagationPath& path2 ) const;
+3 -17
View File
@@ -30,14 +30,13 @@ namespace canvas
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
EventVisitor::EventVisitor( TraverseMode mode, EventVisitor::EventVisitor( TraverseMode mode,
const osg::Vec2f& pos, const osg::Vec2f& pos,
const osg::Vec2f& delta,
const ElementPtr& root ): const ElementPtr& root ):
_traverse_mode( mode ), _traverse_mode( mode ),
_root(root) _root(root)
{ {
if( mode == TRAVERSE_DOWN ) if( mode == TRAVERSE_DOWN )
{ {
EventTarget target = {ElementWeakPtr(), pos, delta}; EventTarget target = {ElementWeakPtr(), pos};
_target_path.push_back(target); _target_path.push_back(target);
} }
} }
@@ -63,14 +62,8 @@ namespace canvas
// We only need to check for hits while traversing down // We only need to check for hits while traversing down
if( _traverse_mode == TRAVERSE_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& pos = _target_path.back().local_pos;
const osg::Vec2f local_pos const osg::Vec2f local_pos = el.posToLocal(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)
);
// Don't check specified root element for collision, as its purpose is to // 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 // 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) ) if( _root.get() != &el && !el.hitBound(pos, local_pos) )
return false; return false;
const osg::Vec2f& delta = _target_path.back().local_delta; EventTarget target = {el.getWeakPtr(), local_pos};
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};
_target_path.push_back(target); _target_path.push_back(target);
if( el.traverse(*this) || &el == _root.get() ) if( el.traverse(*this) || &el == _root.get() )
-2
View File
@@ -42,12 +42,10 @@ namespace canvas
* *
* @param mode * @param mode
* @param pos Mouse position * @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 * @param root Element to dispatch events to if no element is hit
*/ */
EventVisitor( TraverseMode mode, EventVisitor( TraverseMode mode,
const osg::Vec2f& pos, const osg::Vec2f& pos,
const osg::Vec2f& delta,
const ElementPtr& root = ElementPtr() ); const ElementPtr& root = ElementPtr() );
virtual ~EventVisitor(); virtual ~EventVisitor();
virtual bool traverse(Element& el); virtual bool traverse(Element& el);
+12
View File
@@ -303,6 +303,18 @@ namespace canvas
return _transform.get(); 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) void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
{ {
@@ -129,6 +129,11 @@ namespace canvas
osg::MatrixTransform* getMatrixTransform(); osg::MatrixTransform* getMatrixTransform();
osg::MatrixTransform const* getMatrixTransform() const; osg::MatrixTransform const* getMatrixTransform() const;
/**
* Transform position to local coordinages.
*/
osg::Vec2f posToLocal(const osg::Vec2f& pos) const;
virtual void childAdded( SGPropertyNode * parent, virtual void childAdded( SGPropertyNode * parent,
SGPropertyNode * child ); SGPropertyNode * child );
virtual void childRemoved( SGPropertyNode * parent, virtual void childRemoved( SGPropertyNode * parent,