Canvas.hxx
CanvasEvent.hxx
CanvasEventListener.hxx
+ CanvasEventManager.hxx
CanvasEventTypes.hxx
CanvasEventVisitor.hxx
CanvasMgr.hxx
Canvas.cxx
CanvasEvent.cxx
CanvasEventListener.cxx
+ CanvasEventManager.cxx
CanvasEventVisitor.cxx
CanvasMgr.cxx
CanvasPlacement.cxx
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "Canvas.hxx"
+#include "CanvasEventManager.hxx"
#include "CanvasEventVisitor.hxx"
#include <simgear/canvas/MouseEvent.hxx>
#include <simgear/canvas/CanvasPlacement.hxx>
Canvas::Canvas(SGPropertyNode* node):
PropertyBasedElement(node),
_canvas_mgr(0),
+ _event_manager(new EventManager),
_size_x(-1),
_size_y(-1),
_view_width(-1),
if( !_root_group->accept(visitor) )
return false;
- // TODO create special events like click/dblclick etc.
-
- return visitor.propagateEvent(event);
+ return _event_manager->handleEvent(event, visitor.getPropagationPath());
}
//----------------------------------------------------------------------------
CanvasPtr canvas = boost::static_pointer_cast<Canvas>(self);
_root_group.reset( new Group(canvas, _node) );
+ _root_group->setSelf(_root_group);
// Remove automatically created property listener as we forward them on our
// own
SystemAdapterPtr _system_adapter;
CanvasMgr *_canvas_mgr;
+ std::auto_ptr<EventManager> _event_manager;
+
int _size_x,
_size_y,
_view_width,
_visible;
ODGauge _texture;
- std::auto_ptr<Group> _root_group;
+ GroupPtr _root_group;
CullCallbackPtr _cull_callback;
bool _render_always; //<! Used to disable automatic lazy rendering (culling)
return target;
}
+ //----------------------------------------------------------------------------
+ double Event::getTime() const
+ {
+ return time;
+ }
+
//----------------------------------------------------------------------------
void Event::stopPropagation()
{
Type type;
ElementWeakPtr target;
+ double time;
bool propagation_stopped;
Event();
std::string getTypeString() const;
ElementWeakPtr getTarget() const;
+
+ double getTime() const;
+
void stopPropagation();
static Type strToType(const std::string& str);
--- /dev/null
+// Manage event handling inside a Canvas similar to the DOM Level 3 Event Model
+//
+// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#include "CanvasEventManager.hxx"
+#include "MouseEvent.hxx"
+#include <simgear/canvas/elements/CanvasElement.hxx>
+
+namespace simgear
+{
+namespace canvas
+{
+
+ const unsigned int drag_threshold = 8;
+ const double multi_click_timeout = 0.4;
+
+ //----------------------------------------------------------------------------
+ EventManager::EventManager():
+ _current_click_count(0)
+ {
+
+ }
+
+ //----------------------------------------------------------------------------
+ bool EventManager::handleEvent( const MouseEventPtr& event,
+ const EventPropagationPath& path )
+ {
+ propagateEvent(event, path);
+ switch( event->type )
+ {
+ case Event::MOUSE_DOWN:
+ _last_mouse_down = StampedPropagationPath(path, event->getTime());
+ break;
+ case Event::MOUSE_UP:
+ {
+ if( _last_mouse_down.path.empty() )
+ // Ignore mouse up without any previous mouse down
+ return false;
+
+ if( checkClickDistance(path, _last_mouse_down.path) )
+ handleClick(event, getCommonAncestor(_last_mouse_down.path, path));
+
+ break;
+ }
+ default:
+ return false;
+ }
+
+ return true;
+ }
+
+ //----------------------------------------------------------------------------
+ void EventManager::handleClick( const MouseEventPtr& event,
+ const EventPropagationPath& path )
+ {
+ MouseEventPtr click(new MouseEvent(*event));
+ click->type = Event::CLICK;
+
+ if( event->getTime() > _last_click.time + multi_click_timeout )
+ _current_click_count = 1;
+ else
+ {
+ // Maximum current click count is 3
+ _current_click_count = (_current_click_count % 3) + 1;
+
+ if( _current_click_count > 1 )
+ {
+ // Reset current click count if moved too far
+ if( !checkClickDistance(path, _last_click.path) )
+ _current_click_count = 1;
+ }
+ }
+
+ click->click_count = _current_click_count;
+
+ MouseEventPtr dbl_click;
+ if( _current_click_count == 2 )
+ {
+ dbl_click.reset(new MouseEvent(*click));
+ dbl_click->type = Event::DBL_CLICK;
+ }
+
+ propagateEvent(click, path);
+
+ if( dbl_click )
+ propagateEvent(dbl_click, getCommonAncestor(_last_click.path, path));
+
+ _last_click = StampedPropagationPath(path, event->getTime());
+ }
+
+ //----------------------------------------------------------------------------
+ bool EventManager::propagateEvent( const EventPtr& event,
+ const EventPropagationPath& path )
+ {
+ event->target = path.back().element;
+ MouseEventPtr mouse_event = boost::dynamic_pointer_cast<MouseEvent>(event);
+
+ // Event propagation similar to DOM Level 3 event flow:
+ // http://www.w3.org/TR/DOM-Level-3-Events/#event-flow
+
+ // Capturing phase
+// for( EventTargets::iterator it = _target_path.begin();
+// it != _target_path.end();
+// ++it )
+// {
+// if( it->element )
+// std::cout << it->element->getProps()->getPath() << " "
+// << "(" << it->local_pos.x() << "|" << it->local_pos.y() << ")\n";
+// }
+
+ // Bubbling phase
+ for( EventPropagationPath::const_reverse_iterator
+ it = path.rbegin();
+ it != path.rend();
+ ++it )
+ {
+ ElementPtr el = it->element.lock();
+
+ if( !el )
+ // Ignore element if it has been destroyed while traversing the event
+ // (eg. removed by another event handler)
+ continue;
+
+ if( mouse_event )
+ {
+ // Position and delta are specified in local coordinate system of
+ // current element
+ mouse_event->pos = it->local_pos;
+ mouse_event->delta = it->local_delta;
+ }
+
+ el->callListeners(event);
+
+ if( event->propagation_stopped )
+ return true;
+ }
+
+ return true;
+ }
+
+ //----------------------------------------------------------------------------
+ bool
+ EventManager::checkClickDistance( const EventPropagationPath& path1,
+ const EventPropagationPath& path2 ) const
+ {
+ osg::Vec2 delta = path1.front().local_pos - path2.front().local_pos;
+ return delta.x() < drag_threshold
+ && delta.y() < drag_threshold;
+ }
+
+ //----------------------------------------------------------------------------
+ EventPropagationPath
+ EventManager::getCommonAncestor( const EventPropagationPath& path1,
+ const EventPropagationPath& path2 ) const
+ {
+ if( path1.back().element.lock() == path2.back().element.lock() )
+ return path2;
+
+ EventPropagationPath path;
+
+ for( size_t i = 0; i < path1.size() && i < path2.size(); ++i )
+ {
+ if( path1[i].element.lock() != path2[i].element.lock() )
+ break;
+
+ path.push_back(path2[i]);
+ }
+
+ return path;
+ }
+
+} // namespace canvas
+} // namespace simgear
--- /dev/null
+// Manage event handling inside a Canvas similar to the DOM Level 3 Event Model
+//
+// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#ifndef CANVAS_EVENT_MANAGER_HXX_
+#define CANVAS_EVENT_MANAGER_HXX_
+
+#include "canvas_fwd.hxx"
+#include <deque>
+
+namespace simgear
+{
+namespace canvas
+{
+
+ struct EventTarget
+ {
+ ElementWeakPtr element;
+ osg::Vec2f local_pos,
+ local_delta;
+ };
+ typedef std::deque<EventTarget> EventPropagationPath;
+
+ class EventManager
+ {
+ public:
+ EventManager();
+
+ bool handleEvent( const MouseEventPtr& event,
+ const EventPropagationPath& path );
+
+ protected:
+ struct StampedPropagationPath
+ {
+ StampedPropagationPath():
+ time(0)
+ {}
+
+ StampedPropagationPath(const EventPropagationPath& path, double time):
+ path(path),
+ time(time)
+ {}
+
+ EventPropagationPath path;
+ double time;
+ };
+
+ // TODO if we really need the paths modify to not copy around the paths
+ // that much.
+ StampedPropagationPath _last_mouse_down,
+ _last_click;
+ size_t _current_click_count;
+
+ /**
+ * Propagate click event and handle multi-click (eg. create dblclick)
+ */
+ void handleClick( const MouseEventPtr& event,
+ const EventPropagationPath& path );
+
+ bool propagateEvent( const EventPtr& event,
+ const EventPropagationPath& path );
+
+ /**
+ * Check if two click events (either mousedown/up or two consecutive
+ * clicks) are inside a maximum distance to still create a click or
+ * dblclick event respectively.
+ */
+ bool checkClickDistance( const EventPropagationPath& path1,
+ const EventPropagationPath& path2 ) const;
+ EventPropagationPath
+ getCommonAncestor( const EventPropagationPath& path1,
+ const EventPropagationPath& path2 ) const;
+ };
+
+} // namespace canvas
+} // namespace simgear
+
+#endif /* CANVAS_EVENT_MANAGER_HXX_ */
{
if( mode == TRAVERSE_DOWN )
{
- EventTarget target = {0, pos, delta};
+ EventTarget target = {ElementWeakPtr(), pos, delta};
_target_path.push_back(target);
}
}
m(0, 1) * delta[0] + m(1, 1) * delta[1]
);
- EventTarget target = {&el, local_pos, local_delta};
+ EventTarget target = {el.getWeakPtr(), local_pos, local_delta};
_target_path.push_back(target);
if( el.traverse(*this) || _target_path.size() <= 2 )
}
//----------------------------------------------------------------------------
- bool EventVisitor::propagateEvent(const EventPtr& event)
+ const EventPropagationPath& EventVisitor::getPropagationPath() const
{
- // Event propagation similar to DOM Level 3 event flow:
- // http://www.w3.org/TR/DOM-Level-3-Events/#event-flow
-
- // Capturing phase
-// for( EventTargets::iterator it = _target_path.begin();
-// it != _target_path.end();
-// ++it )
-// {
-// if( it->element )
-// std::cout << it->element->getProps()->getPath() << " "
-// << "(" << it->local_pos.x() << "|" << it->local_pos.y() << ")\n";
-// }
-
- // Bubbling phase
- for( EventTargets::reverse_iterator it = _target_path.rbegin();
- it != _target_path.rend();
- ++it )
- {
- if( !it->element )
- continue;
-
- it->element->callListeners(event);
-
- if( event->propagation_stopped )
- return true;
- }
-
- return true;
+ return _target_path;
}
} // namespace canvas
#define CANVAS_EVENT_VISITOR_HXX_
#include "canvas_fwd.hxx"
-#include <deque>
+#include "CanvasEventManager.hxx"
namespace simgear
{
virtual bool traverse(Element& el);
virtual bool apply(Element& el);
- bool propagateEvent(const EventPtr& event);
+ const EventPropagationPath& getPropagationPath() const;
protected:
- struct EventTarget
- {
- Element* element;
- osg::Vec2f local_pos,
- local_delta;
- };
- typedef std::deque<EventTarget> EventTargets;
- EventTargets _target_path;
- TraverseMode _traverse_mode;
+ TraverseMode _traverse_mode;
+ EventPropagationPath _target_path;
+
};
} // namespace canvas
MouseEvent():
button(-1),
state(-1),
- mod(-1)
+ mod(-1),
+ click_count(0)
{}
osg::Vec2f getPos() const { return pos; }
float getDeltaX() const { return delta.x(); }
float getDeltaY() const { return delta.y(); }
+ int getCurrentClickCount() const { return click_count; }
+
osg::Vec2f pos,
delta;
- int button, //<! Button for this event
- state, //<! Current button state
- mod; //<! Keyboard modifier state
+ int button, //<! Button for this event
+ state, //<! Current button state
+ mod, //<! Keyboard modifier state
+ click_count; //<! Current click count
};
} // namespace canvas
#undef SG_FWD_DECL
+ class EventManager;
class EventVisitor;
typedef std::map<std::string, const SGPropertyNode*> Style;
}
}
+ //----------------------------------------------------------------------------
+ ElementWeakPtr Element::getWeakPtr() const
+ {
+ return boost::static_pointer_cast<Element>(_self.lock());
+ }
+
//----------------------------------------------------------------------------
void Element::update(double dt)
{
return naNil();
}
- //----------------------------------------------------------------------------
- SGConstPropertyNode_ptr Element::getProps() const
- {
- return _node;
- }
-
- //----------------------------------------------------------------------------
- SGPropertyNode_ptr Element::getProps()
- {
- return _node;
- }
-
//----------------------------------------------------------------------------
bool Element::accept(EventVisitor& visitor)
{
const SGPropertyNode_ptr& node,
const Style& parent_style,
Element* parent ):
+ PropertyBasedElement(node),
_canvas( canvas ),
_parent( parent ),
_transform_dirty( false ),
_transform( new osg::MatrixTransform ),
- _node( node ),
_style( parent_style ),
_drawable( 0 )
{
- assert( _node );
- _node->addChangeListener(this);
-
SG_LOG
(
SG_GL,
#include <simgear/canvas/canvas_fwd.hxx>
#include <simgear/canvas/CanvasEvent.hxx>
-#include <simgear/props/props.hxx>
+#include <simgear/props/PropertyBasedElement.hxx>
#include <simgear/misc/stdint.hxx> // for uint32_t
#include <simgear/nasal/cppbind/Ghost.hxx>
{
class Element:
- public SGPropertyChangeListener
+ public PropertyBasedElement
{
public:
typedef boost::function<void(const SGPropertyNode*)> StyleSetter;
*/
virtual ~Element() = 0;
+ ElementWeakPtr getWeakPtr() const;
+
/**
* Called every frame to update internal state
*
naRef addEventListener(const nasal::CallContext& ctx);
- SGConstPropertyNode_ptr getProps() const;
- SGPropertyNode_ptr getProps();
-
virtual bool accept(EventVisitor& visitor);
virtual bool ascend(EventVisitor& visitor);
virtual bool traverse(EventVisitor& visitor);
CanvasWeakPtr _canvas;
Element *_parent;
+
uint32_t _attributes_dirty;
bool _transform_dirty;
osg::ref_ptr<osg::MatrixTransform> _transform;
std::vector<TransformType> _transform_types;
- SGPropertyNode_ptr _node;
Style _style;
StyleSetters _style_setters;
std::vector<SGPropertyNode_ptr> _bounding_box;
const Style& style,
Element* parent )
{
- return ElementPtr( new T(canvas, node, style, parent) );
+ ElementPtr el( new T(canvas, node, style, parent) );
+ el->setSelf(el);
+ return el;
}
//----------------------------------------------------------------------------