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