From: Thomas Geymayer Date: Thu, 13 Dec 2012 12:52:53 +0000 (+0100) Subject: Allow Canvas Windows to be resized by dragging X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=4bf4754f7b7a1a4670cd205778efb2394d877473;p=flightgear.git 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. --- diff --git a/src/Canvas/gui_mgr.cxx b/src/Canvas/gui_mgr.cxx index 711eda309..efd3f94e1 100644 --- a/src/Canvas/gui_mgr.cxx +++ b/src/Canvas/gui_mgr.cxx @@ -19,6 +19,7 @@ #include "gui_mgr.hxx" #include +#include
#include
#include
#include @@ -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 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(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& 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(); diff --git a/src/Canvas/gui_mgr.hxx b/src/Canvas/gui_mgr.hxx index 1661011d5..23888a52f 100644 --- a/src/Canvas/gui_mgr.hxx +++ b/src/Canvas/gui_mgr.hxx @@ -49,6 +49,7 @@ class GUIMgr: bool handleEvent(const osgGA::GUIEventAdapter& ea); protected: + osg::ref_ptr _event_handler; osg::ref_ptr _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 diff --git a/src/Canvas/window.cxx b/src/Canvas/window.cxx index cda7e2931..57d862fa4 100644 --- a/src/Canvas/window.cxx +++ b/src/Canvas/window.cxx @@ -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 diff --git a/src/Canvas/window.hxx b/src/Canvas/window.hxx index 28a681ba7..3b31623b0 100644 --- a/src/Canvas/window.hxx +++ b/src/Canvas/window.hxx @@ -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& 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 _resize_top, + _resize_right, + _resize_bottom, + _resize_left, + _resize_status; - void doRaise(SGPropertyNode* node_raise); }; } // namespace canvas diff --git a/src/Viewer/FGEventHandler.cxx b/src/Viewer/FGEventHandler.cxx index 1174adc6d..b0f9bcd1d 100644 --- a/src/Viewer/FGEventHandler.cxx +++ b/src/Viewer/FGEventHandler.cxx @@ -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;