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