EventVisitor visitor( EventVisitor::TRAVERSE_DOWN,
event->getClientPos(),
- event->getDelta(),
_root_group );
if( !_root_group->accept(visitor) )
return false;
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)
{
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:
{
// 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));
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:
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;
}
}
handled |= propagateEvent( dbl_click,
getCommonAncestor(_last_click.path, path) );
- _last_click = StampedPropagationPath(path, event->getTime());
+ _last_click.set(event, path);
return handled;
}
// 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[] = {
// 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);
//----------------------------------------------------------------------------
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;
}
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)
// 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)
*/
* 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;
//----------------------------------------------------------------------------
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);
}
}
// 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
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() )
*
* @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);
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)
{
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,