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