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