]> git.mxchange.org Git - flightgear.git/commitdiff
Allow Canvas Windows to be resized by dragging
authorThomas Geymayer <tomgey@gmail.com>
Thu, 13 Dec 2012 12:52:53 +0000 (13:52 +0100)
committerThomas Geymayer <tomgey@gmail.com>
Thu, 13 Dec 2012 13:12:29 +0000 (14:12 +0100)
 - 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.

src/Canvas/gui_mgr.cxx
src/Canvas/gui_mgr.hxx
src/Canvas/window.cxx
src/Canvas/window.hxx
src/Viewer/FGEventHandler.cxx

index 711eda3094979bcc568271d2bd6feb7665ebece2..efd3f94e1cfc90453f4db58c30bcf9a908bac0a8 100644 (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();
 
index 1661011d50964745b1bcf7471d0fe09fd0925802..23888a52f17658b8b4efbd6cb8b4e3cb8d6bdaa1 100644 (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
index cda7e293153f2c508933f9f0986bac3b0e12e093..57d862fa4a157a2641d5a66fc529811bd17cc261 100644 (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
index 28a681ba7099d34ae9c3b8678ea6fc307e1cfecb..3b31623b0a9b0099c75830b7802007c80a86f78e 100644 (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
 
index 1174adc6dd98f3d4266b7f6a908d3fb54436cadc..b0f9bcd1dd6353621a21f26f691ab61edd106185 100644 (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;