1
0
Fork 0

canvas GUI: Cleanup and trigger dragstart/end events

This commit is contained in:
Thomas Geymayer 2018-02-15 20:05:47 +01:00
parent e295d991a6
commit 82078b632b

View file

@ -41,6 +41,26 @@ typedef SGSharedPtr<DesktopGroup> DesktopPtr;
typedef SGWeakPtr<DesktopGroup> DesktopWeakPtr; typedef SGWeakPtr<DesktopGroup> DesktopWeakPtr;
namespace sc = simgear::canvas; namespace sc = simgear::canvas;
using osgEA = osgGA::GUIEventAdapter;
/*
RESIZE AREAS
============
| || | _ inside corner region (L-shaped part inside margin) both
|___||_|_ _ _/ directions can be resized (outside only one axis)
| || | |
| || |
| || |_____|__ _
| || | } margin_neg \
| ========|== <-- window border |_ area where resize
| | } margin_pos | can be initiated
|____________|__/ _/
|<- corner ->|
*/
const float RESIZE_MARGIN_POS = 12;
const float RESIZE_MARGIN_NEG = 2;
const float RESIZE_CORNER = 20;
/** /**
* Event handler * Event handler
@ -51,7 +71,7 @@ class GUIEventHandler:
public: public:
GUIEventHandler(const DesktopWeakPtr& desktop_group); GUIEventHandler(const DesktopWeakPtr& desktop_group);
bool handle( const osgGA::GUIEventAdapter& ea, bool handle( const osgEA& ea,
osgGA::GUIActionAdapter&, osgGA::GUIActionAdapter&,
osg::Object*, osg::Object*,
osg::NodeVisitor* ); osg::NodeVisitor* );
@ -106,7 +126,10 @@ class DesktopGroup:
bool grabPointer(const sc::WindowPtr& window); bool grabPointer(const sc::WindowPtr& window);
void ungrabPointer(const sc::WindowPtr& window); void ungrabPointer(const sc::WindowPtr& window);
bool handleEvent(const osgGA::GUIEventAdapter& ea); sc::WindowPtr windowAtPosition(const osg::Vec2f& screen_pos);
osg::Vec2f toScreenPos(const osgEA& ea) const;
bool handleOsgEvent(const osgEA& ea);
protected: protected:
@ -119,6 +142,7 @@ class DesktopGroup:
_height; _height;
sc::WindowWeakPtr _last_push, sc::WindowWeakPtr _last_push,
_last_drag,
_last_mouse_over, _last_mouse_over,
_resize_window, _resize_window,
_focus_window, _focus_window,
@ -126,23 +150,25 @@ class DesktopGroup:
uint8_t _resize {sc::Window::NONE}; uint8_t _resize {sc::Window::NONE};
int _last_cursor {MOUSE_CURSOR_NONE}; int _last_cursor {MOUSE_CURSOR_NONE};
bool _drag_finished {false};
osg::Vec2 _drag_start; osg::Vec2f _drag_start,
float _last_x {-1}, _last_mouse_pos;
_last_y {-1};
double _last_scroll_time {0}; double _last_scroll_time {0};
uint32_t _last_key_down_no_mod {~0u}; // Key repeat for non modifier keys uint32_t _last_key_down_no_mod {~0u}; // Key repeat for non modifier keys
bool canHandleInput() const; bool canHandleInput() const;
bool handleMouse(const osgGA::GUIEventAdapter& ea); bool handleMouse(const osgEA& ea);
bool handleKeyboard(const osgGA::GUIEventAdapter& ea); bool handleKeyboard(const osgEA& ea);
bool handleEvent( const sc::EventPtr& event, bool propagateEvent( const sc::EventPtr& event,
const sc::WindowPtr& active_window ); const sc::WindowPtr& active_window );
bool handleRootEvent(const sc::EventPtr& event); bool propagateRootEvent(const sc::EventPtr& event);
void handleResize(int x, int y, int width, int height); void handleResize(int x, int y, int width, int height);
bool handleDrag(const sc::EventPtr& event);
void finishDrag(const sc::WindowPtr& drag_src, const sc::EventPtr& event);
void handleMouseMode(SGPropertyNode* node); void handleMouseMode(SGPropertyNode* node);
/** /**
@ -166,7 +192,7 @@ GUIEventHandler::GUIEventHandler(const DesktopWeakPtr& desktop_group):
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool GUIEventHandler::handle( const osgGA::GUIEventAdapter& ea, bool GUIEventHandler::handle( const osgEA& ea,
osgGA::GUIActionAdapter&, osgGA::GUIActionAdapter&,
osg::Object*, osg::Object*,
osg::NodeVisitor* ) osg::NodeVisitor* )
@ -175,7 +201,7 @@ bool GUIEventHandler::handle( const osgGA::GUIEventAdapter& ea,
return false; return false;
DesktopPtr desktop = _desktop.lock(); DesktopPtr desktop = _desktop.lock();
return desktop && desktop->handleEvent(ea); return desktop && desktop->handleOsgEvent(ea);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -244,22 +270,67 @@ void DesktopGroup::ungrabPointer(const sc::WindowPtr& window)
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool DesktopGroup::handleEvent(const osgGA::GUIEventAdapter& ea) sc::WindowPtr DesktopGroup::windowAtPosition(const osg::Vec2f& screen_pos)
{
for( int i = _scene_group->getNumChildren() - 1; i >= 0; --i )
{
osg::Group *element = _scene_group->getChild(i)->asGroup();
if( !element || !element->getUserData() )
continue; // TODO warn/log?
sc::WindowPtr window =
dynamic_cast<sc::Window*>
(
static_cast<sc::Element::OSGUserData*>(
element->getUserData()
)->element.get()
);
if( !window || !window->isCapturingEvents() || !window->isVisible() )
continue;
float margin = window->isResizable() ? RESIZE_MARGIN_POS : 0;
if( window->getScreenRegion().contains( screen_pos.x(),
screen_pos.y(),
margin ) )
{
return window;
}
}
return {};
}
//------------------------------------------------------------------------------
osg::Vec2f DesktopGroup::toScreenPos(const osgEA& ea) const
{
float x = SGMiscf::round(0.5 * (ea.getXnormalized() + 1) * _width);
float y = SGMiscf::round(0.5 * (ea.getYnormalized() + 1) * _height);
if( ea.getMouseYOrientation() != osgEA::Y_INCREASING_DOWNWARDS )
y = _height - y;
return {x, y};
}
//------------------------------------------------------------------------------
bool DesktopGroup::handleOsgEvent(const osgEA& ea)
{ {
switch( ea.getEventType() ) switch( ea.getEventType() )
{ {
case osgGA::GUIEventAdapter::PUSH: case osgEA::PUSH:
case osgGA::GUIEventAdapter::RELEASE: case osgEA::RELEASE:
// case osgGA::GUIEventAdapter::DOUBLECLICK: // case osgEA::DOUBLECLICK:
// // DOUBLECLICK doesn't seem to be triggered... // // DOUBLECLICK doesn't seem to be triggered...
case osgGA::GUIEventAdapter::DRAG: case osgEA::DRAG:
case osgGA::GUIEventAdapter::MOVE: case osgEA::MOVE:
case osgGA::GUIEventAdapter::SCROLL: case osgEA::SCROLL:
return handleMouse(ea); return handleMouse(ea);
case osgGA::GUIEventAdapter::KEYDOWN: case osgEA::KEYDOWN:
case osgGA::GUIEventAdapter::KEYUP: case osgEA::KEYUP:
return handleKeyboard(ea); return handleKeyboard(ea);
case osgGA::GUIEventAdapter::RESIZE: case osgEA::RESIZE:
handleResize( ea.getWindowX(), handleResize( ea.getWindowX(),
ea.getWindowY(), ea.getWindowY(),
ea.getWindowWidth(), ea.getWindowWidth(),
@ -270,25 +341,6 @@ bool DesktopGroup::handleEvent(const osgGA::GUIEventAdapter& ea)
} }
} }
/*
RESIZE AREAS
============
| || | _ inside corner region (L-shaped part inside margin) both
|___||_|_ _ _/ directions can be resized (outside only one axis)
| || | |
| || |
| || |_____|__ _
| || | } margin_neg \
| ========|== <-- window border |_ area where resize
| | } margin_pos | can be initiated
|____________|__/ _/
|<- corner ->|
*/
const float resize_margin_pos = 12;
const float resize_margin_neg = 2;
const float resize_corner = 20;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool DesktopGroup::canHandleInput() const bool DesktopGroup::canHandleInput() const
{ {
@ -298,102 +350,74 @@ bool DesktopGroup::canHandleInput() const
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool DesktopGroup::handleMouse(const osgGA::GUIEventAdapter& ea) bool DesktopGroup::handleMouse(const osgEA& ea)
{ {
if( !canHandleInput() ) if( !canHandleInput() )
return false; return false;
sc::MouseEventPtr event(new sc::MouseEvent(ea)); osg::Vec2f mouse_pos = toScreenPos(ea),
delta = mouse_pos - _last_mouse_pos;
_last_mouse_pos = mouse_pos;
event->screen_pos.x() = 0.5 * (ea.getXnormalized() + 1) * _width + 0.5; if( auto resize_window = _resize_window.lock() )
event->screen_pos.y() = 0.5 * (ea.getYnormalized() + 1) * _height + 0.5;
if( ea.getMouseYOrientation()
!= osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS )
event->screen_pos.y() = _height - event->screen_pos.y();
event->delta.x() = event->getScreenX() - _last_x;
event->delta.y() = event->getScreenY() - _last_y;
_last_x = event->getScreenX();
_last_y = event->getScreenY();
event->local_pos = event->client_pos = event->screen_pos;
if( !_resize_window.expired() )
{ {
switch( ea.getEventType() ) switch( ea.getEventType() )
{ {
case osgGA::GUIEventAdapter::RELEASE: case osgEA::RELEASE:
_resize_window.lock()->handleResize(sc::Window::NONE); resize_window->handleResize(sc::Window::NONE);
_resize_window.reset(); _resize_window.reset();
break; break;
case osgGA::GUIEventAdapter::DRAG: case osgEA::DRAG:
_resize_window.lock()->handleResize( _resize, resize_window->handleResize(_resize, mouse_pos - _drag_start);
event->screen_pos - _drag_start );
return true; return true;
default: default:
return false; // Ignore all other events while resizing
return true;
} }
} }
sc::MouseEventPtr event(new sc::MouseEvent(ea));
event->screen_pos = mouse_pos;
event->delta = delta;
if( !_drag_finished && ea.getEventType() == osgEA::DRAG )
return handleDrag(event);
if( auto last_drag = _last_drag.lock() )
{
if( ea.getEventType() == osgEA::RELEASE )
finishDrag(last_drag, event);
else
// While dragging ignore all other mouse events
return true;
}
sc::WindowPtr window_at_cursor = _pointer_grab_window.lock(); sc::WindowPtr window_at_cursor = _pointer_grab_window.lock();
if( !window_at_cursor ) if( !window_at_cursor )
{ window_at_cursor = windowAtPosition(event->screen_pos);
for( int i = _scene_group->getNumChildren() - 1; i >= 0; --i )
{
osg::Group *element = _scene_group->getChild(i)->asGroup();
if( !element || !element->getUserData() )
continue; // TODO warn/log?
sc::WindowPtr window =
dynamic_cast<sc::Window*>
(
static_cast<sc::Element::OSGUserData*>(
element->getUserData()
)->element.get()
);
if( !window || !window->isCapturingEvents() || !window->isVisible() )
continue;
float margin = window->isResizable() ? resize_margin_pos : 0;
if( window->getScreenRegion().contains( event->getScreenX(),
event->getScreenY(),
margin ) )
{
window_at_cursor = window;
break;
}
}
}
if( window_at_cursor ) if( window_at_cursor )
{ {
const SGRect<float>& reg = window_at_cursor->getScreenRegion(); const SGRect<float>& reg = window_at_cursor->getScreenRegion();
if( window_at_cursor->isResizable() if( window_at_cursor->isResizable()
&& ( ea.getEventType() == osgGA::GUIEventAdapter::MOVE
|| ea.getEventType() == osgGA::GUIEventAdapter::PUSH
|| ea.getEventType() == osgGA::GUIEventAdapter::RELEASE
)
&& !reg.contains( event->getScreenX(), && !reg.contains( event->getScreenX(),
event->getScreenY(), event->getScreenY(),
-resize_margin_neg ) ) -RESIZE_MARGIN_NEG ) )
{ {
if( !_last_cursor ) if( !_last_cursor )
_last_cursor = fgGetMouseCursor(); _last_cursor = fgGetMouseCursor();
_resize = 0; _resize = 0;
if( event->getScreenX() <= reg.l() + resize_corner ) if( event->getScreenX() <= reg.l() + RESIZE_CORNER )
_resize |= sc::Window::LEFT; _resize |= sc::Window::LEFT;
else if( event->getScreenX() >= reg.r() - resize_corner ) else if( event->getScreenX() >= reg.r() - RESIZE_CORNER )
_resize |= sc::Window::RIGHT; _resize |= sc::Window::RIGHT;
if( event->getScreenY() <= reg.t() + resize_corner ) if( event->getScreenY() <= reg.t() + RESIZE_CORNER )
_resize |= sc::Window::TOP; _resize |= sc::Window::TOP;
else if( event->getScreenY() >= reg.b() - resize_corner ) else if( event->getScreenY() >= reg.b() - RESIZE_CORNER )
_resize |= sc::Window::BOTTOM; _resize |= sc::Window::BOTTOM;
static const int cursor_mapping[] = static const int cursor_mapping[] =
@ -408,7 +432,7 @@ bool DesktopGroup::handleMouse(const osgGA::GUIEventAdapter& ea)
fgSetMouseCursor(cursor_mapping[_resize]); fgSetMouseCursor(cursor_mapping[_resize]);
if( ea.getEventType() == osgGA::GUIEventAdapter::PUSH ) if( ea.getEventType() == osgEA::PUSH )
{ {
_resize_window = window_at_cursor; _resize_window = window_at_cursor;
_drag_start = event->screen_pos; _drag_start = event->screen_pos;
@ -428,20 +452,20 @@ bool DesktopGroup::handleMouse(const osgGA::GUIEventAdapter& ea)
return true; return true;
} }
sc::WindowPtr target_window = window_at_cursor;
switch( ea.getEventType() ) switch( ea.getEventType() )
{ {
case osgGA::GUIEventAdapter::PUSH: case osgEA::PUSH:
_last_push = window_at_cursor; _last_push = window_at_cursor;
_drag_finished = false;
event->type = sc::Event::MOUSE_DOWN; event->type = sc::Event::MOUSE_DOWN;
break; break;
case osgGA::GUIEventAdapter::SCROLL: case osgEA::SCROLL:
switch( ea.getScrollingMotion() ) switch( ea.getScrollingMotion() )
{ {
case osgGA::GUIEventAdapter::SCROLL_UP: case osgEA::SCROLL_UP:
event->delta.y() = 1; event->delta.y() = 1;
break; break;
case osgGA::GUIEventAdapter::SCROLL_DOWN: case osgEA::SCROLL_DOWN:
event->delta.y() = -1; event->delta.y() = -1;
break; break;
default: default:
@ -457,68 +481,58 @@ bool DesktopGroup::handleMouse(const osgGA::GUIEventAdapter& ea)
event->type = sc::Event::WHEEL; event->type = sc::Event::WHEEL;
break; break;
case osgGA::GUIEventAdapter::MOVE:
// If drag has not been handled yet it has been aborted. So let's treat it
// like a normal mouse movement.
case osgEA::DRAG:
case osgEA::MOVE:
{ {
sc::WindowPtr last_mouse_over = _last_mouse_over.lock(); sc::WindowPtr last_mouse_over = _last_mouse_over.lock();
if( last_mouse_over != window_at_cursor && last_mouse_over ) if( last_mouse_over && last_mouse_over != window_at_cursor )
{ last_mouse_over->handleEvent(event->clone(sc::Event::MOUSE_LEAVE));
sc::MouseEventPtr move_event( new sc::MouseEvent(*event) );
move_event->type = sc::Event::MOUSE_LEAVE;
move_event->client_pos -= toOsg(last_mouse_over->getPosition());
move_event->local_pos = move_event->client_pos;
last_mouse_over->handleEvent(move_event);
}
_last_mouse_over = window_at_cursor; _last_mouse_over = window_at_cursor;
event->type = sc::Event::MOUSE_MOVE; event->type = sc::Event::MOUSE_MOVE;
break; break;
} }
case osgGA::GUIEventAdapter::RELEASE: case osgEA::RELEASE:
{ {
event->type = sc::Event::MOUSE_UP;
sc::WindowPtr last_push = _last_push.lock(); sc::WindowPtr last_push = _last_push.lock();
_last_push.reset(); if( last_push && last_push != window_at_cursor )
if( last_push && last_push != target_window )
{ {
// Leave old window // Leave old window
sc::MouseEventPtr leave_event( new sc::MouseEvent(*event) ); last_push->handleEvent(event->clone(sc::Event::MOUSE_LEAVE));
leave_event->type = sc::Event::MOUSE_LEAVE;
leave_event->client_pos -= toOsg(last_push->getPosition());
leave_event->local_pos = leave_event->client_pos;
last_push->handleEvent(leave_event);
} }
_last_push.reset();
event->type = sc::Event::MOUSE_UP;
break; break;
} }
case osgGA::GUIEventAdapter::DRAG:
target_window = _last_push.lock();
event->type = sc::Event::DRAG;
break;
default: default:
return false; return false;
} }
if( target_window ) return propagateEvent(event, window_at_cursor);
{
event->client_pos -= toOsg(target_window->getPosition());
event->local_pos = event->client_pos;
return target_window->handleEvent(event);
}
else
return handleRootEvent(event);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool DesktopGroup::handleKeyboard(const osgGA::GUIEventAdapter& ea) bool DesktopGroup::handleKeyboard(const osgEA& ea)
{ {
if( !canHandleInput() ) if( !canHandleInput() )
return false; return false;
sc::KeyboardEventPtr event(new sc::KeyboardEvent(ea)); sc::KeyboardEventPtr event(new sc::KeyboardEvent(ea));
if( auto drag = _last_drag.lock() )
{
if( ea.getKey() == osgEA::KEY_Escape )
finishDrag(drag, event);
// While dragging ignore all key events
return true;
}
// Detect key repeat (of non modifier keys) // Detect key repeat (of non modifier keys)
if( !event->isModifier() ) if( !event->isModifier() )
{ {
@ -536,34 +550,31 @@ bool DesktopGroup::handleKeyboard(const osgGA::GUIEventAdapter& ea)
} }
sc::WindowPtr active_window = _focus_window.lock(); sc::WindowPtr active_window = _focus_window.lock();
bool handled = handleEvent(event, active_window); bool handled = propagateEvent(event, active_window);
if( event->getType() == sc::Event::KEY_DOWN if( event->getType() == sc::Event::KEY_DOWN
&& !event->defaultPrevented() && !event->defaultPrevented()
&& event->isPrint() ) && event->isPrint() )
{ {
sc::KeyboardEventPtr keypress( new sc::KeyboardEvent(*event) ); handled |= propagateEvent(event->clone(sc::Event::KEY_PRESS), active_window);
keypress->type = sc::Event::KEY_PRESS;
handled |= handleEvent(keypress, active_window);
} }
return handled; return handled;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool DesktopGroup::handleEvent( const sc::EventPtr& event, bool DesktopGroup::propagateEvent( const sc::EventPtr& event,
const sc::WindowPtr& active_window ) const sc::WindowPtr& active_window )
{ {
return active_window return active_window
? active_window->handleEvent(event) ? active_window->handleEvent(event)
: handleRootEvent(event); : propagateRootEvent(event);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool DesktopGroup::handleRootEvent(const sc::EventPtr& event) bool DesktopGroup::propagateRootEvent(const sc::EventPtr& event)
{ {
sc::Element::handleEvent(event); handleEvent(event);
// stopPropagation() on DesktopGroup stops propagation to internal event // stopPropagation() on DesktopGroup stops propagation to internal event
// handling. // handling.
@ -592,6 +603,33 @@ void DesktopGroup::handleResize(int x, int y, int width, int height)
} }
} }
//------------------------------------------------------------------------------
bool DesktopGroup::handleDrag(const sc::EventPtr& event)
{
event->type = sc::Event::DRAG;
auto drag_window = _last_drag.lock();
if( !drag_window )
{
_last_drag = drag_window = _last_push.lock();
if( drag_window )
drag_window->handleEvent(event->clone(sc::Event::DRAG_START));
}
// TODO dragover
return drag_window && drag_window->handleEvent(event);
}
//------------------------------------------------------------------------------
void DesktopGroup::finishDrag( const sc::WindowPtr& drag_src,
const sc::EventPtr& event )
{
drag_src->handleEvent(event->clone(sc::Event::DRAG_END));
_last_drag.reset();
_drag_finished = true;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void DesktopGroup::handleMouseMode(SGPropertyNode* node) void DesktopGroup::handleMouseMode(SGPropertyNode* node)
{ {