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