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