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