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:
parent
1c8d9ef132
commit
4bf4754f7b
5 changed files with 229 additions and 18 deletions
src
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue