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