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,
|
||||
event->getClientPos(),
|
||||
event->getDelta(),
|
||||
_root_group );
|
||||
if( !_root_group->accept(visitor) )
|
||||
return false;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() )
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user