]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasElement.hxx
Fix #1783: repeated error message on console
[simgear.git] / simgear / canvas / elements / CanvasElement.hxx
1 ///@file
2 /// Interface for 2D Canvas elements
3 //
4 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Library General Public
8 // License as published by the Free Software Foundation; either
9 // version 2 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Library General Public License for more details.
15 //
16 // You should have received a copy of the GNU Library General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
19
20 #ifndef CANVAS_ELEMENT_HXX_
21 #define CANVAS_ELEMENT_HXX_
22
23 #include <simgear/canvas/canvas_fwd.hxx>
24 #include <simgear/canvas/CanvasEvent.hxx>
25 #include <simgear/props/PropertyBasedElement.hxx>
26 #include <simgear/misc/stdint.hxx> // for uint32_t
27
28 #include <osg/BoundingBox>
29 #include <osg/MatrixTransform>
30
31 #include <boost/bind.hpp>
32 #include <boost/function.hpp>
33 #include <boost/type_traits/is_base_of.hpp>
34
35 namespace osg
36 {
37   class Drawable;
38 }
39
40 namespace simgear
41 {
42 namespace canvas
43 {
44
45   /**
46    * Base class for Elements displayed inside a Canvas.
47    */
48   class Element:
49     public PropertyBasedElement
50   {
51     public:
52
53       /**
54        * Store pointer to window as user data
55        */
56       class OSGUserData:
57         public osg::Referenced
58       {
59         public:
60           ElementPtr element;
61           OSGUserData(ElementPtr element);
62       };
63
64       typedef boost::function<bool(Element&, const SGPropertyNode*)>
65               StyleSetterFunc;
66       typedef boost::function<void(Element&, const SGPropertyNode*)>
67               StyleSetterFuncUnchecked;
68       struct StyleSetter:
69         public SGReferenced
70       {
71         StyleSetterFunc func;
72         SGSharedPtr<StyleSetter> next;
73       };
74       struct StyleInfo
75       {
76         StyleSetter setter; ///< Function(s) for setting this style
77         std::string type;   ///< Interpolation type
78         bool inheritable;   ///< Whether children can inherit this style from
79                             ///  their parents
80       };
81
82       /**
83        * Coordinate reference frame (eg. "clip" property)
84        */
85       enum ReferenceFrame
86       {
87         GLOBAL, ///< Global coordinates
88         PARENT, ///< Coordinates relative to parent coordinate frame
89         LOCAL   ///< Coordinates relative to local coordinates (parent
90                 ///  coordinates with local transformations applied)
91       };
92
93       /**
94        *
95        */
96       virtual ~Element() = 0;
97       virtual void onDestroy();
98
99       ElementPtr getParent() const;
100       CanvasWeakPtr getCanvas() const;
101
102       /**
103        * Called every frame to update internal state
104        *
105        * @param dt  Frame time in seconds
106        */
107       virtual void update(double dt);
108
109       bool addEventListener(const std::string& type, const EventListener& cb);
110       virtual void clearEventListener();
111
112       /// Get (keyboard) input focus.
113       void setFocus();
114
115       virtual bool accept(EventVisitor& visitor);
116       virtual bool ascend(EventVisitor& visitor);
117       virtual bool traverse(EventVisitor& visitor);
118
119       /// Get the number of event handlers for the given type
120       size_t numEventHandler(int type) const;
121
122       virtual bool handleEvent(const EventPtr& event);
123       bool dispatchEvent(const EventPtr& event);
124
125       /**
126        *
127        * @param global_pos Position in global (canvas) coordinate frame
128        * @param parent_pos Position in parent coordinate frame
129        * @param local_pos  Position in local (element) coordinate frame
130        */
131       virtual bool hitBound( const osg::Vec2f& global_pos,
132                              const osg::Vec2f& parent_pos,
133                              const osg::Vec2f& local_pos ) const;
134
135       /**
136        * Set visibility of the element.
137        */
138       virtual void setVisible(bool visible);
139
140       /**
141        * Get whether the element is visible or hidden.
142        */
143       virtual bool isVisible() const;
144
145       osg::MatrixTransform* getMatrixTransform();
146       osg::MatrixTransform const* getMatrixTransform() const;
147
148       /**
149        * Transform position to local coordinages.
150        */
151       osg::Vec2f posToLocal(const osg::Vec2f& pos) const;
152
153       virtual void childAdded( SGPropertyNode * parent,
154                                SGPropertyNode * child );
155       virtual void childRemoved( SGPropertyNode * parent,
156                                  SGPropertyNode * child );
157       virtual void valueChanged(SGPropertyNode * child);
158
159       virtual bool setStyle( const SGPropertyNode* child,
160                              const StyleInfo* style_info = 0 );
161
162       /**
163        * Set clipping shape
164        *
165        * @note Only "rect(<top>, <right>, <bottom>, <left>)" is supported
166        * @see http://www.w3.org/TR/CSS21/visufx.html#propdef-clip
167        */
168       void setClip(const std::string& clip);
169
170       /**
171        * Clipping coordinates reference frame
172        */
173       void setClipFrame(ReferenceFrame rf);
174
175       /**
176        * Get bounding box (may not be as tight as bounding box returned by
177        * #getTightBoundingBox)
178        */
179       osg::BoundingBox getBoundingBox() const;
180
181       /**
182        * Get tight bounding box (child points are transformed to elements
183        * coordinate space before calculating the bounding box).
184        */
185       osg::BoundingBox getTightBoundingBox() const;
186
187       /**
188        * Get bounding box with children/drawables transformed by passed matrix
189        */
190       virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
191
192       /**
193        * Get the transformation matrix (product of all transforms)
194        */
195       osg::Matrix getMatrix() const;
196
197       /**
198        * Create an canvas Element
199        *
200        * @tparam Derived    Type of element (needs to derive from Element)
201        */
202       template<typename Derived>
203       static
204       typename boost::enable_if<
205         boost::is_base_of<Element, Derived>,
206         ElementPtr
207       >::type create( const CanvasWeakPtr& canvas,
208                       const SGPropertyNode_ptr& node,
209                       const Style& style = Style(),
210                       Element* parent = NULL )
211       {
212         return ElementPtr( new Derived(canvas, node, style, parent) );
213       }
214
215     protected:
216
217       enum Attributes
218       {
219         TRANSFORM       = 1,
220         BLEND_FUNC      = TRANSFORM << 1,
221         LAST_ATTRIBUTE  = BLEND_FUNC << 1
222       };
223
224       enum TransformType
225       {
226         TT_NONE,
227         TT_MATRIX,
228         TT_TRANSLATE,
229         TT_ROTATE,
230         TT_SCALE
231       };
232
233       class RelativeScissor;
234
235       CanvasWeakPtr _canvas;
236       Element      *_parent;
237
238       mutable uint32_t _attributes_dirty;
239
240       osg::observer_ptr<osg::MatrixTransform> _transform;
241       std::vector<TransformType>              _transform_types;
242
243       Style             _style;
244       RelativeScissor  *_scissor;
245
246       typedef std::vector<EventListener> Listener;
247       typedef std::map<int, Listener> ListenerMap;
248
249       ListenerMap _listener;
250
251       typedef std::map<std::string, StyleInfo> StyleSetters;
252       static StyleSetters _style_setters;
253
254       static void staticInit();
255
256       Element( const CanvasWeakPtr& canvas,
257                const SGPropertyNode_ptr& node,
258                const Style& parent_style,
259                Element* parent );
260
261       /**
262        * Returns false on first call and true on any successive call. Use to
263        * perform initialization tasks which are only required once per element
264        * type.
265        *
266        * @tparam Derived    (Derived) class type
267        */
268       template<class Derived>
269       static bool isInit()
270       {
271         static bool is_init = false;
272         if( is_init )
273           return true;
274
275         is_init = true;
276         return false;
277       }
278
279       /**
280        * Register a function for setting a style specified by the given property
281        *
282        * @param name        Property name
283        * @param type        Interpolation type
284        * @param setter      Setter function
285        * @param inheritable If this style propagates to child elements
286        *
287        * @tparam T1         Type of value used to retrieve value from property
288        *                    node
289        * @tparam T2         Type of value the setter function expects
290        * @tparam Derived    Type of class the setter can be applied to
291        *
292        * @note T1 needs to be convertible to T2
293        */
294       template<
295         typename T1,
296         typename T2,
297         class Derived
298       >
299       static
300       StyleSetter
301       addStyle( const std::string& name,
302                 const std::string& type,
303                 const boost::function<void (Derived&, T2)>& setter,
304                 bool inheritable = true )
305       {
306         StyleInfo& style_info = _style_setters[ name ];
307         if( !type.empty() )
308         {
309           if( !style_info.type.empty() && type != style_info.type )
310             SG_LOG
311             (
312               SG_GENERAL,
313               SG_WARN,
314               "changing animation type for '" << name << "': "
315                 << style_info.type << " -> " << type
316             );
317
318           style_info.type = type;
319         }
320         // TODO check if changed?
321         style_info.inheritable = inheritable;
322
323         StyleSetter* style = &style_info.setter;
324         while( style->next )
325           style = style->next;
326         if( style->func )
327           style = style->next = new StyleSetter;
328
329         style->func = boost::bind
330         (
331           &type_match<Derived>::call,
332           _1,
333           _2,
334           bindStyleSetter<T1>(name, setter)
335         );
336         return *style;
337       }
338
339       template<
340         typename T,
341         class Derived
342       >
343       static
344       StyleSetter
345       addStyle( const std::string& name,
346                 const std::string& type,
347                 const boost::function<void (Derived&, T)>& setter,
348                 bool inheritable = true )
349       {
350         return addStyle<T, T>(name, type, setter, inheritable);
351       }
352
353       template<
354         typename T,
355         class Derived
356       >
357       static
358       StyleSetter
359       addStyle( const std::string& name,
360                 const std::string& type,
361                 void (Derived::*setter)(T),
362                 bool inheritable = true )
363       {
364         return addStyle<T, T>
365         (
366           name,
367           type,
368           boost::function<void (Derived&, T)>(setter),
369           inheritable
370         );
371       }
372
373       template<
374         typename T1,
375         typename T2,
376         class Derived
377       >
378       static
379       StyleSetterFunc
380       addStyle( const std::string& name,
381                 const std::string& type,
382                 void (Derived::*setter)(T2),
383                 bool inheritable = true )
384       {
385         return addStyle<T1>
386         (
387           name,
388           type,
389           boost::function<void (Derived&, T2)>(setter),
390           inheritable
391         );
392       }
393
394       template<
395         class Derived
396       >
397       static
398       StyleSetter
399       addStyle( const std::string& name,
400                 const std::string& type,
401                 void (Derived::*setter)(const std::string&),
402                 bool inheritable = true )
403       {
404         return addStyle<const char*, const std::string&>
405         (
406           name,
407           type,
408           boost::function<void (Derived&, const std::string&)>(setter),
409           inheritable
410         );
411       }
412
413       template<
414         typename T,
415         class Derived,
416         class Other,
417         class OtherRef
418       >
419       static
420       StyleSetter
421       addStyle( const std::string& name,
422                 const std::string& type,
423                 void (Other::*setter)(T),
424                 OtherRef Derived::*instance_ref,
425                 bool inheritable = true )
426       {
427         return addStyle<T, T>
428         (
429           name,
430           type,
431           bindOther(setter, instance_ref),
432           inheritable
433         );
434       }
435
436       template<
437         typename T1,
438         typename T2,
439         class Derived,
440         class Other,
441         class OtherRef
442       >
443       static
444       StyleSetter
445       addStyle( const std::string& name,
446                 const std::string& type,
447                 void (Other::*setter)(T2),
448                 OtherRef Derived::*instance_ref,
449                 bool inheritable = true )
450       {
451         return addStyle<T1>
452         (
453           name,
454           type,
455           bindOther(setter, instance_ref),
456           inheritable
457         );
458       }
459
460       template<
461         typename T1,
462         typename T2,
463         class Derived,
464         class Other,
465         class OtherRef
466       >
467       static
468       StyleSetter
469       addStyle( const std::string& name,
470                 const std::string& type,
471                 const boost::function<void (Other&, T2)>& setter,
472                 OtherRef Derived::*instance_ref,
473                 bool inheritable = true )
474       {
475         return addStyle<T1>
476         (
477           name,
478           type,
479           bindOther(setter, instance_ref),
480           inheritable
481         );
482       }
483
484       template<
485         class Derived,
486         class Other,
487         class OtherRef
488       >
489       static
490       StyleSetter
491       addStyle( const std::string& name,
492                 const std::string& type,
493                 void (Other::*setter)(const std::string&),
494                 OtherRef Derived::*instance_ref,
495                 bool inheritable = true )
496       {
497         return addStyle<const char*, const std::string&>
498         (
499           name,
500           type,
501           boost::function<void (Other&, const std::string&)>(setter),
502           instance_ref,
503           inheritable
504         );
505       }
506
507       template<typename T, class Derived, class Other, class OtherRef>
508       static
509       boost::function<void (Derived&, T)>
510       bindOther( void (Other::*setter)(T), OtherRef Derived::*instance_ref )
511       {
512         return boost::bind(setter, boost::bind(instance_ref, _1), _2);
513       }
514
515       template<typename T, class Derived, class Other, class OtherRef>
516       static
517       boost::function<void (Derived&, T)>
518       bindOther( const boost::function<void (Other&, T)>& setter,
519                  OtherRef Derived::*instance_ref )
520       {
521         return boost::bind
522         (
523           setter,
524           boost::bind
525           (
526             &reference_from_pointer<Other, OtherRef>,
527             boost::bind(instance_ref, _1)
528           ),
529           _2
530         );
531       }
532
533       template<typename T1, typename T2, class Derived>
534       static
535       StyleSetterFuncUnchecked
536       bindStyleSetter( const std::string& name,
537                        const boost::function<void (Derived&, T2)>& setter )
538       {
539         return boost::bind
540         (
541           setter,
542           // We will only call setters with Derived instances, so we can safely
543           // cast here.
544           boost::bind(&derived_cast<Derived>, _1),
545           boost::bind(&getValue<T1>, _2)
546         );
547       }
548
549       bool isStyleEmpty(const SGPropertyNode* child) const;
550       bool canApplyStyle(const SGPropertyNode* child) const;
551       bool setStyleImpl( const SGPropertyNode* child,
552                          const StyleInfo* style_info = 0 );
553
554       const StyleInfo* getStyleInfo(const std::string& name) const;
555       const StyleSetter* getStyleSetter(const std::string& name) const;
556       const SGPropertyNode* getParentStyle(const SGPropertyNode* child) const;
557
558       virtual void childAdded(SGPropertyNode * child)  {}
559       virtual void childRemoved(SGPropertyNode * child){}
560       virtual void childChanged(SGPropertyNode * child){}
561
562       void setDrawable(osg::Drawable* drawable);
563
564       /**
565        * Get stateset of drawable if available or use transform otherwise
566        */
567       virtual osg::StateSet* getOrCreateStateSet();
568
569       void setupStyle();
570
571     private:
572
573       osg::ref_ptr<osg::Drawable> _drawable;
574
575       Element(const Element&);// = delete
576
577       template<class Derived>
578       static Derived& derived_cast(Element& el)
579       {
580         return static_cast<Derived&>(el);
581       }
582
583       template<class T, class SharedPtr>
584       static T& reference_from_pointer(const SharedPtr& p)
585       {
586         return *get_pointer(p);
587       }
588
589       /**
590        * Helper to call a function only of the element type can be converted to
591        * the required type.
592        *
593        * @return Whether the function has been called
594        */
595       template<class Derived>
596       struct type_match
597       {
598         static bool call( Element& el,
599                           const SGPropertyNode* prop,
600                           const StyleSetterFuncUnchecked& func )
601         {
602           Derived* d = dynamic_cast<Derived*>(&el);
603           if( !d )
604             return false;
605           func(*d, prop);
606           return true;
607         }
608       };
609   };
610
611 } // namespace canvas
612
613   template<>
614   struct enum_traits<canvas::Element::ReferenceFrame>
615   {
616     static const char* name()
617     {
618       return "canvas::Element::ReferenceFrame";
619     }
620
621     static canvas::Element::ReferenceFrame defVal()
622     {
623       return canvas::Element::GLOBAL;
624     }
625
626     static bool validate(int frame)
627     {
628       return frame >= canvas::Element::GLOBAL
629           && frame <= canvas::Element::LOCAL;
630     }
631   };
632
633 } // namespace simgear
634
635 #endif /* CANVAS_ELEMENT_HXX_ */