Fix control properties for C# custom view
[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++ custom control the dictionary allows us to find what CustomView it belongs to
143     /// </summary>
144     private Dictionary<IntPtr, Dali.CustomView> _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, CustomView>();
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)  uses 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
219         // Store the mapping between this instance of the custom control and native part
220         // We store a pointer to the RefObject for the control
221         IntPtr cPtr = newControl.GetPtrfromActor();
222         RefObject refObj = newControl.GetObjectPtr ();
223         IntPtr refCptr = (IntPtr) RefObject.getCPtr(refObj);
224
225         //Console.WriteLine ("________Storing ref object cptr in control map Hex: {0:X}", refCptr);
226         Instance._controlMap.Add (refCptr , newControl );
227
228         return cPtr;  // return pointer to handle
229       }
230       else
231       {
232         throw new global::System.InvalidOperationException("C# View not registererd with ViewRegistry"+ controlName );
233         return IntPtr.Zero;
234       }
235     }
236
237     private static IntPtr GetProperty( IntPtr controlPtr, IntPtr propertyName )
238     {
239       string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (propertyName);
240       return Instance.GetPropertyValue ( controlPtr, name);
241     }
242
243     private static void SetProperty(  IntPtr controlPtr, IntPtr propertyName, IntPtr propertyValue )
244     {
245       string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (propertyName);
246       //Console.WriteLine ( SetControlProperty  called for:" + name );
247       Instance.SetPropertyValue ( controlPtr, name, propertyValue);
248
249     }
250
251     public static ViewRegistry Instance
252     {
253       get
254       {
255         if (instance==null)
256         {
257           instance = new ViewRegistry();
258         }
259         return instance;
260       }
261     }
262
263     public static CustomView GetCustomViewFromActor( Actor actor )
264     {
265       // we store a dictionary of ref-obects (C++ land) to custom views (C# land)
266       Dali.CustomView view;
267
268       RefObject refObj = actor.GetObjectPtr ();
269       IntPtr refObjectPtr = (IntPtr) RefObject.getCPtr(refObj);
270
271       if ( Instance._controlMap.TryGetValue ( refObjectPtr, out view) )
272       {
273
274         // call the get property function
275
276         return view;
277       }
278       else
279       {
280         return null;
281       }
282     }
283
284
285     /// <summary>
286     /// Function which registers a view and all it's scriptable properties with DALi's type registry.
287     /// Means the View can be created / configured from a JSON script.
288     ///
289     /// The function uses introspection to scan a Views C# properties, then selects the ones with
290     ///[ScriptableProperty] attribute to be registered.
291     /// Example of a Spin view registering itself
292     ///   static Spin()
293     /// {
294     ///   ViewRegistry registers control type with DALi type registery
295     ///   also uses introspection to find any properties that need to be registered with type registry
296     ///   ViewRegistry.Instance.Register(CreateInstance, typeof(Spin) );
297     /// }
298     ///
299     /// </summary>
300     public void Register(Func< CustomView > createFunction, System.Type viewType )
301     {
302       // add the mapping between the view name and it's create function
303       _constructorMap.Add (viewType.Name, createFunction);
304
305       // Call into DALi C++ to register the control with the type registry
306       TypeRegistration.RegisterControl( viewType.Name, _createCallback );
307
308       // Cycle through each property in the class
309       foreach (System.Reflection.PropertyInfo propertyInfo in viewType.GetProperties())
310       {
311
312         if ( propertyInfo.CanRead )
313         {
314
315           System.Attribute[] attrs = System.Attribute.GetCustomAttributes(propertyInfo);
316           foreach (System.Attribute attr in attrs)
317           {
318             // If the Scriptable attribute exists, then register it with the type registry.
319             if (attr is ScriptableProperty)
320             {
321               //Console.WriteLine ("Got a DALi JSON scriptable property = " + propertyInfo.Name +", of type " + propertyInfo.PropertyType.Name);
322
323               // first get the attribute type, ( default, or animatable)
324               ScriptableProperty scriptableProp = attr as ScriptableProperty;
325
326               // 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)
327               int propertyIndex =  _propertyRangeManager.GetPropertyIndex( viewType.Name, viewType, scriptableProp.type );
328
329               // get the enum for the property type... E.g. registering a string property returns Dali.PropertyType.String
330               Dali.Property.Type propertyType = GetDaliPropertyType( propertyInfo.PropertyType.Name );
331
332               // Example   RegisterProperty("spin","maxValue", 50001, FLOAT, set, get );
333               // Native call to register the property
334               TypeRegistration.RegisterProperty (viewType.Name, propertyInfo.Name , propertyIndex, propertyType, _setPropertyCallback, _getPropertyCallback);
335             }
336           }
337           // Console.WriteLine ("property name = " + propertyInfo.Name);
338         }
339       }
340     }
341
342     /// <summary>
343     /// Get a property value from a View
344     ///
345     /// </summary>
346     private IntPtr GetPropertyValue ( IntPtr controlPtr, string propertyName)
347     {
348       // Get the C# control that maps to the C++ control
349       Dali.CustomView view;
350
351       BaseHandle baseHandle = new BaseHandle (controlPtr, false);
352
353       RefObject refObj = baseHandle.GetObjectPtr ();
354
355       IntPtr refObjectPtr = (IntPtr) RefObject.getCPtr(refObj);
356
357       if ( _controlMap.TryGetValue ( refObjectPtr, out view) )
358       {
359
360         // call the get property function
361         System.Object val = view.GetType ().GetProperty (propertyName).GetAccessors () [0].Invoke (view, null);
362
363         Property.Value value = Property.Value.CreateFromObject (val);
364
365         return (IntPtr)Property.Value.getCPtr (value);
366       }
367       else
368       {
369         return IntPtr.Zero;
370       }
371     }
372
373     /// <summary>
374     /// Set a property value on a View
375     ///
376     /// </summary>
377     private void SetPropertyValue ( IntPtr controlPtr, string propertyName, IntPtr propertyValuePtr)
378     {
379       // Get the C# control that maps to the C++ control
380       Dali.CustomView view;
381
382       //Console.WriteLine ("SetPropertyValue   refObjectPtr = {0:X}", controlPtr);
383
384       Property.Value propValue = new Property.Value (propertyValuePtr, false);
385
386       if ( _controlMap.TryGetValue ( controlPtr, out view) )
387       {
388
389         System.Reflection.PropertyInfo propertyInfo = view.GetType().GetProperty(propertyName);
390
391         // We know the property name, we know it's type, we just need to convert from a DALi property value to native C# type
392         System.Type type = propertyInfo.PropertyType;
393         bool ok = false;
394
395         if ( type.Equals (typeof(Int32)) )
396         {
397           int value = 0;
398           ok = propValue.Get( ref value );
399           if ( ok )
400           {
401             propertyInfo.SetValue (view, value);
402           }
403         }
404         else if ( type.Equals (typeof(bool)) )
405         {
406           bool value = false;
407           ok = propValue.Get( ref value );
408           if ( ok )
409           {
410             propertyInfo.SetValue (view, value);
411           }
412         }
413         else if ( type.Equals (typeof(float)) )
414         {
415           float value = 0;
416           ok = propValue.Get( ref value );
417           if ( ok )
418           {
419             propertyInfo.SetValue (view, value);
420           }
421         }
422         else if ( type.Equals (typeof(string)) )
423         {
424           string value = "";
425           ok = propValue.Get( out value );
426           if ( ok )
427           {
428             propertyInfo.SetValue (view, value);
429           }
430         }
431         else if ( type.Equals (typeof(Vector2)) )
432         {
433           Vector2 value = new Vector2 ();
434           ok = propValue.Get( value );
435           if ( ok )
436           {
437             propertyInfo.SetValue (view, value);
438           }
439         }
440         else if ( type.Equals (typeof(Vector3)) )
441         {
442           Vector3 value = new Vector3 ();
443           ok = propValue.Get( value );
444           if ( ok )
445           {
446             propertyInfo.SetValue (view, value);
447           }
448         }
449         else if ( type.Equals (typeof(Vector4)) )
450         {
451           Vector4 value = new Vector4 ();
452           ok = propValue.Get( value );
453
454           if ( ok )
455           {
456            propertyInfo.SetValue (view, value);
457           }
458         }
459         else if ( type.Equals (typeof(Position)) )
460         {
461           Position value = new Position ();
462           ok = propValue.Get( value );
463           if ( ok )
464           {
465             propertyInfo.SetValue (view, value);
466           }
467         }
468         else if ( type.Equals (typeof(Size)) )
469         {
470           // DALi sizes are Vector3
471           Vector3 value = new Vector3();
472           ok = propValue.Get( value );
473           if ( ok )
474           {
475             propertyInfo.SetValue(view, new Size(value.X,value.Y));
476           };
477         }
478         else if ( type.Equals (typeof(Color)) )
479         {
480           // Colors are stored as Vector4's in DALi
481           Vector4 value = new Vector4();
482           ok = propValue.Get( value );
483           if ( ok )
484           {
485             propertyInfo.SetValue (view, (Color)value);
486           };
487         }
488         else
489         {
490           throw new global::System.InvalidOperationException("SetPropertyValue Unimplemented type for Property Value");
491         }
492         if ( !ok )
493         {
494           throw new global::System.InvalidOperationException("SetPropertyValue propValue.Get failed");
495         }
496       }
497       else
498       {
499         throw new global::System.InvalidOperationException("failed to find the control to write a property to: cptr = " + controlPtr);
500       }
501
502     }
503
504   }
505
506
507 }