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