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:
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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() )
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user