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