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