]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/CanvasWindow.cxx
canvas::Layout: support for hiding items.
[simgear.git] / simgear / canvas / CanvasWindow.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 library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Library General Public
7 // License as published by the Free Software Foundation; either
8 // version 2 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // Library General Public License for more details.
14 //
15 // You should have received a copy of the GNU Library General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
18
19 #include "CanvasMgr.hxx"
20 #include "CanvasSystemAdapter.hxx"
21 #include "CanvasWindow.hxx"
22
23 #include <simgear/canvas/Canvas.hxx>
24 #include <simgear/scene/util/OsgMath.hxx>
25
26 #include <osgGA/GUIEventHandler>
27
28 #include <boost/algorithm/string/predicate.hpp>
29 #include <boost/foreach.hpp>
30
31 namespace simgear
32 {
33 namespace canvas
34 {
35
36   //----------------------------------------------------------------------------
37   const std::string Window::TYPE_NAME = "window";
38
39   //----------------------------------------------------------------------------
40   Window::Window( const CanvasWeakPtr& canvas,
41                   const SGPropertyNode_ptr& node,
42                   const Style& parent_style,
43                   Element* parent ):
44     Image(canvas, node, parent_style, parent),
45     _attributes_dirty(0),
46     _resizable(false),
47     _capture_events(true),
48     _resize_top(node, "resize-top"),
49     _resize_right(node, "resize-right"),
50     _resize_bottom(node, "resize-bottom"),
51     _resize_left(node, "resize-left"),
52     _resize_status(node, "resize-status")
53   {
54     node->setFloatValue("source/right", 1);
55     node->setFloatValue("source/bottom", 1);
56     node->setBoolValue("source/normalized", true);
57   }
58
59   //----------------------------------------------------------------------------
60   Window::~Window()
61   {
62     if( _canvas_decoration )
63       _canvas_decoration->destroy();
64   }
65
66   //----------------------------------------------------------------------------
67   void Window::update(double delta_time_sec)
68   {
69     if( _attributes_dirty & DECORATION )
70     {
71       updateDecoration();
72       _attributes_dirty &= ~DECORATION;
73     }
74
75     Image::update(delta_time_sec);
76   }
77
78   //----------------------------------------------------------------------------
79   void Window::valueChanged(SGPropertyNode * node)
80   {
81     bool handled = false;
82     if( node->getParent() == _node )
83     {
84       handled = true;
85       const std::string& name = node->getNameString();
86       if( name  == "resize" )
87         _resizable = node->getBoolValue();
88       else if( name == "update" )
89         update(0);
90       else if( name == "capture-events" )
91         _capture_events = node->getBoolValue();
92       else if( name == "decoration-border" )
93         parseDecorationBorder(node->getStringValue());
94       else if(    boost::starts_with(name, "shadow-")
95                || name == "content-size" )
96         _attributes_dirty |= DECORATION;
97       else
98         handled = false;
99     }
100
101     if( !handled )
102       Image::valueChanged(node);
103   }
104
105   //----------------------------------------------------------------------------
106   osg::Group* Window::getGroup()
107   {
108     return getMatrixTransform();
109   }
110
111   //----------------------------------------------------------------------------
112   const SGVec2<float> Window::getPosition() const
113   {
114     const osg::Matrix& m = getMatrixTransform()->getMatrix();
115     return SGVec2<float>( m(3, 0), m(3, 1) );
116   }
117
118   //----------------------------------------------------------------------------
119   const SGRect<float> Window::getScreenRegion() const
120   {
121     return getPosition() + getRegion();
122   }
123
124   //----------------------------------------------------------------------------
125   void Window::setCanvasContent(CanvasPtr canvas)
126   {
127     _canvas_content = canvas;
128     if( _layout )
129     {
130       canvas->setLayout(_layout);
131       _layout.clear();
132     }
133
134     if( _image_content )
135       // Placement within decoration canvas
136       _image_content->setSrcCanvas(canvas);
137     else
138       setSrcCanvas(canvas);
139   }
140
141   //----------------------------------------------------------------------------
142   CanvasWeakPtr Window::getCanvasContent() const
143   {
144     return _canvas_content;
145   }
146
147   //----------------------------------------------------------------------------
148   void Window::setLayout(const LayoutRef& layout)
149   {
150     CanvasPtr canvas = _canvas_content.lock();
151     if( canvas )
152       canvas->setLayout(layout);
153     else
154       _layout = layout; // keep layout until content canvas is set
155   }
156
157   //----------------------------------------------------------------------------
158   CanvasPtr Window::getCanvasDecoration() const
159   {
160     return _canvas_decoration;
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   void Window::setVisible(bool visible)
177   {
178     LayoutItem::setVisible(visible);
179     Element::setVisible(LayoutItem::isVisible());
180   }
181
182   //----------------------------------------------------------------------------
183   bool Window::isVisible() const
184   {
185     return Element::isVisible();
186   }
187
188   //----------------------------------------------------------------------------
189   void Window::raise()
190   {
191     // on writing the z-index the window always is moved to the top of all other
192     // windows with the same z-index.
193     set<int>("z-index", get<int>("z-index", 0));
194   }
195
196   //----------------------------------------------------------------------------
197   void Window::handleResize( uint8_t mode,
198                              const osg::Vec2f& offset )
199   {
200     if( mode == NONE )
201     {
202       _resize_status = 0;
203       return;
204     }
205     else if( mode & INIT )
206     {
207       _resize_top    = getRegion().t();
208       _resize_right  = getRegion().r();
209       _resize_bottom = getRegion().b();
210       _resize_left   = getRegion().l();
211       _resize_status = 1;
212     }
213
214     if( mode & BOTTOM )
215       _resize_bottom = getRegion().b() + offset.y();
216     else if( mode & TOP )
217       _resize_top = getRegion().t() + offset.y();
218
219     if( mode & canvas::Window::RIGHT )
220       _resize_right = getRegion().r() + offset.x();
221     else if( mode & canvas::Window::LEFT )
222       _resize_left = getRegion().l() + offset.x();
223   }
224
225   //----------------------------------------------------------------------------
226   void Window::parseDecorationBorder(const std::string& str)
227   {
228     _decoration_border = simgear::CSSBorder::parse(str);
229     _attributes_dirty |= DECORATION;
230   }
231
232   //----------------------------------------------------------------------------
233   void Window::updateDecoration()
234   {
235     int shadow_radius = get<float>("shadow-radius") + 0.5;
236     if( shadow_radius < 2 )
237       shadow_radius = 0;
238
239     CanvasPtr content = _canvas_content.lock();
240     SGRect<int> content_view
241     (
242       0,
243       0,
244       get<int>("content-size[0]", content ? content->getViewWidth()  : 400),
245       get<int>("content-size[1]", content ? content->getViewHeight() : 300)
246     );
247
248     if( _decoration_border.isNone() && !shadow_radius )
249     {
250       setSrcCanvas(content);
251       set<int>("size[0]", content_view.width());
252       set<int>("size[1]", content_view.height());
253
254       _image_content.reset();
255       _image_shadow.reset();
256       if( _canvas_decoration )
257         _canvas_decoration->destroy();
258       _canvas_decoration.reset();
259       return;
260     }
261
262     if( !_canvas_decoration )
263     {
264       CanvasMgr* mgr = dynamic_cast<CanvasMgr*>(
265         Canvas::getSystemAdapter()->getSubsystem("Canvas")
266       );
267
268       if( !mgr )
269       {
270         SG_LOG(SG_GENERAL, SG_WARN, "canvas::Window: no canvas manager!");
271         return;
272       }
273
274       _canvas_decoration = mgr->createCanvas("window-decoration");
275       _canvas_decoration->getProps()
276                         ->setStringValue("background", "rgba(0,0,0,0)");
277       setSrcCanvas(_canvas_decoration);
278
279       _image_content = _canvas_decoration->getRootGroup()
280                                          ->createChild<Image>("content");
281       _image_content->setSrcCanvas(content);
282
283       // Draw content on top of decoration
284       _image_content->set<int>("z-index", 1);
285     }
286
287     GroupPtr group_decoration =
288       _canvas_decoration->getOrCreateGroup("decoration");
289     group_decoration->set<int>("tf/t[0]", shadow_radius);
290     group_decoration->set<int>("tf/t[1]", shadow_radius);
291     // TODO do we need clipping or shall we trust the decorator not to draw over
292     //      the shadow?
293
294     CSSBorder::Offsets const border =
295       _decoration_border.getAbsOffsets(content_view);
296
297     int shad2 = 2 * shadow_radius,
298         outer_width  = border.l + content_view.width()  + border.r + shad2,
299         outer_height = border.t + content_view.height() + border.b + shad2;
300
301     _canvas_decoration->setSizeX( outer_width );
302     _canvas_decoration->setSizeY( outer_height );
303     _canvas_decoration->setViewWidth( outer_width );
304     _canvas_decoration->setViewHeight( outer_height );
305
306     set<int>("size[0]", outer_width - shad2);
307     set<int>("size[1]", outer_height - shad2);
308     set<int>("outset", shadow_radius);
309
310     assert(_image_content);
311     _image_content->set<int>("x", shadow_radius + border.l);
312     _image_content->set<int>("y", shadow_radius + border.t);
313     _image_content->set<int>("size[0]", content_view.width());
314     _image_content->set<int>("size[1]", content_view.height());
315
316     if( !shadow_radius )
317     {
318       if( _image_shadow )
319       {
320         _image_shadow->destroy();
321         _image_shadow.reset();
322       }
323       return;
324     }
325
326     int shadow_inset = std::max<int>(get<float>("shadow-inset") + 0.5, 0),
327         slice_width  = shadow_radius + shadow_inset;
328
329     _image_shadow = _canvas_decoration->getRootGroup()
330                                       ->getOrCreateChild<Image>("shadow");
331     _image_shadow->set<std::string>("src", "gui/images/shadow.png");
332     _image_shadow->set<float>("slice", 7);
333     _image_shadow->set<std::string>("fill", "#000000");
334     _image_shadow->set<float>("slice-width", slice_width);
335     _image_shadow->set<int>("size[0]", outer_width);
336     _image_shadow->set<int>("size[1]", outer_height);
337
338     // Draw shadow below decoration
339     _image_shadow->set<int>("z-index", -1);
340   }
341
342 } // namespace canvas
343 } // namespace simgear