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:39:23 +01:00
parent 80e77b8372
commit f28e3fc9bb
7 changed files with 79 additions and 59 deletions

View File

@@ -414,7 +414,6 @@ namespace canvas
EventVisitor visitor( EventVisitor::TRAVERSE_DOWN,
event->getClientPos(),
event->getDelta(),
_root_group );
if( !_root_group->accept(visitor) )
return false;

View File

@@ -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;
}

View File

@@ -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<EventTarget> 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;

View File

@@ -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() )

View File

@@ -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);

View File

@@ -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)
{

View File

@@ -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,