]> git.mxchange.org Git - flightgear.git/blob - src/Canvas/window.cxx
Add window decoration support to Canvas GUI.
[flightgear.git] / src / Canvas / window.cxx
1 // Window for placing a Canvas onto it (for dialogs, menus, etc.)
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 "canvas_mgr.hxx"
20 #include "window.hxx"
21 #include <Main/globals.hxx>
22 #include <simgear/canvas/Canvas.hxx>
23 #include <simgear/scene/util/OsgMath.hxx>
24
25 #include <osgGA/GUIEventHandler>
26
27 #include <boost/algorithm/string/predicate.hpp>
28 #include <boost/foreach.hpp>
29
30 namespace canvas
31 {
32   //----------------------------------------------------------------------------
33   Window::Window(SGPropertyNode* node):
34     PropertyBasedElement(node),
35     _attributes_dirty(0),
36     _image(simgear::canvas::CanvasPtr(), node),
37     _resizable(false),
38     _capture_events(true),
39     _resize_top(node, "resize-top"),
40     _resize_right(node, "resize-right"),
41     _resize_bottom(node, "resize-bottom"),
42     _resize_left(node, "resize-left"),
43     _resize_status(node, "resize-status")
44   {
45     _image.removeListener();
46
47     node->setFloatValue("source/right", 1);
48     node->setFloatValue("source/bottom", 1);
49     node->setBoolValue("source/normalized", true);
50   }
51
52   //----------------------------------------------------------------------------
53   Window::~Window()
54   {
55     if( _canvas_decoration )
56       _canvas_decoration->destroy();
57   }
58
59   //----------------------------------------------------------------------------
60   void Window::update(double delta_time_sec)
61   {
62     _image.update(delta_time_sec);
63
64     if( _attributes_dirty & SHADOW )
65     {
66       float radius = get<float>("shadow-radius"),
67             inset = get<float>("shadow-inset"),
68             slice_width = radius + inset;
69
70       if( slice_width <= 1 || _canvas_content.expired() )
71       {
72         if( _image_shadow )
73         {
74           getGroup()->removeChild(_image_shadow->getMatrixTransform());
75           _image_shadow.reset();
76         }
77       }
78       else
79       {
80         if( !_image_shadow )
81         {
82           _image_shadow.reset(new simgear::canvas::Image(
83             _canvas_content,
84             _node->getChild("image-shadow", 0, true)
85           ));
86           _image_shadow->set<std::string>("file", "gui/images/shadow.png");
87           _image_shadow->set<float>("slice", 7);
88           _image_shadow->set<std::string>("fill", "#000000");
89           getGroup()->insertChild(0, _image_shadow->getMatrixTransform());
90         }
91
92         simgear::canvas::CanvasPtr canvas = _canvas_decoration
93                                           ? _canvas_decoration
94                                           : _canvas_content.lock();
95
96         _image_shadow->set<float>("slice-width", slice_width);
97         _image_shadow->set<int>("x", -radius);
98         _image_shadow->set<int>("y", -radius);
99         _image_shadow->set<int>("size[0]", canvas->getViewWidth() + 2 * radius);
100         _image_shadow->set<int>("size[1]", canvas->getViewHeight()+ 2 * radius);
101       }
102
103       _attributes_dirty &= ~SHADOW;
104     }
105
106     if( _image_shadow )
107       _image_shadow->update(delta_time_sec);
108   }
109
110   //----------------------------------------------------------------------------
111   void Window::valueChanged(SGPropertyNode * node)
112   {
113     bool handled = false;
114     if( node->getParent() == _node )
115     {
116       handled = true;
117       const std::string& name = node->getNameString();
118       if( name == "raise-top" )
119         doRaise(node);
120       else if( name  == "resize" )
121         _resizable = node->getBoolValue();
122       else if( name == "capture-events" )
123         _capture_events = node->getBoolValue();
124       else if( name == "decoration-border" )
125         parseDecorationBorder(node->getStringValue());
126       else if( boost::starts_with(name, "shadow-") )
127         _attributes_dirty |= SHADOW;
128       else
129         handled = false;
130     }
131
132     if( !handled )
133       _image.valueChanged(node);
134   }
135
136   //----------------------------------------------------------------------------
137   void Window::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
138   {
139     _image.childAdded(parent, child);
140   }
141
142   //----------------------------------------------------------------------------
143   void Window::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
144   {
145     _image.childRemoved(parent, child);
146   }
147
148   //----------------------------------------------------------------------------
149   osg::Group* Window::getGroup()
150   {
151     return _image.getMatrixTransform();
152   }
153
154   //----------------------------------------------------------------------------
155   const SGRect<float>& Window::getRegion() const
156   {
157     return _image.getRegion();
158   }
159
160   //----------------------------------------------------------------------------
161   const SGVec2<float> Window::getPosition() const
162   {
163     const osg::Matrix& m = _image.getMatrixTransform()->getMatrix();
164     return SGVec2<float>( m(3, 0), m(3, 1) );
165   }
166
167   //----------------------------------------------------------------------------
168   const SGRect<float> Window::getScreenRegion() const
169   {
170     return getPosition() + getRegion();
171   }
172
173   //----------------------------------------------------------------------------
174   void Window::setCanvas(simgear::canvas::CanvasPtr canvas)
175   {
176     _canvas_content = canvas;
177     _image.setSrcCanvas(canvas);
178   }
179
180   //----------------------------------------------------------------------------
181   simgear::canvas::CanvasWeakPtr Window::getCanvas() const
182   {
183     return _image.getSrcCanvas();
184   }
185
186   //----------------------------------------------------------------------------
187   simgear::canvas::CanvasPtr Window::getCanvasDecoration()
188   {
189     return _canvas_decoration;
190   }
191
192   //----------------------------------------------------------------------------
193   bool Window::isVisible() const
194   {
195     return _image.isVisible();
196   }
197
198   //----------------------------------------------------------------------------
199   bool Window::isResizable() const
200   {
201     return _resizable;
202   }
203
204   //----------------------------------------------------------------------------
205   bool Window::isCapturingEvents() const
206   {
207     return _capture_events;
208   }
209
210   //----------------------------------------------------------------------------
211   bool Window::handleMouseEvent(const simgear::canvas::MouseEventPtr& event)
212   {
213     return _image.handleEvent(event);
214   }
215
216   //----------------------------------------------------------------------------
217   void Window::handleResize(uint8_t mode, const osg::Vec2f& delta)
218   {
219     if( mode == NONE )
220     {
221       _resize_status = 0;
222       return;
223     }
224     else if( mode & INIT )
225     {
226       _resize_top    = getRegion().t();
227       _resize_right  = getRegion().r();
228       _resize_bottom = getRegion().b();
229       _resize_left   = getRegion().l();
230       _resize_status = 1;
231     }
232
233     if( mode & BOTTOM )
234       _resize_bottom += delta.y();
235     else if( mode & TOP )
236       _resize_top += delta.y();
237
238     if( mode & canvas::Window::RIGHT )
239       _resize_right += delta.x();
240     else if( mode & canvas::Window::LEFT )
241       _resize_left += delta.x();
242   }
243
244   //----------------------------------------------------------------------------
245   void Window::doRaise(SGPropertyNode* node_raise)
246   {
247     if( node_raise && !node_raise->getBoolValue() )
248       return;
249
250     BOOST_FOREACH(osg::Group* parent, getGroup()->getParents())
251     {
252       // Remove window...
253       parent->removeChild(getGroup());
254
255       // ...and add again as topmost window
256       parent->addChild(getGroup());
257     }
258
259     if( node_raise )
260       node_raise->setBoolValue(false);
261   }
262
263   //----------------------------------------------------------------------------
264   void Window::parseDecorationBorder( const std::string& str )
265   {
266     _decoration_border = simgear::CSSBorder::parse(str);
267     if( _decoration_border.isNone() )
268     {
269       simgear::canvas::CanvasPtr canvas_content = _canvas_content.lock();
270       _image.setSrcCanvas(canvas_content);
271       _image.set<int>("size[0]", canvas_content->getViewWidth());
272       _image.set<int>("size[1]", canvas_content->getViewHeight());
273
274       _image_content.reset();
275       _canvas_decoration->destroy();
276       _canvas_decoration.reset();
277       return;
278     }
279
280     simgear::canvas::CanvasPtr content = _canvas_content.lock();
281     if( !_canvas_decoration )
282     {
283       CanvasMgr* mgr =
284         dynamic_cast<CanvasMgr*>(globals->get_subsystem("Canvas"));
285
286       if( !mgr )
287       {
288         SG_LOG(SG_GENERAL, SG_WARN, "canvas::Window: no canvas manager!");
289         return;
290       }
291
292       _canvas_decoration = mgr->createCanvas("window-decoration");
293       _canvas_decoration->getProps()->setStringValue("background", "rgba(0,0,0,0)");
294       _image.setSrcCanvas(_canvas_decoration);
295
296       // Decoration should be drawn first...
297       _canvas_decoration->createGroup("decoration");
298
299       // ...to allow drawing the actual content on top of the decoration
300       _image_content =
301         boost::dynamic_pointer_cast<simgear::canvas::Image>(
302             _canvas_decoration->getRootGroup()->createChild("image", "content")
303         );
304       _image_content->setSrcCanvas(content);
305     }
306
307     simgear::CSSBorder::Offsets const border =
308       _decoration_border.getAbsOffsets(content->getViewport());
309
310     int outer_width  = border.l + content->getViewWidth()  + border.r,
311         outer_height = border.t + content->getViewHeight() + border.b;
312
313     _canvas_decoration->setSizeX( outer_width );
314     _canvas_decoration->setSizeY( outer_height );
315     _canvas_decoration->setViewWidth( outer_width );
316     _canvas_decoration->setViewHeight( outer_height );
317
318     _image.set<int>("size[0]", outer_width);
319     _image.set<int>("size[1]", outer_height);
320
321     assert(_image_content);
322     _image_content->set<int>("x", border.l);
323     _image_content->set<int>("y", border.t);
324   }
325
326 } // namespace canvas