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