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