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