]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasElement.hxx
Canvas: fix property inheritance.
[simgear.git] / simgear / canvas / elements / CanvasElement.hxx
1 // Interface for 2D Canvas elements
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 #ifndef CANVAS_ELEMENT_HXX_
20 #define CANVAS_ELEMENT_HXX_
21
22 #include <simgear/canvas/canvas_fwd.hxx>
23 #include <simgear/canvas/CanvasEvent.hxx>
24 #include <simgear/props/PropertyBasedElement.hxx>
25 #include <simgear/misc/stdint.hxx> // for uint32_t
26 #include <simgear/nasal/cppbind/Ghost.hxx>
27
28 #include <osg/BoundingBox>
29 #include <osg/MatrixTransform>
30
31 #include <boost/bind.hpp>
32 #include <boost/function.hpp>
33
34 namespace osg
35 {
36   class Drawable;
37 }
38
39 namespace simgear
40 {
41 namespace canvas
42 {
43
44   class Element:
45     public PropertyBasedElement
46   {
47     public:
48
49       /**
50        * Store pointer to window as user data
51        */
52       class OSGUserData:
53         public osg::Referenced
54       {
55         public:
56           ElementPtr element;
57           OSGUserData(ElementPtr element);
58       };
59
60       typedef boost::function<bool(Element&, const SGPropertyNode*)>
61               StyleSetterFunc;
62       typedef boost::function<void(Element&, const SGPropertyNode*)>
63               StyleSetterFuncUnchecked;
64       struct StyleSetter:
65         public SGReferenced
66       {
67         StyleSetterFunc func;
68         SGSharedPtr<StyleSetter> next;
69       };
70       struct StyleInfo
71       {
72         StyleSetter setter; ///!< Function(s) for setting this style
73         std::string type;   ///!< Interpolation type
74       };
75
76       /**
77        *
78        */
79       virtual ~Element() = 0;
80
81       virtual void setSelf(const PropertyBasedElementPtr& self);
82       virtual void onDestroy();
83
84       ElementWeakPtr getWeakPtr() const;
85
86       /**
87        * Called every frame to update internal state
88        *
89        * @param dt  Frame time in seconds
90        */
91       virtual void update(double dt);
92
93       naRef addEventListener(const nasal::CallContext& ctx);
94       virtual void clearEventListener();
95
96       virtual bool accept(EventVisitor& visitor);
97       virtual bool ascend(EventVisitor& visitor);
98       virtual bool traverse(EventVisitor& visitor);
99
100       virtual bool handleEvent(canvas::EventPtr event);
101
102       virtual bool hitBound( const osg::Vec2f& pos,
103                              const osg::Vec2f& local_pos ) const;
104
105       /**
106        * Get whether the element is visible or hidden (Can be changed with
107        * setting property "visible" accordingly).
108        */
109       bool isVisible() const;
110
111       osg::MatrixTransform* getMatrixTransform();
112       osg::MatrixTransform const* getMatrixTransform() const;
113
114       virtual void childAdded( SGPropertyNode * parent,
115                                SGPropertyNode * child );
116       virtual void childRemoved( SGPropertyNode * parent,
117                                  SGPropertyNode * child );
118       virtual void valueChanged(SGPropertyNode * child);
119
120       virtual bool setStyle( const SGPropertyNode* child,
121                              const StyleSetter* setter = 0 );
122
123       /**
124        * Set clipping shape
125        *
126        * @note Only "rect(<top>, <right>, <bottom>, <left>)" is supported
127        * @see http://www.w3.org/TR/CSS21/visufx.html#propdef-clip
128        */
129       void setClip(const std::string& clip);
130
131       /**
132        * Write the given bounding box to the property tree
133        */
134       void setBoundingBox(const osg::BoundingBox& bb);
135
136       /**
137        * Get bounding box with children/drawables transformed by passed matrix
138        */
139       virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
140
141       /**
142        * Create an canvas Element
143        *
144        * @tparam Derived    Type of element (needs to derive from Element)
145        */
146       template<typename Derived>
147       static
148       typename boost::enable_if<
149         boost::is_base_of<Element, Derived>,
150         ElementPtr
151       >::type create( const CanvasWeakPtr& canvas,
152                       const SGPropertyNode_ptr& node,
153                       const Style& style,
154                       Element* parent )
155       {
156         ElementPtr el( new Derived(canvas, node, style, parent) );
157         el->setSelf(el);
158         return el;
159       }
160
161     protected:
162
163       enum Attributes
164       {
165         BLEND_FUNC = 0x0001,
166         LAST_ATTRIBUTE  = BLEND_FUNC << 1
167       };
168
169       enum TransformType
170       {
171         TT_NONE,
172         TT_MATRIX,
173         TT_TRANSLATE,
174         TT_ROTATE,
175         TT_SCALE
176       };
177
178       CanvasWeakPtr _canvas;
179       Element      *_parent;
180
181       uint32_t _attributes_dirty;
182
183       bool _transform_dirty;
184       osg::observer_ptr<osg::MatrixTransform> _transform;
185       std::vector<TransformType>              _transform_types;
186
187       Style                             _style;
188       std::vector<SGPropertyNode_ptr>   _bounding_box;
189
190       typedef std::vector<EventListenerPtr> Listener;
191       typedef std::map<Event::Type, Listener> ListenerMap;
192
193       ListenerMap _listener;
194
195       typedef std::map<std::string, StyleInfo> StyleSetters;
196       static StyleSetters       _style_setters;
197
198       Element( const CanvasWeakPtr& canvas,
199                const SGPropertyNode_ptr& node,
200                const Style& parent_style,
201                Element* parent );
202
203       /**
204        * Returns false on first call and true on any successive call. Use to
205        * perform initialization tasks which are only required once per element
206        * type.
207        *
208        * @tparam Derived    (Derived) class type
209        */
210       template<class Derived>
211       bool isInit() const
212       {
213         static bool is_init = false;
214         if( is_init )
215           return true;
216
217         is_init = true;
218         return false;
219       }
220
221       /**
222        * Register a function for setting a style specified by the given property
223        *
224        * @param name    Property name
225        * @param type    Interpolation type
226        * @param setter  Setter function
227        *
228        * @tparam T1         Type of value used to retrieve value from property
229        *                    node
230        * @tparam T2         Type of value the setter function expects
231        * @tparam Derived    Type of class the setter can be applied to
232        *
233        * @note T1 needs to be convertible to T2
234        */
235       template<
236         typename T1,
237         typename T2,
238         class Derived
239       >
240       StyleSetter
241       addStyle( const std::string& name,
242                 const std::string& type,
243                 const boost::function<void (Derived&, T2)>& setter )
244       {
245         StyleInfo& style_info = _style_setters[ name ];
246         if( !type.empty() )
247         {
248           if( !style_info.type.empty() && type != style_info.type )
249             SG_LOG
250             (
251               SG_GENERAL,
252               SG_WARN,
253               "changing animation type for '" << name << "': "
254                 << style_info.type << " -> " << type
255             );
256
257           style_info.type = type;
258         }
259
260         StyleSetter* style = &style_info.setter;
261         while( style->next )
262           style = style->next;
263         if( style->func )
264           style = style->next = new StyleSetter;
265
266         style->func = boost::bind
267         (
268           &type_match<Derived>::call,
269           _1,
270           _2,
271           bindStyleSetter<T1>(name, setter)
272         );
273         return *style;
274       }
275
276       template<
277         typename T,
278         class Derived
279       >
280       StyleSetter
281       addStyle( const std::string& name,
282                 const std::string& type,
283                 const boost::function<void (Derived&, T)>& setter )
284       {
285         return addStyle<T, T>(name, type, setter);
286       }
287
288       template<
289         typename T,
290         class Derived
291       >
292       StyleSetter
293       addStyle( const std::string& name,
294                 const std::string& type,
295                 void (Derived::*setter)(T) )
296       {
297         return addStyle<T, T>
298         (
299           name,
300           type,
301           boost::function<void (Derived&, T)>(setter)
302         );
303       }
304
305       template<
306         typename T1,
307         typename T2,
308         class Derived
309       >
310       StyleSetterFunc
311       addStyle( const std::string& name,
312                 const std::string& type,
313                 void (Derived::*setter)(T2) )
314       {
315         return addStyle<T1>
316         (
317           name,
318           type,
319           boost::function<void (Derived&, T2)>(setter)
320         );
321       }
322
323       template<
324         class Derived
325       >
326       StyleSetter
327       addStyle( const std::string& name,
328                 const std::string& type,
329                 void (Derived::*setter)(const std::string&) )
330       {
331         return addStyle<const char*, const std::string&>
332         (
333           name,
334           type,
335           boost::function<void (Derived&, const std::string&)>(setter)
336         );
337       }
338
339       template<
340         typename T,
341         class Derived,
342         class Other,
343         class OtherRef
344       >
345       StyleSetter
346       addStyle( const std::string& name,
347                 const std::string& type,
348                 void (Other::*setter)(T),
349                 OtherRef Derived::*instance_ref )
350       {
351         return addStyle<T, T>(name, type, bindOther(setter, instance_ref));
352       }
353
354       template<
355         typename T1,
356         typename T2,
357         class Derived,
358         class Other,
359         class OtherRef
360       >
361       StyleSetter
362       addStyle( const std::string& name,
363                 const std::string& type,
364                 void (Other::*setter)(T2),
365                 OtherRef Derived::*instance_ref )
366       {
367         return addStyle<T1>(name, type, bindOther(setter, instance_ref));
368       }
369
370       template<
371         typename T1,
372         typename T2,
373         class Derived,
374         class Other,
375         class OtherRef
376       >
377       StyleSetter
378       addStyle( const std::string& name,
379                 const std::string& type,
380                 const boost::function<void (Other&, T2)>& setter,
381                 OtherRef Derived::*instance_ref )
382       {
383         return addStyle<T1>(name, type, bindOther(setter, instance_ref));
384       }
385
386       template<
387         class Derived,
388         class Other,
389         class OtherRef
390       >
391       StyleSetter
392       addStyle( const std::string& name,
393                 const std::string& type,
394                 void (Other::*setter)(const std::string&),
395                 OtherRef Derived::*instance_ref )
396       {
397         return addStyle<const char*, const std::string&>
398         (
399           name,
400           type,
401           boost::function<void (Other&, const std::string&)>(setter),
402           instance_ref
403         );
404       }
405
406       template<typename T, class Derived, class Other, class OtherRef>
407       boost::function<void (Derived&, T)>
408       bindOther( void (Other::*setter)(T), OtherRef Derived::*instance_ref )
409       {
410         return boost::bind(setter, boost::bind(instance_ref, _1), _2);
411       }
412
413       template<typename T, class Derived, class Other, class OtherRef>
414       boost::function<void (Derived&, T)>
415       bindOther( const boost::function<void (Other&, T)>& setter,
416                  OtherRef Derived::*instance_ref )
417       {
418         return boost::bind
419         (
420           setter,
421           boost::bind
422           (
423             &reference_from_pointer<Other, OtherRef>,
424             boost::bind(instance_ref, _1)
425           ),
426           _2
427         );
428       }
429
430       template<typename T1, typename T2, class Derived>
431       StyleSetterFuncUnchecked
432       bindStyleSetter( const std::string& name,
433                        const boost::function<void (Derived&, T2)>& setter )
434       {
435         return boost::bind
436         (
437           setter,
438           // We will only call setters with Derived instances, so we can safely
439           // cast here.
440           boost::bind(&derived_cast<Derived>, _1),
441           boost::bind(&getValue<T1>, _2)
442         );
443       }
444
445       bool isStyleEmpty(const SGPropertyNode* child) const;
446       bool canApplyStyle(const SGPropertyNode* child) const;
447       bool setStyleImpl( const SGPropertyNode* child,
448                          const StyleSetter* setter = 0 );
449
450       const StyleSetter* getStyleSetter(const std::string& name) const;
451       const SGPropertyNode* getParentStyle(const SGPropertyNode* child) const;
452
453       virtual void childAdded(SGPropertyNode * child)  {}
454       virtual void childRemoved(SGPropertyNode * child){}
455       virtual void childChanged(SGPropertyNode * child){}
456
457       void setDrawable(osg::Drawable* drawable);
458
459       /**
460        * Get stateset of drawable if available or use transform otherwise
461        */
462       osg::StateSet* getOrCreateStateSet();
463
464       void setupStyle();
465
466     private:
467
468       osg::ref_ptr<osg::Drawable> _drawable;
469
470       Element(const Element&);// = delete
471
472       template<class Derived>
473       static Derived& derived_cast(Element& el)
474       {
475         return static_cast<Derived&>(el);
476       }
477
478       template<class T, class SharedPtr>
479       static T& reference_from_pointer(const SharedPtr& p)
480       {
481         return *get_pointer(p);
482       }
483
484       /**
485        * Helper to call a function only of the element type can be converted to
486        * the required type.
487        *
488        * @return Whether the function has been called
489        */
490       template<class Derived>
491       struct type_match
492       {
493         static bool call( Element& el,
494                           const SGPropertyNode* prop,
495                           const StyleSetterFuncUnchecked& func )
496         {
497           Derived* d = dynamic_cast<Derived*>(&el);
498           if( !d )
499             return false;
500           func(*d, prop);
501           return true;
502         }
503       };
504   };
505
506 } // namespace canvas
507 } // namespace simgear
508
509 #endif /* CANVAS_ELEMENT_HXX_ */