1
0
Fork 0

Allow Canvas Windows to be resized by dragging

- Setting 'resize' property to true on canvas::Window shows
   resize icons and exposes requested size to the property
   tree. This can be used eg. from Nasal to actually resize
   the window and/or show a preview of the resized window
   while resizing.
 - Event handling now ignores events which have already
   been handled. Before eg. clicking inside a window
   also caused picking to be performed inside the
   scene which is for sure not the expected behaviour.
 - Also forwards scroll wheel events from canvas::Window.
This commit is contained in:
Thomas Geymayer 2012-12-13 13:52:53 +01:00
parent 1c8d9ef132
commit 4bf4754f7b
5 changed files with 229 additions and 18 deletions

View file

@ -19,6 +19,7 @@
#include "gui_mgr.hxx"
#include <Canvas/window.hxx>
#include <Main/fg_os.hxx>
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <Viewer/CameraGroup.hxx>
@ -49,6 +50,8 @@ class GUIEventHandler:
osg::Object*,
osg::NodeVisitor* )
{
if( ea.getHandled() )
return false;
return _gui_mgr->handleEvent(ea);
}
@ -116,7 +119,10 @@ GUIMgr::GUIMgr():
_event_handler( new GUIEventHandler(this) ),
_transform( new osg::MatrixTransform ),
_width(_props, "size[0]"),
_height(_props, "size[1]")
_height(_props, "size[1]"),
_resize(canvas::Window::NONE),
_last_cursor(MOUSE_CURSOR_NONE),
_last_scroll_time(0)
{
_width = _height = -1;
@ -157,7 +163,9 @@ void GUIMgr::init()
globals->get_renderer()
->getViewer()
->addEventHandler( _event_handler );
->getEventHandlers()
// GUI is on top of everything so lets install as first event handler
.push_front( _event_handler );
}
//------------------------------------------------------------------------------
@ -214,7 +222,7 @@ bool GUIMgr::handleEvent(const osgGA::GUIEventAdapter& ea)
ea.getWindowY(),
ea.getWindowWidth(),
ea.getWindowHeight() );
return true;
return false; // Let other event handlers also consume resize events
default:
return false;
}
@ -251,6 +259,25 @@ GUIMgr::addPlacement( SGPropertyNode* node,
return placements;
}
/*
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 GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea)
{
@ -277,7 +304,24 @@ bool GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea)
event->button = ea.getButton();
event->state = ea.getButtonMask();
event->mod = ea.getModKeyMask();
//event->scroll = ea.getScrollingMotion();
static simgear::Rect<float> resize_region;
if( !_resize_window.expired() )
{
switch( ea.getEventType() )
{
case osgGA::GUIEventAdapter::RELEASE:
_resize_window.lock()->handleResize(canvas::Window::NONE);
_resize_window.reset();
break;
case osgGA::GUIEventAdapter::DRAG:
_resize_window.lock()->handleResize(_resize, event->delta);
return true;
default:
return false;
}
}
canvas::WindowPtr window_at_cursor;
for( int i = _transform->getNumChildren() - 1; i >= 0; --i )
@ -293,8 +337,10 @@ bool GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea)
canvas::WindowPtr window =
static_cast<WindowUserData*>(layer->getChild(j)->getUserData())
->window.lock();
float margin = window->isResizable() ? resize_margin_pos : 0;
if( window->getRegion().contains( event->getScreenX(),
event->getScreenY() ) )
event->getScreenY(),
margin ) )
{
window_at_cursor = window;
break;
@ -305,6 +351,65 @@ bool GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea)
break;
}
if( window_at_cursor )
{
const simgear::Rect<float>& reg = window_at_cursor->getRegion();
if( window_at_cursor->isResizable()
&& ( ea.getEventType() == osgGA::GUIEventAdapter::MOVE
|| ea.getEventType() == osgGA::GUIEventAdapter::PUSH
|| ea.getEventType() == osgGA::GUIEventAdapter::RELEASE
)
&& !reg.contains( event->getScreenX(),
event->getScreenY(),
-resize_margin_neg ) )
{
if( !_last_cursor )
_last_cursor = fgGetMouseCursor();
_resize = 0;
if( event->getScreenX() <= reg.l() + resize_corner )
_resize |= canvas::Window::LEFT;
else if( event->getScreenX() >= reg.r() - resize_corner )
_resize |= canvas::Window::RIGHT;
if( event->getScreenY() <= reg.t() + resize_corner )
_resize |= canvas::Window::TOP;
else if( event->getScreenY() >= reg.b() - resize_corner )
_resize |= canvas::Window::BOTTOM;
static const int cursor_mapping[] =
{
0, MOUSE_CURSOR_LEFTSIDE, MOUSE_CURSOR_RIGHTSIDE, 0,
MOUSE_CURSOR_TOPSIDE, MOUSE_CURSOR_TOPLEFT, MOUSE_CURSOR_TOPRIGHT, 0,
MOUSE_CURSOR_BOTTOMSIDE, MOUSE_CURSOR_BOTTOMLEFT, MOUSE_CURSOR_BOTTOMRIGHT,
};
if( !cursor_mapping[_resize] )
return false;
fgSetMouseCursor(cursor_mapping[_resize]);
if( ea.getEventType() == osgGA::GUIEventAdapter::PUSH )
{
_resize_window = window_at_cursor;
window_at_cursor->doRaise();
window_at_cursor->handleResize( _resize | canvas::Window::INIT,
event->delta );
}
return true;
}
}
if( _last_cursor )
{
fgSetMouseCursor(_last_cursor);
_last_cursor = 0;
return true;
}
canvas::WindowPtr target_window = window_at_cursor;
switch( ea.getEventType() )
{
@ -312,9 +417,28 @@ bool GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea)
_last_push = window_at_cursor;
event->type = sc::Event::MOUSE_DOWN;
break;
// case osgGA::GUIEventAdapter::SCROLL:
// event->type = sc::Event::SCROLL;
// break;
case osgGA::GUIEventAdapter::SCROLL:
switch( ea.getScrollingMotion() )
{
case osgGA::GUIEventAdapter::SCROLL_UP:
event->delta.y() = 1;
break;
case osgGA::GUIEventAdapter::SCROLL_DOWN:
event->delta.y() = -1;
break;
default:
return false;
}
// osg sends two events for every scrolling motion. We don't need
// duplicate events, so lets ignore the second event with the same
// timestamp.
if( _last_scroll_time == ea.getTime() )
return true;
_last_scroll_time = ea.getTime();
event->type = sc::Event::WHEEL;
break;
case osgGA::GUIEventAdapter::MOVE:
{
canvas::WindowPtr last_mouse_over = _last_mouse_over.lock();
@ -323,7 +447,8 @@ bool GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea)
sc::MouseEventPtr move_event( new sc::MouseEvent(*event) );
move_event->type = sc::Event::MOUSE_LEAVE;
// Let the event position be always relative to the top left window corner
// Let the event position be always relative to the top left window
// corner
move_event->client_pos.x() -= last_mouse_over->getRegion().x();
move_event->client_pos.y() -= last_mouse_over->getRegion().y();

View file

@ -49,6 +49,7 @@ class GUIMgr:
bool handleEvent(const osgGA::GUIEventAdapter& ea);
protected:
osg::ref_ptr<GUIEventHandler> _event_handler;
osg::ref_ptr<osg::MatrixTransform> _transform;
@ -56,9 +57,14 @@ class GUIMgr:
_height;
canvas::WindowWeakPtr _last_push,
_last_mouse_over;
_last_mouse_over,
_resize_window;
uint8_t _resize;
int _last_cursor;
float _last_x,
_last_y;
double _last_scroll_time;
canvas::WindowPtr getWindow(size_t i);
simgear::canvas::Placements

View file

@ -30,7 +30,13 @@ namespace canvas
PropertyBasedElement(node),
_image( simgear::canvas::CanvasPtr(),
node,
simgear::canvas::Style() )
simgear::canvas::Style() ),
_resizable(false),
_resize_top(node, "resize-top"),
_resize_right(node, "resize-right"),
_resize_bottom(node, "resize-bottom"),
_resize_left(node, "resize-left"),
_resize_status(node, "resize-status")
{
_image.removeListener();
@ -60,9 +66,19 @@ namespace canvas
//----------------------------------------------------------------------------
void Window::valueChanged(SGPropertyNode * node)
{
if( node->getParent() == _node && node->getNameString() == "raise-top" )
doRaise(node);
else
bool handled = false;
if( node->getParent() == _node )
{
handled = true;
if( node->getNameString() == "raise-top" )
doRaise(node);
else if( node->getNameString() == "resize" )
_resizable = node->getBoolValue();
else
handled = false;
}
if( !handled )
_image.valueChanged(node);
}
@ -90,6 +106,12 @@ namespace canvas
return _image.getSrcCanvas();
}
//----------------------------------------------------------------------------
bool Window::isResizable() const
{
return _resizable;
}
//----------------------------------------------------------------------------
bool Window::handleMouseEvent(const simgear::canvas::MouseEventPtr& event)
{
@ -99,10 +121,38 @@ namespace canvas
return false;
}
//----------------------------------------------------------------------------
void Window::handleResize(uint8_t mode, const osg::Vec2f& delta)
{
if( mode == NONE )
{
_resize_status = 0;
return;
}
else if( mode & INIT )
{
_resize_top = getRegion().t();
_resize_right = getRegion().r();
_resize_bottom = getRegion().b();
_resize_left = getRegion().l();
_resize_status = 1;
}
if( mode & BOTTOM )
_resize_bottom += delta.y();
else if( mode & TOP )
_resize_top += delta.y();
if( mode & canvas::Window::RIGHT )
_resize_right += delta.x();
else if( mode & canvas::Window::LEFT )
_resize_left += delta.x();
}
//----------------------------------------------------------------------------
void Window::doRaise(SGPropertyNode* node_raise)
{
if( !node_raise->getBoolValue() )
if( node_raise && !node_raise->getBoolValue() )
return;
BOOST_FOREACH(osg::Group* parent, getGroup()->getParents())
@ -114,7 +164,8 @@ namespace canvas
parent->addChild(getGroup());
}
node_raise->setBoolValue(false);
if( node_raise )
node_raise->setBoolValue(false);
}
} // namespace canvas

View file

@ -33,11 +33,22 @@ namespace canvas
public simgear::PropertyBasedElement
{
public:
enum Resize
{
NONE = 0,
LEFT = 1,
RIGHT = LEFT << 1,
TOP = RIGHT << 1,
BOTTOM = TOP << 1,
INIT = BOTTOM << 1
};
Window(SGPropertyNode* node);
virtual ~Window();
virtual void update(double delta_time_sec);
virtual void valueChanged (SGPropertyNode * node);
virtual void valueChanged(SGPropertyNode* node);
osg::Group* getGroup();
const simgear::Rect<float>& getRegion() const;
@ -45,13 +56,25 @@ namespace canvas
void setCanvas(simgear::canvas::CanvasPtr canvas);
simgear::canvas::CanvasWeakPtr getCanvas() const;
bool isResizable() const;
bool handleMouseEvent(const simgear::canvas::MouseEventPtr& event);
void handleResize(uint8_t mode, const osg::Vec2f& delta = osg::Vec2f());
void doRaise(SGPropertyNode* node_raise = 0);
protected:
simgear::canvas::Image _image;
bool _resizable;
simgear::PropertyObject<int> _resize_top,
_resize_right,
_resize_bottom,
_resize_left,
_resize_status;
void doRaise(SGPropertyNode* node_raise);
};
} // namespace canvas

View file

@ -165,6 +165,12 @@ eventToViewport(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us,
bool FGEventHandler::handle(const osgGA::GUIEventAdapter& ea,
osgGA::GUIActionAdapter& us)
{
// Event handlers seem to be called even if the according event has already
// been handled. Already handled events shouldn't be handled multiple times
// so we need to exit here manually.
if( ea.getHandled() )
return false;
int x = 0;
int y = 0;