bd9fecbbae11ce299b12e7b69cd548c52d32cf11
[platform/core/uifw/dali-toolkit.git] / plugins / dali-swig / manual / csharp / ViewRegistry.cs
1 using System;
2 using System.Runtime.InteropServices;
3 using System.Collections.Generic;
4
5 namespace Dali
6 {
7   /// <summary>
8   /// Add this attribute to any property belonging to a View (control) you want to be scriptable from JSON
9   /// </summary>
10   /// <remarks>
11   /// Example:
12   ///
13   /// class MyView : public CustomView
14   /// {
15   ///  [ScriptableProperty()]
16   ///  public int MyProperty
17   ///  {
18   ///   get
19   ///   {
20   ///     return _myProperty;
21   ///   }
22   ///   set
23   ///   {
24   ///    _myProperty = value;
25   ///   }
26   ///  }
27   /// }
28   ///
29   /// Internally the following occurs for property registration ( this only occurs once per Type, not per Instance):
30   ///
31   /// - The controls static constructor should call ViewRegistry.Register()  (only called once for the lifecycle of the app)
32   /// - Within Register() the code will introspect the Controls properties, looking for the ScriptableProperty() attribute
33   /// - For every property with the ScriptableProperty() attribute, TypeRegistration.RegisterProperty is called.
34   /// - TypeRegistration.RegisterProperty calls in to DALi C++ Code Dali::CSharpTypeRegistry::RegisterProperty()
35   /// - DALi C++ now knows the existance of the property and will try calling SetProperty, if it finds the property in a JSON file (loaded using builder).
36   ///
37   ///  The DALi C# example
38   ///
39   ///  class MyView : public CustomView
40   ///  {
41   ///
42   ///    [ScriptableProperty()]
43   ///    public double Hours
44   ///    {
45   ///     get { return seconds / 3600; }
46   ///     set { seconds = value * 3600; }
47   ///    }
48   ///  }
49   ///
50   ///  Equivalent code in DALi C++:
51   ///  in MyControl.h
52   ///  class MyControl : public Control
53   ///  {
54   ///    struct Property
55   ///    {
56   ///      enum
57   ///      {
58   ///        HOURS =  Control::CONTROL_PROPERTY_END_INDEX + 1
59   ///      }
60   ///    }
61   ///  }
62   ///
63   /// in MyControl-impl.cpp
64   ///
65   /// DALI_TYPE_REGISTRATION_BEGIN( Toolkit::MyControl, Toolkit::Control, Create );
66   /// DALI_PROPERTY_REGISTRATION( Toolkit, MyControl, "Hours",  INTEGER, DISABLED                     )
67   /// DALI_TYPE_REGISTRATION_END()
68   /// </remarks>
69   ///
70   ///
71   public class ScriptableProperty : System.Attribute
72   {
73     public enum ScriptableType
74     {
75       Default,    // Read Writable, non-animatable property, event thread only
76     //  Animatable // Animatable property, Currently disabled, UK
77     }
78     public readonly ScriptableType type;
79
80     public ScriptableProperty(ScriptableType type = ScriptableType.Default )
81     {
82       this.type = type;
83     }
84   }
85
86   /// <summary>
87   /// View Registry singleton.
88   /// Used for registering controls and any scriptable properties they have ( see ScriptableProperty )
89   ///
90   /// Internal Design from C# to C++
91   ///
92   /// - Each custom C# view should have it's static constructor called before any JSON file is loaded.
93   /// Static constructors for a class will only run once ( they are run per control type, not per instance).
94   /// Example of running a static constructor:
95   ///      System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (typeof(Spin).TypeHandle);
96   /// Inside the static constructor the control should register it's type with the ViewRegistry
97   /// e.g.
98   ///
99   ///  static Spin()
100   ///  {
101   ///     ViewRegistry.Instance.Register(CreateInstance, typeof(Spin) );
102   ///  }
103   ///
104   ///  The control should also provide a CreateInstance function, which gets passed to the ViewRegistry
105   ///  // Eventually it will be called if DALi Builderfinds a Spin control in a JSON file
106   ///  static CustomView CreateInstance()
107   ///  {
108   ///    return new Spin();
109   ///  }
110   ///
111   ///
112   ///
113   /// The DALi C++ equivalent of this is
114   ///
115   ///  TypeRegistration mType( typeid(Toolkit::Spin), typeid(Toolkit::Control), CreateInstance );
116   ///
117   ///
118   ///
119   /// </summary>
120   public sealed class ViewRegistry
121   {
122     /// <summary>
123     /// ViewRegistry is a singleton
124     /// </summary>
125     private static ViewRegistry instance = null;
126
127     [UnmanagedFunctionPointer(CallingConvention.StdCall)]
128     delegate IntPtr CreateControlDelegate( IntPtr cPtrControlName );
129
130     [UnmanagedFunctionPointer(CallingConvention.StdCall)]
131     delegate IntPtr GetPropertyDelegate( IntPtr controlPtr, IntPtr propertyName );
132
133     [UnmanagedFunctionPointer(CallingConvention.StdCall)]
134     delegate void SetPropertyDelegate( IntPtr controlPtr, IntPtr propertyName, IntPtr propertyValue );
135
136     private CreateControlDelegate _createCallback;
137     private SetPropertyDelegate _setPropertyCallback;
138     private GetPropertyDelegate _getPropertyCallback;
139     private PropertyRangeManager _propertyRangeManager;
140
141     /// <summary>
142     /// Given a C++ control the dictionary allows us to find which C# control (View) it belongs to
143     /// </summary>
144     private Dictionary<IntPtr, Dali.View> _controlMap;
145
146     ///<summary>
147     // Maps the name of a custom view to a create instance function
148     /// E.g. given a string "Spin", we can get a function used to create the Spin View.
149     ///</summary>
150     private Dictionary<String, Func< CustomView > > _constructorMap;
151
152     /// <summary>
153     /// Lookup table to match C# types to DALi types, used for the automatic property registration
154     /// </summary>
155     private static readonly Dictionary<string, Dali.Property.Type> _daliPropertyTypeLookup
156     = new Dictionary< string, Dali.Property.Type  >
157     {
158       { "float",   Property.Type.FLOAT },
159       { "int",     Property.Type.INTEGER },
160       { "Int32",   Property.Type.INTEGER },
161       { "Boolean", Property.Type.BOOLEAN },
162       { "string",  Property.Type.STRING },
163       { "Vector2", Property.Type.VECTOR2 },
164       { "Vector3", Property.Type.VECTOR3 },
165       { "Vector4", Property.Type.VECTOR4 },
166       { "Size",    Property.Type.VECTOR2 },
167       { "Position",Property.Type.VECTOR3 },
168       { "Color",   Property.Type.VECTOR4 },
169     //  { "Matrix3", Property.Type.MATRIX3 }, commented out until we need to use Matrices from JSON
170     //  { "Matrix",  Property.Type.MATRIX },
171     };
172
173
174     public ViewRegistry()
175     {
176       _createCallback = new CreateControlDelegate( CreateControl );
177       _getPropertyCallback = new GetPropertyDelegate (GetProperty);
178       _setPropertyCallback  = new SetPropertyDelegate (SetProperty);
179
180       _controlMap = new Dictionary<IntPtr, View>();
181       _constructorMap = new Dictionary<string, Func<CustomView>>();
182       _propertyRangeManager = new PropertyRangeManager();
183
184     }
185
186     private Dali.Property.Type GetDaliPropertyType( string cSharpTypeName )
187     {
188       Dali.Property.Type daliType;
189       if ( _daliPropertyTypeLookup.TryGetValue (cSharpTypeName, out daliType) )
190       {
191         //Console.WriteLine("mapped "+ cSharpTypeName + " to dAli type " +daliType );
192         return daliType;
193       }
194       else
195       {
196        // Console.WriteLine("Failed to find a mapping between C# property" + cSharpTypeName +" and DALi type");
197         return Property.Type.NONE;
198       }
199     }
200
201     /// <summary>
202     /// Called directly from DALi C++ type registry to create a control (View) using no marshalling.
203     /// </summary>
204     /// <returns>Pointer to the Control (Views) handle </returns>
205     /// <param name="cPtrControlName"> C pointer to the Control (View) name</param>
206     private static IntPtr CreateControl( IntPtr cPtrControlName )
207     {
208       string controlName = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (cPtrControlName);
209       // Console.WriteLine ("Create controlled called from C++ create a " + controlName);
210
211       Func< CustomView > controlConstructor;
212
213       // find the control constructor
214       if ( Instance._constructorMap.TryGetValue (controlName, out controlConstructor) )
215       {
216         // Create the control
217         CustomView newControl = controlConstructor ();
218         return newControl.GetPtrfromActor();  // return pointer to handle
219       }
220       else
221       {
222         throw new global::System.InvalidOperationException("C# View not registererd with ViewRegistry"+ controlName );
223         return IntPtr.Zero;
224       }
225     }
226
227     /// <summary>
228     /// Store the mapping between this instance of control (View) and native part.
229     /// </summary>
230     /// <param name="view"> The instance of control (View)</param>
231     public static void RegisterView( View view )
232     {
233       // We store a pointer to the RefObject for the control
234       RefObject refObj = view.GetObjectPtr();
235       IntPtr refCptr = (IntPtr) RefObject.getCPtr(refObj);
236
237       //Console.WriteLine ("________Storing ref object cptr in control map Hex: {0:X}", refCptr);
238       if ( !Instance._controlMap.ContainsKey(refCptr) )
239       {
240         Instance._controlMap.Add(refCptr, view );
241       }
242
243       return;
244     }
245
246     private static IntPtr GetProperty( IntPtr controlPtr, IntPtr propertyName )
247     {
248       string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (propertyName);
249       return Instance.GetPropertyValue ( controlPtr, name);
250     }
251
252     private static void SetProperty(  IntPtr controlPtr, IntPtr propertyName, IntPtr propertyValue )
253     {
254       string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (propertyName);
255       //Console.WriteLine ( SetControlProperty  called for:" + name );
256       Instance.SetPropertyValue ( controlPtr, name, propertyValue);
257
258     }
259
260     public static ViewRegistry Instance
261     {
262       get
263       {
264         if (instance==null)
265         {
266           instance = new ViewRegistry();
267         }
268         return instance;
269       }
270     }
271
272     public static View GetViewFromActor( Actor actor )
273     {
274       // we store a dictionary of ref-obects (C++ land) to custom views (C# land)
275       Dali.View view;
276
277       RefObject refObj = actor.GetObjectPtr ();
278       IntPtr refObjectPtr = (IntPtr) RefObject.getCPtr(refObj);
279
280       if ( Instance._controlMap.TryGetValue ( refObjectPtr, out view) )
281       {
282         return view;
283       }
284       else
285       {
286         return null;
287       }
288     }
289
290
291     /// <summary>
292     /// Function which registers a view and all it's scriptable properties with DALi's type registry.
293     /// Means the View can be created / configured from a JSON script.
294     ///
295     /// The function uses introspection to scan a Views C# properties, then selects the ones with
296     ///[ScriptableProperty] attribute to be registered.
297     /// Example of a Spin view registering itself
298     ///   static Spin()
299     /// {
300     ///   ViewRegistry registers control type with DALi type registery
301     ///   also uses introspection to find any properties that need to be registered with type registry
302     ///   ViewRegistry.Instance.Register(CreateInstance, typeof(Spin) );
303     /// }
304     ///
305     /// </summary>
306     public void Register(Func< CustomView > createFunction, System.Type viewType )
307     {
308       // add the mapping between the view name and it's create function
309       _constructorMap.Add (viewType.Name, createFunction);
310
311       // Call into DALi C++ to register the control with the type registry
312       TypeRegistration.RegisterControl( viewType.Name, _createCallback );
313
314       // Cycle through each property in the class
315       foreach (System.Reflection.PropertyInfo propertyInfo in viewType.GetProperties())
316       {
317
318         if ( propertyInfo.CanRead )
319         {
320
321           System.Attribute[] attrs = System.Attribute.GetCustomAttributes(propertyInfo);
322           foreach (System.Attribute attr in attrs)
323           {
324             // If the Scriptable attribute exists, then register it with the type registry.
325             if (attr is ScriptableProperty)
326             {
327               //Console.WriteLine ("Got a DALi JSON scriptable property = " + propertyInfo.Name +", of type " + propertyInfo.PropertyType.Name);
328
329               // first get the attribute type, ( default, or animatable)
330               ScriptableProperty scriptableProp = attr as ScriptableProperty;
331
332               // we get the start property index, based on the type and it's heirachy, e.g. DateView (70,000)-> Spin (60,000) -> View (50,000)
333               int propertyIndex =  _propertyRangeManager.GetPropertyIndex( viewType.Name, viewType, scriptableProp.type );
334
335               // get the enum for the property type... E.g. registering a string property returns Dali.PropertyType.String
336               Dali.Property.Type propertyType = GetDaliPropertyType( propertyInfo.PropertyType.Name );
337
338               // Example   RegisterProperty("spin","maxValue", 50001, FLOAT, set, get );
339               // Native call to register the property
340               TypeRegistration.RegisterProperty (viewType.Name, propertyInfo.Name , propertyIndex, propertyType, _setPropertyCallback, _getPropertyCallback);
341             }
342           }
343           // Console.WriteLine ("property name = " + propertyInfo.Name);
344         }
345       }
346     }
347
348     /// <summary>
349     /// Get a property value from a View
350     ///
351     /// </summary>
352     private IntPtr GetPropertyValue ( IntPtr controlPtr, string propertyName)
353     {
354       // Get the C# control that maps to the C++ control
355       Dali.View view;
356
357       BaseHandle baseHandle = new BaseHandle (controlPtr, false);
358
359       RefObject refObj = baseHandle.GetObjectPtr ();
360
361       IntPtr refObjectPtr = (IntPtr) RefObject.getCPtr(refObj);
362
363       if ( _controlMap.TryGetValue ( refObjectPtr, out view) )
364       {
365         // call the get property function
366         System.Object val = view.GetType ().GetProperty (propertyName).GetAccessors () [0].Invoke (view, null);
367
368         Property.Value value = Property.Value.CreateFromObject (val);
369
370         return (IntPtr)Property.Value.getCPtr (value);
371       }
372       else
373       {
374         return IntPtr.Zero;
375       }
376     }
377
378     /// <summary>
379     /// Set a property value on a View
380     ///
381     /// </summary>
382     private void SetPropertyValue ( IntPtr controlPtr, string propertyName, IntPtr propertyValuePtr)
383     {
384       // Get the C# control that maps to the C++ control
385       Dali.View view;
386
387       //Console.WriteLine ("SetPropertyValue   refObjectPtr = {0:X}", controlPtr);
388
389       Property.Value propValue = new Property.Value (propertyValuePtr, false);
390
391       if ( _controlMap.TryGetValue ( controlPtr, out view) )
392       {
393
394         System.Reflection.PropertyInfo propertyInfo = view.GetType().GetProperty(propertyName);
395
396         // We know the property name, we know it's type, we just need to convert from a DALi property value to native C# type
397         System.Type type = propertyInfo.PropertyType;
398         bool ok = false;
399
400         if ( type.Equals (typeof(Int32)) )
401         {
402           int value = 0;
403           ok = propValue.Get( ref value );
404           if ( ok )
405           {
406             propertyInfo.SetValue (view, value);
407           }
408         }
409         else if ( type.Equals (typeof(bool)) )
410         {
411           bool value = false;
412           ok = propValue.Get( ref value );
413           if ( ok )
414           {
415             propertyInfo.SetValue (view, value);
416           }
417         }
418         else if ( type.Equals (typeof(float)) )
419         {
420           float value = 0;
421           ok = propValue.Get( ref value );
422           if ( ok )
423           {
424             propertyInfo.SetValue (view, value);
425           }
426         }
427         else if ( type.Equals (typeof(string)) )
428         {
429           string value = "";
430           ok = propValue.Get( out value );
431           if ( ok )
432           {
433             propertyInfo.SetValue (view, value);
434           }
435         }
436         else if ( type.Equals (typeof(Vector2)) )
437         {
438           Vector2 value = new Vector2 ();
439           ok = propValue.Get( value );
440           if ( ok )
441           {
442             propertyInfo.SetValue (view, value);
443           }
444         }
445         else if ( type.Equals (typeof(Vector3)) )
446         {
447           Vector3 value = new Vector3 ();
448           ok = propValue.Get( value );
449           if ( ok )
450           {
451             propertyInfo.SetValue (view, value);
452           }
453         }
454         else if ( type.Equals (typeof(Vector4)) )
455         {
456           Vector4 value = new Vector4 ();
457           ok = propValue.Get( value );
458
459           if ( ok )
460           {
461            propertyInfo.SetValue (view, value);
462           }
463         }
464         else if ( type.Equals (typeof(Position)) )
465         {
466           Position value = new Position ();
467           ok = propValue.Get( value );
468           if ( ok )
469           {
470             propertyInfo.SetValue (view, value);
471           }
472         }
473         else if ( type.Equals (typeof(Size)) )
474         {
475           // DALi sizes are Vector3
476           Vector3 value = new Vector3();
477           ok = propValue.Get( value );
478           if ( ok )
479           {
480             propertyInfo.SetValue(view, new Size(value.X,value.Y));
481           };
482         }
483         else if ( type.Equals (typeof(Color)) )
484         {
485           // Colors are stored as Vector4's in DALi
486           Vector4 value = new Vector4();
487           ok = propValue.Get( value );
488           if ( ok )
489           {
490             propertyInfo.SetValue (view, (Color)value);
491           };
492         }
493         else
494         {
495           throw new global::System.InvalidOperationException("SetPropertyValue Unimplemented type for Property Value");
496         }
497         if ( !ok )
498         {
499           throw new global::System.InvalidOperationException("SetPropertyValue propValue.Get failed");
500         }
501       }
502       else
503       {
504         throw new global::System.InvalidOperationException("failed to find the control to write a property to: cptr = " + controlPtr);
505       }
506
507     }
508
509   }
510
511
512 }