]> git.mxchange.org Git - flightgear.git/blob - src/Canvas/gui_mgr.cxx
Canvas: Image/Window unifying and allow using canvas inside canvas.
[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 typedef boost::shared_ptr<canvas::Window> WindowPtr;
88 WindowPtr windowFactory(SGPropertyNode* node)
89 {
90   return WindowPtr(new canvas::Window(node));
91 }
92
93 //------------------------------------------------------------------------------
94 GUIMgr::GUIMgr():
95   PropertyBasedMgr("/sim/gui/canvas", "window", &windowFactory),
96   _event_handler( new GUIEventHandler(this) ),
97   _transform( new osg::MatrixTransform ),
98   _width(_props, "size[0]"),
99   _height(_props, "size[1]"),
100   _last_push(-1)
101 {
102   _width = _height = -1;
103
104   osg::Camera* camera =
105     flightgear::getGUICamera( flightgear::CameraGroup::getDefault() );
106   assert(camera);
107   camera->addChild(_transform);
108
109   osg::Viewport* vp = camera->getViewport();
110   handleResize(vp->x(), vp->y(), vp->width(), vp->height());
111
112   Canvas::addPlacementFactory
113   (
114     "window",
115     boost::bind(&GUIMgr::addPlacement, this, _1, _2)
116   );
117
118   osg::StateSet* stateSet = _transform->getOrCreateStateSet();
119   stateSet->setDataVariance(osg::Object::STATIC);
120   stateSet->setRenderBinDetails(1000, "RenderBin");
121
122   // speed optimization?
123   stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
124   stateSet->setAttribute(new osg::BlendFunc(
125     osg::BlendFunc::SRC_ALPHA,
126     osg::BlendFunc::ONE_MINUS_SRC_ALPHA)
127   );
128   stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
129   stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
130   stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
131   stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
132 }
133
134 //------------------------------------------------------------------------------
135 void GUIMgr::init()
136 {
137   PropertyBasedMgr::init();
138
139   globals->get_renderer()
140          ->getViewer()
141          ->addEventHandler( _event_handler );
142 }
143
144 //------------------------------------------------------------------------------
145 void GUIMgr::shutdown()
146 {
147   PropertyBasedMgr::shutdown();
148
149   globals->get_renderer()
150          ->getViewer()
151          ->removeEventHandler( _event_handler );
152 }
153
154 //------------------------------------------------------------------------------
155 void GUIMgr::elementCreated(PropertyBasedElementPtr element)
156 {
157   _transform->addChild
158   (
159     static_cast<canvas::Window*>(element.get())->getGroup()
160   );
161 }
162
163 //------------------------------------------------------------------------------
164 bool GUIMgr::handleEvent(const osgGA::GUIEventAdapter& ea)
165 {
166   switch( ea.getEventType() )
167   {
168     case osgGA::GUIEventAdapter::PUSH:
169     case osgGA::GUIEventAdapter::RELEASE:
170 //    case osgGA::GUIEventAdapter::DOUBLECLICK:
171 //    // DOUBLECLICK doesn't seem to be triggered...
172     case osgGA::GUIEventAdapter::DRAG:
173     case osgGA::GUIEventAdapter::MOVE:
174     case osgGA::GUIEventAdapter::SCROLL:
175       return handleMouse(ea);
176 //        case osgGA::GUIEventAdapter::MOVE:
177 //          std::cout << "MOVE" << std::endl;
178 //          break;
179     case osgGA::GUIEventAdapter::RESIZE:
180       handleResize( ea.getWindowX(),
181                     ea.getWindowY(),
182                     ea.getWindowWidth(),
183                     ea.getWindowHeight() );
184       return true;
185     default:
186       return false;
187   }
188 }
189
190 //------------------------------------------------------------------------------
191 canvas::WindowPtr GUIMgr::getWindow(size_t i)
192 {
193   return boost::static_pointer_cast<canvas::Window>(_elements[i]);
194 }
195
196 //------------------------------------------------------------------------------
197 canvas::Placements GUIMgr::addPlacement( const SGPropertyNode* node,
198                                          CanvasPtr canvas )
199 {
200   int placement_index = node->getIntValue("index", -1);
201
202   canvas::Placements placements;
203   for( size_t i = 0; i < _elements.size(); ++i )
204   {
205     if( placement_index > 0 && static_cast<int>(i) != placement_index )
206       continue;
207
208     canvas::WindowPtr window = getWindow(i);
209     if( !window )
210       continue;
211
212     window->setCanvas(canvas);
213     placements.push_back(
214       canvas::PlacementPtr(new WindowPlacement(window, canvas))
215     );
216   }
217   return placements;
218 }
219
220 //------------------------------------------------------------------------------
221 bool GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea)
222 {
223   canvas::MouseEvent event( ea.getEventType() );
224
225   event.x = 0.5 * (ea.getXnormalized() + 1) * _width + 0.5;
226   event.y = 0.5 * (ea.getYnormalized() + 1) * _height + 0.5;
227   if(    ea.getMouseYOrientation()
228       != osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS )
229     event.y = _height - event.y;
230
231   event.button = ea.getButton();
232   event.state = ea.getButtonMask();
233   event.mod = ea.getModKeyMask();
234   event.scroll = ea.getScrollingMotion();
235
236   int window_at_cursor = -1;
237   for( size_t i = 0; i < _elements.size(); ++i )
238   {
239     if(    _elements[i]
240         && getWindow(i)->getRegion().contains(event.x, event.y) )
241     {
242       window_at_cursor = i;
243       break;
244     }
245   }
246
247   int target_window = window_at_cursor;
248   switch( ea.getEventType() )
249   {
250     case osgGA::GUIEventAdapter::PUSH:
251       _last_push = window_at_cursor;
252       break;
253     case osgGA::GUIEventAdapter::SCROLL:
254     case osgGA::GUIEventAdapter::MOVE:
255       break;
256
257     case osgGA::GUIEventAdapter::RELEASE:
258       if( _last_push < 0 )
259         return false;
260
261       target_window = _last_push;
262       _last_push = -1;
263       break;
264
265     case osgGA::GUIEventAdapter::DRAG:
266       target_window = _last_push;
267       break;
268
269     default:
270       return false;
271   }
272
273   if( target_window >= 0 )
274   {
275     canvas::WindowPtr window = getWindow(target_window);
276
277     event.dx = event.x - _last_x;
278     event.dy = event.y - _last_y;
279
280     _last_x = event.x;
281     _last_y = event.y;
282
283     // Let the event position be always relative to the top left window corner
284     event.x -= window->getRegion().x();
285     event.y -= window->getRegion().y();
286
287     return window->handleMouseEvent(event);
288   }
289   else
290     return false;
291 }
292
293 //------------------------------------------------------------------------------
294 void GUIMgr::handleResize(int x, int y, int width, int height)
295 {
296   if( _width == width && _height == height )
297     return;
298
299   _width = width;
300   _height = height;
301
302   // Origin should be at top left corner, therefore we need to mirror the y-axis
303   _transform->setMatrix(osg::Matrix(
304     1,  0, 0, 0,
305     0, -1, 0, 0,
306     0,  0, 1, 0,
307     0, _height, 0, 1
308   ));
309 }