]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasElement.hxx
Canvas: Fix crash on hide/show after detaching element from scenegraph.
[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
27 #include <osg/BoundingBox>
28 #include <osg/MatrixTransform>
29
30 #include <boost/bind.hpp>
31 #include <boost/function.hpp>
32 #include <boost/type_traits/is_base_of.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         bool inheritable;   ///!< Whether children can inherit this style from
75                             ///   their parents
76       };
77
78       /**
79        * Coordinate reference frame (eg. "clip" property)
80        */
81       enum ReferenceFrame
82       {
83         GLOBAL, ///!< Global coordinates
84         PARENT, ///!< Coordinates relative to parent coordinate frame
85         LOCAL   ///!< Coordinates relative to local coordinates (parent
86                 ///   coordinates with local transformations applied)
87       };
88
89       /**
90        *
91        */
92       virtual ~Element() = 0;
93
94       virtual void setSelf(const PropertyBasedElementPtr& self);
95       virtual void onDestroy();
96
97       ElementWeakPtr getWeakPtr() const;
98       ElementPtr getParent();
99
100       /**
101        * Called every frame to update internal state
102        *
103        * @param dt  Frame time in seconds
104        */
105       virtual void update(double dt);
106
107       bool addEventListener(const std::string& type, const EventListener& cb);
108       virtual void clearEventListener();
109
110       virtual bool accept(EventVisitor& visitor);
111       virtual bool ascend(EventVisitor& visitor);
112       virtual bool traverse(EventVisitor& visitor);
113
114       virtual bool handleEvent(canvas::EventPtr event);
115
116       virtual bool hitBound( const osg::Vec2f& pos,
117                              const osg::Vec2f& local_pos ) const;
118
119       /**
120        * Set visibility of the element.
121        */
122       void setVisible(bool visible);
123
124       /**
125        * Get whether the element is visible or hidden.
126        */
127       bool isVisible() const;
128
129       osg::MatrixTransform* getMatrixTransform();
130       osg::MatrixTransform const* getMatrixTransform() const;
131
132       virtual void childAdded( SGPropertyNode * parent,
133                                SGPropertyNode * child );
134       virtual void childRemoved( SGPropertyNode * parent,
135                                  SGPropertyNode * child );
136       virtual void valueChanged(SGPropertyNode * child);
137
138       virtual bool setStyle( const SGPropertyNode* child,
139                              const StyleInfo* style_info = 0 );
140
141       /**
142        * Set clipping shape
143        *
144        * @note Only "rect(<top>, <right>, <bottom>, <left>)" is supported
145        * @see http://www.w3.org/TR/CSS21/visufx.html#propdef-clip
146        */
147       void setClip(const std::string& clip);
148
149       /**
150        * Clipping coordinates reference frame
151        */
152       void setClipFrame(ReferenceFrame rf);
153
154       /**
155        * Write the given bounding box to the property tree
156        */
157       void setBoundingBox(const osg::BoundingBox& bb);
158
159       /**
160        * Get bounding box with children/drawables transformed by passed matrix
161        */
162       virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
163
164       /**
165        * Get the transformation matrix (product of all transforms)
166        */
167       osg::Matrix getMatrix() const;
168
169       /**
170        * Create an canvas Element
171        *
172        * @tparam Derived    Type of element (needs to derive from Element)
173        */
174       template<typename Derived>
175       static
176       typename boost::enable_if<
177         boost::is_base_of<Element, Derived>,
178         ElementPtr
179       >::type create( const CanvasWeakPtr& canvas,
180                       const SGPropertyNode_ptr& node,
181                       const Style& style,
182                       Element* parent )
183       {
184         ElementPtr el( new Derived(canvas, node, style, parent) );
185         el->setSelf(el);
186         return el;
187       }
188
189     protected:
190
191       enum Attributes
192       {
193         TRANSFORM       = 1,
194         BLEND_FUNC      = TRANSFORM << 1,
195         SCISSOR_COORDS  = BLEND_FUNC << 1,
196         LAST_ATTRIBUTE  = SCISSOR_COORDS << 1
197       };
198
199       enum TransformType
200       {
201         TT_NONE,
202         TT_MATRIX,
203         TT_TRANSLATE,
204         TT_ROTATE,
205         TT_SCALE
206       };
207
208       class RelativeScissor;
209
210       CanvasWeakPtr _canvas;
211       Element      *_parent;
212
213       mutable uint32_t _attributes_dirty;
214
215       osg::observer_ptr<osg::MatrixTransform> _transform;
216       std::vector<TransformType>              _transform_types;
217
218       Style                             _style;
219       std::vector<SGPropertyNode_ptr>   _bounding_box;
220       RelativeScissor                  *_scissor;
221
222       typedef std::vector<EventListener> Listener;
223       typedef std::map<Event::Type, Listener> ListenerMap;
224
225       ListenerMap _listener;
226
227       typedef std::map<std::string, StyleInfo> StyleSetters;
228       static StyleSetters       _style_setters;
229
230       static void staticInit();
231
232       Element( const CanvasWeakPtr& canvas,
233                const SGPropertyNode_ptr& node,
234                const Style& parent_style,
235                Element* parent );
236
237       /**
238        * Returns false on first call and true on any successive call. Use to
239        * perform initialization tasks which are only required once per element
240        * type.
241        *
242        * @tparam Derived    (Derived) class type
243        */
244       template<class Derived>
245       static bool isInit()
246       {
247         static bool is_init = false;
248         if( is_init )
249           return true;
250
251         is_init = true;
252         return false;
253       }
254
255       /**
256        * Register a function for setting a style specified by the given property
257        *
258        * @param name    Property name
259        * @param type    Interpolation type
260        * @param setter  Setter function
261        *
262        * @tparam T1         Type of value used to retrieve value from property
263        *                    node
264        * @tparam T2         Type of value the setter function expects
265        * @tparam Derived    Type of class the setter can be applied to
266        *
267        * @note T1 needs to be convertible to T2
268        */
269       template<
270         typename T1,
271         typename T2,
272         class Derived
273       >
274       static
275       StyleSetter
276       addStyle( const std::string& name,
277                 const std::string& type,
278                 const boost::function<void (Derived&, T2)>& setter,
279                 bool inheritable = true )
280       {
281         StyleInfo& style_info = _style_setters[ name ];
282         if( !type.empty() )
283         {
284           if( !style_info.type.empty() && type != style_info.type )
285             SG_LOG
286             (
287               SG_GENERAL,
288               SG_WARN,
289               "changing animation type for '" << name << "': "
290                 << style_info.type << " -> " << type
291             );
292
293           style_info.type = type;
294         }
295         // TODO check if changed?
296         style_info.inheritable = inheritable;
297
298         StyleSetter* style = &style_info.setter;
299         while( style->next )
300           style = style->next;
301         if( style->func )
302           style = style->next = new StyleSetter;
303
304         style->func = boost::bind
305         (
306           &type_match<Derived>::call,
307           _1,
308           _2,
309           bindStyleSetter<T1>(name, setter)
310         );
311         return *style;
312       }
313
314       template<
315         typename T,
316         class Derived
317       >
318       static
319       StyleSetter
320       addStyle( const std::string& name,
321                 const std::string& type,
322                 const boost::function<void (Derived&, T)>& setter,
323                 bool inheritable = true )
324       {
325         return addStyle<T, T>(name, type, setter, inheritable);
326       }
327
328       template<
329         typename T,
330         class Derived
331       >
332       static
333       StyleSetter
334       addStyle( const std::string& name,
335                 const std::string& type,
336                 void (Derived::*setter)(T),
337                 bool inheritable = true )
338       {
339         return addStyle<T, T>
340         (
341           name,
342           type,
343           boost::function<void (Derived&, T)>(setter),
344           inheritable
345         );
346       }
347
348       template<
349         typename T1,
350         typename T2,
351         class Derived
352       >
353       static
354       StyleSetterFunc
355       addStyle( const std::string& name,
356                 const std::string& type,
357                 void (Derived::*setter)(T2),
358                 bool inheritable = true )
359       {
360         return addStyle<T1>
361         (
362           name,
363           type,
364           boost::function<void (Derived&, T2)>(setter),
365           inheritable
366         );
367       }
368
369       template<
370         class Derived
371       >
372       static
373       StyleSetter
374       addStyle( const std::string& name,
375                 const std::string& type,
376                 void (Derived::*setter)(const std::string&),
377                 bool inheritable = true )
378       {
379         return addStyle<const char*, const std::string&>
380         (
381           name,
382           type,
383           boost::function<void (Derived&, const std::string&)>(setter),
384           inheritable
385         );
386       }
387
388       template<
389         typename T,
390         class Derived,
391         class Other,
392         class OtherRef
393       >
394       static
395       StyleSetter
396       addStyle( const std::string& name,
397                 const std::string& type,
398                 void (Other::*setter)(T),
399                 OtherRef Derived::*instance_ref,
400                 bool inheritable = true )
401       {
402         return addStyle<T, T>
403         (
404           name,
405           type,
406           bindOther(setter, instance_ref),
407           inheritable
408         );
409       }
410
411       template<
412         typename T1,
413         typename T2,
414         class Derived,
415         class Other,
416         class OtherRef
417       >
418       static
419       StyleSetter
420       addStyle( const std::string& name,
421                 const std::string& type,
422                 void (Other::*setter)(T2),
423                 OtherRef Derived::*instance_ref,
424                 bool inheritable = true )
425       {
426         return addStyle<T1>
427         (
428           name,
429           type,
430           bindOther(setter, instance_ref),
431           inheritable
432         );
433       }
434
435       template<
436         typename T1,
437         typename T2,
438         class Derived,
439         class Other,
440         class OtherRef
441       >
442       static
443       StyleSetter
444       addStyle( const std::string& name,
445                 const std::string& type,
446                 const boost::function<void (Other&, T2)>& setter,
447                 OtherRef Derived::*instance_ref,
448                 bool inheritable = true )
449       {
450         return addStyle<T1>
451         (
452           name,
453           type,
454           bindOther(setter, instance_ref),
455           inheritable
456         );
457       }
458
459       template<
460         class Derived,
461         class Other,
462         class OtherRef
463       >
464       static
465       StyleSetter
466       addStyle( const std::string& name,
467                 const std::string& type,
468                 void (Other::*setter)(const std::string&),
469                 OtherRef Derived::*instance_ref,
470                 bool inheritable = true )
471       {
472         return addStyle<const char*, const std::string&>
473         (
474           name,
475           type,
476           boost::function<void (Other&, const std::string&)>(setter),
477           instance_ref,
478           inheritable
479         );
480       }
481
482       template<typename T, class Derived, class Other, class OtherRef>
483       static
484       boost::function<void (Derived&, T)>
485       bindOther( void (Other::*setter)(T), OtherRef Derived::*instance_ref )
486       {
487         return boost::bind(setter, boost::bind(instance_ref, _1), _2);
488       }
489
490       template<typename T, class Derived, class Other, class OtherRef>
491       static
492       boost::function<void (Derived&, T)>
493       bindOther( const boost::function<void (Other&, T)>& setter,
494                  OtherRef Derived::*instance_ref )
495       {
496         return boost::bind
497         (
498           setter,
499           boost::bind
500           (
501             &reference_from_pointer<Other, OtherRef>,
502             boost::bind(instance_ref, _1)
503           ),
504           _2
505         );
506       }
507
508       template<typename T1, typename T2, class Derived>
509       static
510       StyleSetterFuncUnchecked
511       bindStyleSetter( const std::string& name,
512                        const boost::function<void (Derived&, T2)>& setter )
513       {
514         return boost::bind
515         (
516           setter,
517           // We will only call setters with Derived instances, so we can safely
518           // cast here.
519           boost::bind(&derived_cast<Derived>, _1),
520           boost::bind(&getValue<T1>, _2)
521         );
522       }
523
524       bool isStyleEmpty(const SGPropertyNode* child) const;
525       bool canApplyStyle(const SGPropertyNode* child) const;
526       bool setStyleImpl( const SGPropertyNode* child,
527                          const StyleInfo* style_info = 0 );
528
529       const StyleInfo* getStyleInfo(const std::string& name) const;
530       const StyleSetter* getStyleSetter(const std::string& name) const;
531       const SGPropertyNode* getParentStyle(const SGPropertyNode* child) const;
532
533       virtual void childAdded(SGPropertyNode * child)  {}
534       virtual void childRemoved(SGPropertyNode * child){}
535       virtual void childChanged(SGPropertyNode * child){}
536
537       void setDrawable(osg::Drawable* drawable);
538
539       /**
540        * Get stateset of drawable if available or use transform otherwise
541        */
542       osg::StateSet* getOrCreateStateSet();
543
544       void setupStyle();
545
546     private:
547
548       osg::ref_ptr<osg::Drawable> _drawable;
549
550       Element(const Element&);// = delete
551
552       template<class Derived>
553       static Derived& derived_cast(Element& el)
554       {
555         return static_cast<Derived&>(el);
556       }
557
558       template<class T, class SharedPtr>
559       static T& reference_from_pointer(const SharedPtr& p)
560       {
561         return *get_pointer(p);
562       }
563
564       /**
565        * Helper to call a function only of the element type can be converted to
566        * the required type.
567        *
568        * @return Whether the function has been called
569        */
570       template<class Derived>
571       struct type_match
572       {
573         static bool call( Element& el,
574                           const SGPropertyNode* prop,
575                           const StyleSetterFuncUnchecked& func )
576         {
577           Derived* d = dynamic_cast<Derived*>(&el);
578           if( !d )
579             return false;
580           func(*d, prop);
581           return true;
582         }
583       };
584   };
585
586 } // namespace canvas
587
588   template<>
589   struct enum_traits<canvas::Element::ReferenceFrame>
590   {
591     static const char* name()
592     {
593       return "canvas::Element::ReferenceFrame";
594     }
595
596     static canvas::Element::ReferenceFrame defVal()
597     {
598       return canvas::Element::GLOBAL;
599     }
600
601     static bool validate(int frame)
602     {
603       return frame >= canvas::Element::GLOBAL
604           && frame <= canvas::Element::LOCAL;
605     }
606   };
607
608 } // namespace simgear
609
610 #endif /* CANVAS_ELEMENT_HXX_ */