b055a7dc23e57c3fec4c922c844ccfe240248851
[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     /// By keeping the weak reference only, it will allow the object to be garbage collected.
144     /// </summary>
145     private Dictionary<IntPtr, WeakReference> _controlMap;
146
147     ///<summary>
148     // Maps the name of a custom view to a create instance function
149     /// E.g. given a string "Spin", we can get a function used to create the Spin View.
150     ///</summary>
151     private Dictionary<String, Func< CustomView > > _constructorMap;
152
153     /// <summary>
154     /// Lookup table to match C# types to DALi types, used for the automatic property registration
155     /// </summary>
156     private static readonly Dictionary<string, Dali.Property.Type> _daliPropertyTypeLookup
157     = new Dictionary< string, Dali.Property.Type  >
158     {
159       { "float",   Property.Type.FLOAT },
160       { "int",     Property.Type.INTEGER },
161       { "Int32",   Property.Type.INTEGER },
162       { "Boolean", Property.Type.BOOLEAN },
163       { "string",  Property.Type.STRING },
164       { "Vector2", Property.Type.VECTOR2 },
165       { "Vector3", Property.Type.VECTOR3 },
166       { "Vector4", Property.Type.VECTOR4 },
167       { "Size",    Property.Type.VECTOR2 },
168       { "Position",Property.Type.VECTOR3 },
169       { "Color",   Property.Type.VECTOR4 },
170     //  { "Matrix3", Property.Type.MATRIX3 }, commented out until we need to use Matrices from JSON
171     //  { "Matrix",  Property.Type.MATRIX },
172     };
173
174
175     public ViewRegistry()
176     {
177       _createCallback = new CreateControlDelegate( CreateControl );
178       _getPropertyCallback = new GetPropertyDelegate (GetProperty);
179       _setPropertyCallback  = new SetPropertyDelegate (SetProperty);
180
181       _controlMap = new Dictionary<IntPtr, WeakReference>();
182       _constructorMap = new Dictionary<string, Func<CustomView>>();
183       _propertyRangeManager = new PropertyRangeManager();
184
185     }
186
187     private Dali.Property.Type GetDaliPropertyType( string cSharpTypeName )
188     {
189       Dali.Property.Type daliType;
190       if ( _daliPropertyTypeLookup.TryGetValue (cSharpTypeName, out daliType) )
191       {
192         //Console.WriteLine("mapped "+ cSharpTypeName + " to dAli type " +daliType );
193         return daliType;
194       }
195       else
196       {
197        // Console.WriteLine("Failed to find a mapping between C# property" + cSharpTypeName +" and DALi type");
198         return Property.Type.NONE;
199       }
200     }
201
202     /// <summary>
203     /// Called directly from DALi C++ type registry to create a control (View) using no marshalling.
204     /// </summary>
205     /// <returns>Pointer to the Control (Views) handle </returns>
206     /// <param name="cPtrControlName"> C pointer to the Control (View) name</param>
207     private static IntPtr CreateControl( IntPtr cPtrControlName )
208     {
209       string controlName = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (cPtrControlName);
210       // Console.WriteLine ("Create controlled called from C++ create a " + controlName);
211
212       Func< CustomView > controlConstructor;
213
214       // find the control constructor
215       if ( Instance._constructorMap.TryGetValue (controlName, out controlConstructor) )
216       {
217         // Create the control
218         CustomView newControl = controlConstructor ();
219         return newControl.GetPtrfromActor();  // return pointer to handle
220       }
221       else
222       {
223         throw new global::System.InvalidOperationException("C# View not registererd with ViewRegistry"+ controlName );
224         return IntPtr.Zero;
225       }
226     }
227
228     /// <summary>
229     /// Store the mapping between this instance of control (View) and native part.
230     /// </summary>
231     /// <param name="view"> The instance of control (View)</param>
232     public static void RegisterView( View view )
233     {
234       // We store a pointer to the RefObject for the control
235       RefObject refObj = view.GetObjectPtr();
236       IntPtr refCptr = (IntPtr) RefObject.getCPtr(refObj);
237
238       //Console.WriteLine ("________Storing ref object cptr in control map Hex: {0:X}", refCptr);
239       if ( !Instance._controlMap.ContainsKey(refCptr) )
240       {
241         Instance._controlMap.Add(refCptr, new WeakReference(view, false));
242       }
243
244       return;
245     }
246
247     /// <summary>
248     /// Remove the this instance of control (View) and native part from the mapping table.
249     /// </summary>
250     /// <param name="view"> The instance of control (View)</param>
251     public static void UnregisterView( View view )
252     {
253       RefObject refObj = view.GetObjectPtr();
254       IntPtr refCptr = (IntPtr) RefObject.getCPtr(refObj);
255
256       if ( Instance._controlMap.ContainsKey(refCptr) )
257       {
258         Instance._controlMap.Remove(refCptr);
259       }
260
261       return;
262     }
263
264     private static IntPtr GetProperty( IntPtr controlPtr, IntPtr propertyName )
265     {
266       string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (propertyName);
267       return Instance.GetPropertyValue ( controlPtr, name);
268     }
269
270     private static void SetProperty(  IntPtr controlPtr, IntPtr propertyName, IntPtr propertyValue )
271     {
272       string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (propertyName);
273       //Console.WriteLine ( SetControlProperty  called for:" + name );
274       Instance.SetPropertyValue ( controlPtr, name, propertyValue);
275
276     }
277
278     public static ViewRegistry Instance
279     {
280       get
281       {
282         if (instance==null)
283         {
284           instance = new ViewRegistry();
285         }
286         return instance;
287       }
288     }
289
290     public static View GetViewFromActor( Actor actor )
291     {
292       // we store a dictionary of ref-obects (C++ land) to custom views (C# land)
293
294       RefObject refObj = actor.GetObjectPtr ();
295       IntPtr refObjectPtr = (IntPtr) RefObject.getCPtr(refObj);
296
297       WeakReference viewReference;
298       if ( Instance._controlMap.TryGetValue ( refObjectPtr, out viewReference) )
299       {
300         View view = viewReference.Target as View;
301         return view;
302       }
303       else
304       {
305         return null;
306       }
307     }
308
309
310     /// <summary>
311     /// Function which registers a view and all it's scriptable properties with DALi's type registry.
312     /// Means the View can be created / configured from a JSON script.
313     ///
314     /// The function uses introspection to scan a Views C# properties, then selects the ones with
315     ///[ScriptableProperty] attribute to be registered.
316     /// Example of a Spin view registering itself
317     ///   static Spin()
318     /// {
319     ///   ViewRegistry registers control type with DALi type registery
320     ///   also uses introspection to find any properties that need to be registered with type registry
321     ///   ViewRegistry.Instance.Register(CreateInstance, typeof(Spin) );
322     /// }
323     ///
324     /// </summary>
325     public void Register(Func< CustomView > createFunction, System.Type viewType )
326     {
327       // add the mapping between the view name and it's create function
328       _constructorMap.Add (viewType.Name, createFunction);
329
330       // Call into DALi C++ to register the control with the type registry
331       TypeRegistration.RegisterControl( viewType.Name, _createCallback );
332
333       // Cycle through each property in the class
334       foreach (System.Reflection.PropertyInfo propertyInfo in viewType.GetProperties())
335       {
336
337         if ( propertyInfo.CanRead )
338         {
339
340           System.Attribute[] attrs = System.Attribute.GetCustomAttributes(propertyInfo);
341           foreach (System.Attribute attr in attrs)
342           {
343             // If the Scriptable attribute exists, then register it with the type registry.
344             if (attr is ScriptableProperty)
345             {
346               //Console.WriteLine ("Got a DALi JSON scriptable property = " + propertyInfo.Name +", of type " + propertyInfo.PropertyType.Name);
347
348               // first get the attribute type, ( default, or animatable)
349               ScriptableProperty scriptableProp = attr as ScriptableProperty;
350
351               // 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)
352               int propertyIndex =  _propertyRangeManager.GetPropertyIndex( viewType.Name, viewType, scriptableProp.type );
353
354               // get the enum for the property type... E.g. registering a string property returns Dali.PropertyType.String
355               Dali.Property.Type propertyType = GetDaliPropertyType( propertyInfo.PropertyType.Name );
356
357               // Example   RegisterProperty("spin","maxValue", 50001, FLOAT, set, get );
358               // Native call to register the property
359               TypeRegistration.RegisterProperty (viewType.Name, propertyInfo.Name , propertyIndex, propertyType, _setPropertyCallback, _getPropertyCallback);
360             }
361           }
362           // Console.WriteLine ("property name = " + propertyInfo.Name);
363         }
364       }
365     }
366
367     /// <summary>
368     /// Get a property value from a View
369     ///
370     /// </summary>
371     private IntPtr GetPropertyValue ( IntPtr controlPtr, string propertyName)
372     {
373       // Get the C# control that maps to the C++ control
374       BaseHandle baseHandle = new BaseHandle (controlPtr, false);
375
376       RefObject refObj = baseHandle.GetObjectPtr ();
377
378       IntPtr refObjectPtr = (IntPtr) RefObject.getCPtr(refObj);
379
380       WeakReference viewReference;
381       if ( _controlMap.TryGetValue ( refObjectPtr, out viewReference) )
382       {
383         View view = viewReference.Target as View;
384
385         // call the get property function
386         System.Object val = view.GetType ().GetProperty (propertyName).GetAccessors () [0].Invoke (view, null);
387
388         Property.Value value = Property.Value.CreateFromObject (val);
389
390         return (IntPtr)Property.Value.getCPtr (value);
391       }
392       else
393       {
394         return IntPtr.Zero;
395       }
396     }
397
398     /// <summary>
399     /// Set a property value on a View
400     ///
401     /// </summary>
402     private void SetPropertyValue ( IntPtr controlPtr, string propertyName, IntPtr propertyValuePtr)
403     {
404       // Get the C# control that maps to the C++ control
405
406       //Console.WriteLine ("SetPropertyValue   refObjectPtr = {0:X}", controlPtr);
407
408       Property.Value propValue = new Property.Value (propertyValuePtr, false);
409
410       WeakReference viewReference;
411       if ( _controlMap.TryGetValue ( controlPtr, out viewReference) )
412       {
413         View view = viewReference.Target as View;
414         System.Reflection.PropertyInfo propertyInfo = view.GetType().GetProperty(propertyName);
415
416         // We know the property name, we know it's type, we just need to convert from a DALi property value to native C# type
417         System.Type type = propertyInfo.PropertyType;
418         bool ok = false;
419
420         if ( type.Equals (typeof(Int32)) )
421         {
422           int value = 0;
423           ok = propValue.Get( ref value );
424           if ( ok )
425           {
426             propertyInfo.SetValue (view, value);
427           }
428         }
429         else if ( type.Equals (typeof(bool)) )
430         {
431           bool value = false;
432           ok = propValue.Get( ref value );
433           if ( ok )
434           {
435             propertyInfo.SetValue (view, value);
436           }
437         }
438         else if ( type.Equals (typeof(float)) )
439         {
440           float value = 0;
441           ok = propValue.Get( ref value );
442           if ( ok )
443           {
444             propertyInfo.SetValue (view, value);
445           }
446         }
447         else if ( type.Equals (typeof(string)) )
448         {
449           string value = "";
450           ok = propValue.Get( out value );
451           if ( ok )
452           {
453             propertyInfo.SetValue (view, value);
454           }
455         }
456         else if ( type.Equals (typeof(Vector2)) )
457         {
458           Vector2 value = new Vector2 ();
459           ok = propValue.Get( value );
460           if ( ok )
461           {
462             propertyInfo.SetValue (view, value);
463           }
464         }
465         else if ( type.Equals (typeof(Vector3)) )
466         {
467           Vector3 value = new Vector3 ();
468           ok = propValue.Get( value );
469           if ( ok )
470           {
471             propertyInfo.SetValue (view, value);
472           }
473         }
474         else if ( type.Equals (typeof(Vector4)) )
475         {
476           Vector4 value = new Vector4 ();
477           ok = propValue.Get( value );
478
479           if ( ok )
480           {
481            propertyInfo.SetValue (view, value);
482           }
483         }
484         else if ( type.Equals (typeof(Position)) )
485         {
486           Position value = new Position ();
487           ok = propValue.Get( value );
488           if ( ok )
489           {
490             propertyInfo.SetValue (view, value);
491           }
492         }
493         else if ( type.Equals (typeof(Size)) )
494         {
495           // DALi sizes are Vector3
496           Vector3 value = new Vector3();
497           ok = propValue.Get( value );
498           if ( ok )
499           {
500             propertyInfo.SetValue(view, new Size(value.X,value.Y));
501           };
502         }
503         else if ( type.Equals (typeof(Color)) )
504         {
505           // Colors are stored as Vector4's in DALi
506           Vector4 value = new Vector4();
507           ok = propValue.Get( value );
508           if ( ok )
509           {
510             propertyInfo.SetValue (view, (Color)value);
511           };
512         }
513         else
514         {
515           throw new global::System.InvalidOperationException("SetPropertyValue Unimplemented type for Property Value");
516         }
517         if ( !ok )
518         {
519           throw new global::System.InvalidOperationException("SetPropertyValue propValue.Get failed");
520         }
521       }
522       else
523       {
524         throw new global::System.InvalidOperationException("failed to find the control to write a property to: cptr = " + controlPtr);
525       }
526
527     }
528
529   }
530
531
532 }