]> git.mxchange.org Git - flightgear.git/blob - src/Canvas/gui_mgr.cxx
4fa66febd24830533b1c932d5a0145f1b2c5edad
[flightgear.git] / src / Canvas / gui_mgr.cxx
1 // Canvas gui/dialog manager
2 //
3 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18
19 #include "gui_mgr.hxx"
20 #include <Canvas/window.hxx>
21 #include <Canvas/canvas.hxx>
22
23 #include <Main/globals.hxx>
24 #include <Viewer/CameraGroup.hxx>
25 #include <Viewer/renderer.hxx>
26
27 #include <osg/BlendFunc>
28 #include <osgViewer/Viewer>
29 #include <osgGA/GUIEventHandler>
30
31 #include <boost/bind.hpp>
32
33 /**
34  * Event handler
35  */
36 class GUIEventHandler:
37   public osgGA::GUIEventHandler
38 {
39   public:
40     GUIEventHandler(GUIMgr* gui_mgr):
41       _gui_mgr( gui_mgr )
42     {}
43
44     bool handle( const osgGA::GUIEventAdapter& ea,
45                  osgGA::GUIActionAdapter& aa,
46                  osg::Object*,
47                  osg::NodeVisitor* )
48     {
49       return _gui_mgr->handleEvent(ea);
50     }
51
52   protected:
53     GUIMgr *_gui_mgr;
54 };
55
56 /**
57  * Track a canvas placement on a window
58  */
59 class WindowPlacement:
60   public canvas::Placement
61 {
62   public:
63     WindowPlacement( canvas::WindowPtr window,
64                      CanvasPtr canvas ):
65       _window(window),
66       _canvas(canvas)
67     {}
68
69     /**
70      * Remove placement from window
71      */
72     virtual ~WindowPlacement()
73     {
74       canvas::WindowPtr window = _window.lock();
75       CanvasPtr canvas = _canvas.lock();
76
77       if( window && canvas && canvas == window->getCanvas().lock() )
78         window->setCanvas( CanvasPtr() );
79     }
80
81   private:
82     canvas::WindowWeakPtr _window;
83     CanvasWeakPtr _canvas;
84 };
85
86 /**
87  * Store pointer to window as user data
88  */
89 class WindowUserData:
90   public osg::Referenced
91 {
92   public:
93     canvas::WindowPtr window;
94     WindowUserData(canvas::WindowPtr window):
95       window(window)
96     {}
97 };
98
99 //------------------------------------------------------------------------------
100 typedef boost::shared_ptr<canvas::Window> WindowPtr;
101 WindowPtr windowFactory(SGPropertyNode* node)
102 {
103   return WindowPtr(new canvas::Window(node));
104 }
105
106 //------------------------------------------------------------------------------
107 GUIMgr::GUIMgr():
108   PropertyBasedMgr("/sim/gui/canvas", "window", &windowFactory),
109   _event_handler( new GUIEventHandler(this) ),
110   _transform( new osg::MatrixTransform ),
111   _width(_props, "size[0]"),
112   _height(_props, "size[1]")
113 {
114   _width = _height = -1;
115
116   osg::Camera* camera =
117     flightgear::getGUICamera( flightgear::CameraGroup::getDefault() );
118   assert(camera);
119   camera->addChild(_transform);
120
121   osg::Viewport* vp = camera->getViewport();
122   handleResize(vp->x(), vp->y(), vp->width(), vp->height());
123
124   Canvas::addPlacementFactory
125   (
126     "window",
127     boost::bind(&GUIMgr::addPlacement, this, _1, _2)
128   );
129
130   osg::StateSet* stateSet = _transform->getOrCreateStateSet();
131   stateSet->setDataVariance(osg::Object::STATIC);
132   stateSet->setRenderBinDetails(1000, "RenderBin");
133
134   // speed optimization?
135   stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
136   stateSet->setAttribute(new osg::BlendFunc(
137     osg::BlendFunc::SRC_ALPHA,
138     osg::BlendFunc::ONE_MINUS_SRC_ALPHA)
139   );
140   stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
141   stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
142   stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
143   stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
144 }
145
146 //------------------------------------------------------------------------------
147 void GUIMgr::init()
148 {
149   PropertyBasedMgr::init();
150
151   globals->get_renderer()
152          ->getViewer()
153          ->addEventHandler( _event_handler );
154 }
155
156 //------------------------------------------------------------------------------
157 void GUIMgr::shutdown()
158 {
159   PropertyBasedMgr::shutdown();
160
161   globals->get_renderer()
162          ->getViewer()
163          ->removeEventHandler( _event_handler );
164 }
165
166 //------------------------------------------------------------------------------
167 void GUIMgr::elementCreated(PropertyBasedElementPtr element)
168 {
169   canvas::WindowPtr window =
170     boost::static_pointer_cast<canvas::Window>(element);
171
172   size_t layer_index = std::max(0, window->getProps()->getIntValue("layer", 1));
173   osg::Group *layer = 0;
174
175   if( layer_index < _transform->getNumChildren() )
176   {
177     layer = _transform->getChild(layer_index)->asGroup();
178     assert(layer);
179   }
180   else
181   {
182     while( _transform->getNumChildren() <= layer_index )
183     {
184       layer = new osg::Group;
185       _transform->addChild(layer);
186     }
187   }
188   window->getGroup()->setUserData(new WindowUserData(window));
189   layer->addChild(window->getGroup());
190 }
191
192 //------------------------------------------------------------------------------
193 bool GUIMgr::handleEvent(const osgGA::GUIEventAdapter& ea)
194 {
195   switch( ea.getEventType() )
196   {
197     case osgGA::GUIEventAdapter::PUSH:
198     case osgGA::GUIEventAdapter::RELEASE:
199 //    case osgGA::GUIEventAdapter::DOUBLECLICK:
200 //    // DOUBLECLICK doesn't seem to be triggered...
201     case osgGA::GUIEventAdapter::DRAG:
202     case osgGA::GUIEventAdapter::MOVE:
203     case osgGA::GUIEventAdapter::SCROLL:
204       return handleMouse(ea);
205 //        case osgGA::GUIEventAdapter::MOVE:
206 //          std::cout << "MOVE" << std::endl;
207 //          break;
208     case osgGA::GUIEventAdapter::RESIZE:
209       handleResize( ea.getWindowX(),
210                     ea.getWindowY(),
211                     ea.getWindowWidth(),
212                     ea.getWindowHeight() );
213       return true;
214     default:
215       return false;
216   }
217 }
218
219 //------------------------------------------------------------------------------
220 canvas::WindowPtr GUIMgr::getWindow(size_t i)
221 {
222   return boost::static_pointer_cast<canvas::Window>(_elements[i]);
223 }
224
225 //------------------------------------------------------------------------------
226 canvas::Placements GUIMgr::addPlacement( const SGPropertyNode* node,
227                                          CanvasPtr canvas )
228 {
229   int placement_index = node->getIntValue("index", -1);
230
231   canvas::Placements placements;
232   for( size_t i = 0; i < _elements.size(); ++i )
233   {
234     if( placement_index > 0 && static_cast<int>(i) != placement_index )
235       continue;
236
237     canvas::WindowPtr window = getWindow(i);
238     if( !window )
239       continue;
240
241     window->setCanvas(canvas);
242     placements.push_back(
243       canvas::PlacementPtr(new WindowPlacement(window, canvas))
244     );
245   }
246   return placements;
247 }
248
249 //------------------------------------------------------------------------------
250 bool GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea)
251 {
252   if( !_transform->getNumChildren() )
253     return false;
254
255   canvas::MouseEvent event( ea.getEventType() );
256
257   event.x = 0.5 * (ea.getXnormalized() + 1) * _width + 0.5;
258   event.y = 0.5 * (ea.getYnormalized() + 1) * _height + 0.5;
259   if(    ea.getMouseYOrientation()
260       != osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS )
261     event.y = _height - event.y;
262
263   event.button = ea.getButton();
264   event.state = ea.getButtonMask();
265   event.mod = ea.getModKeyMask();
266   event.scroll = ea.getScrollingMotion();
267
268   canvas::WindowPtr window_at_cursor;
269   for( int i = _transform->getNumChildren() - 1; i >= 0; --i )
270   {
271     osg::Group *layer = _transform->getChild(i)->asGroup();
272     assert(layer);
273     if( !layer->getNumChildren() )
274       continue;
275
276     for( int j = layer->getNumChildren() - 1; j >= 0; --j )
277     {
278       assert(layer->getChild(j)->getUserData());
279       canvas::WindowPtr window =
280         static_cast<WindowUserData*>(layer->getChild(j)->getUserData())->window;
281       if( window->getRegion().contains(event.x, event.y) )
282       {
283         window_at_cursor = window;
284         break;
285       }
286     }
287
288     if( window_at_cursor )
289       break;
290   }
291
292   canvas::WindowPtr target_window = window_at_cursor;
293   switch( ea.getEventType() )
294   {
295     case osgGA::GUIEventAdapter::PUSH:
296       _last_push = window_at_cursor;
297       break;
298     case osgGA::GUIEventAdapter::SCROLL:
299     case osgGA::GUIEventAdapter::MOVE:
300       break;
301
302     case osgGA::GUIEventAdapter::RELEASE:
303       if( !_last_push.expired() )
304         return false;
305
306       target_window = _last_push.lock();
307       _last_push.reset();
308       break;
309
310     case osgGA::GUIEventAdapter::DRAG:
311       target_window = _last_push.lock();
312       break;
313
314     default:
315       return false;
316   }
317
318   if( target_window )
319   {
320     event.dx = event.x - _last_x;
321     event.dy = event.y - _last_y;
322
323     _last_x = event.x;
324     _last_y = event.y;
325
326     // Let the event position be always relative to the top left window corner
327     event.x -= target_window->getRegion().x();
328     event.y -= target_window->getRegion().y();
329
330     return target_window->handleMouseEvent(event);
331   }
332   else
333     return false;
334 }
335
336 //------------------------------------------------------------------------------
337 void GUIMgr::handleResize(int x, int y, int width, int height)
338 {
339   if( _width == width && _height == height )
340     return;
341
342   _width = width;
343   _height = height;
344
345   // Origin should be at top left corner, therefore we need to mirror the y-axis
346   _transform->setMatrix(osg::Matrix(
347     1,  0, 0, 0,
348     0, -1, 0, 0,
349     0,  0, 1, 0,
350     0, _height, 0, 1
351   ));
352 }