2 using System.Reflection;
4 using System.Runtime.InteropServices;
5 using System.Collections.Generic;
6 using Tizen.NUI.BaseComponents;
11 /// Add this attribute to any property belonging to a View (control) you want to be scriptable from JSON
16 /// class MyView : public CustomView
18 /// [ScriptableProperty()]
19 /// public int MyProperty
23 /// return _myProperty;
27 /// _myProperty = value;
32 /// Internally the following occurs for property registration ( this only occurs once per Type, not per Instance):
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).
40 /// The DALi C# example
42 /// class MyView : public CustomView
45 /// [ScriptableProperty()]
46 /// public double Hours
48 /// get { return seconds / 3600; }
49 /// set { seconds = value * 3600; }
53 /// Equivalent code in DALi C++:
55 /// class MyControl : public Control
61 /// HOURS = Control::CONTROL_PROPERTY_END_INDEX + 1
66 /// in MyControl-impl.cpp
68 /// DALI_TYPE_REGISTRATION_BEGIN( Toolkit::MyControl, Toolkit::Control, Create );
69 /// DALI_PROPERTY_REGISTRATION( Toolkit, MyControl, "Hours", INTEGER, DISABLED )
70 /// DALI_TYPE_REGISTRATION_END()
74 public class ScriptableProperty : System.Attribute
76 public enum ScriptableType
78 Default, // Read Writable, non-animatable property, event thread only
79 // Animatable // Animatable property, Currently disabled, UK
81 public readonly ScriptableType type;
83 public ScriptableProperty(ScriptableType type = ScriptableType.Default)
90 /// View Registry singleton.
91 /// Used for registering controls and any scriptable properties they have ( see ScriptableProperty )
93 /// Internal Design from C# to C++
95 /// - Each custom C# view should have it's static constructor called before any JSON file is loaded.
96 /// Static constructors for a class will only run once ( they are run per control type, not per instance).
97 /// Example of running a static constructor:
98 /// System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (typeof(Spin).TypeHandle);
99 /// Inside the static constructor the control should register it's type with the ViewRegistry
104 /// ViewRegistry.Instance.Register(CreateInstance, typeof(Spin) );
107 /// The control should also provide a CreateInstance function, which gets passed to the ViewRegistry
108 /// // Eventually it will be called if DALi Builderfinds a Spin control in a JSON file
109 /// static CustomView CreateInstance()
111 /// return new Spin();
116 /// The DALi C++ equivalent of this is
118 /// TypeRegistration mType( typeid(Toolkit::Spin), typeid(Toolkit::Control), CreateInstance );
123 public sealed class CustomViewRegistry
126 /// ViewRegistry is a singleton
128 private static CustomViewRegistry instance = null;
130 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
131 private delegate IntPtr CreateControlDelegate(IntPtr cPtrControlName);
133 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
134 private delegate IntPtr GetPropertyDelegate(IntPtr controlPtr, IntPtr propertyName);
136 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
137 private delegate void SetPropertyDelegate(IntPtr controlPtr, IntPtr propertyName, IntPtr propertyValue);
139 private CreateControlDelegate _createCallback;
140 private SetPropertyDelegate _setPropertyCallback;
141 private GetPropertyDelegate _getPropertyCallback;
142 private PropertyRangeManager _propertyRangeManager;
145 // Maps the name of a custom view to a create instance function
146 /// E.g. given a string "Spin", we can get a function used to create the Spin View.
148 private Dictionary<String, Func<CustomView>> _constructorMap;
151 /// Lookup table to match C# types to DALi types, used for the automatic property registration
153 private static readonly Dictionary<string, Tizen.NUI.PropertyType> _daliPropertyTypeLookup
154 = new Dictionary<string, Tizen.NUI.PropertyType>
156 { "float", PropertyType.Float },
157 { "int", PropertyType.Integer },
158 { "Int32", PropertyType.Integer },
159 { "Boolean", PropertyType.Boolean },
160 { "string", PropertyType.String },
161 { "Vector2", PropertyType.Vector2 },
162 { "Vector3", PropertyType.Vector3 },
163 { "Vector4", PropertyType.Vector4 },
164 { "Size", PropertyType.Vector2 },
165 { "Position",PropertyType.Vector3 },
166 { "Color", PropertyType.Vector4 },
167 // { "Matrix3", PropertyType.MATRIX3 }, commented out until we need to use Matrices from JSON
168 // { "Matrix", PropertyType.MATRIX },
172 private CustomViewRegistry()
174 _createCallback = new CreateControlDelegate(CreateControl);
175 _getPropertyCallback = new GetPropertyDelegate(GetProperty);
176 _setPropertyCallback = new SetPropertyDelegate(SetProperty);
178 _constructorMap = new Dictionary<string, Func<CustomView>>();
179 _propertyRangeManager = new PropertyRangeManager();
182 private Tizen.NUI.PropertyType GetDaliPropertyType(string cSharpTypeName)
184 Tizen.NUI.PropertyType daliType;
185 if (_daliPropertyTypeLookup.TryGetValue(cSharpTypeName, out daliType))
188 Tizen.Log.Debug("NUI", "mapped " + cSharpTypeName + " to dAli type " + daliType);
195 Tizen.Log.Debug("NUI", "Failed to find a mapping between C# property" + cSharpTypeName + " and DALi type");
197 return PropertyType.None;
202 /// Called directly from DALi C++ type registry to create a control (View) using no marshalling.
204 /// <returns>Pointer to the Control (Views) handle </returns>
205 /// <param name="cPtrControlName"> C pointer to the Control (View) name</param>
206 private static IntPtr CreateControl(IntPtr cPtrControlName)
208 string controlName = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(cPtrControlName);
210 Tizen.Log.Debug("NUI", "Create controlled called from C++ create a " + controlName);
212 Func<CustomView> controlConstructor;
214 // find the control constructor
215 if (Instance._constructorMap.TryGetValue(controlName, out controlConstructor))
217 // Create the control
218 CustomView newControl = controlConstructor();
219 return newControl.GetPtrfromView(); // return pointer to handle
223 throw new global::System.InvalidOperationException("C# View not registererd with ViewRegistry" + controlName);
228 private static IntPtr GetProperty(IntPtr controlPtr, IntPtr propertyName)
230 string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(propertyName);
231 return Instance.GetPropertyValue(controlPtr, name);
234 private static void SetProperty(IntPtr controlPtr, IntPtr propertyName, IntPtr propertyValue)
236 string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(propertyName);
238 Tizen.Log.Debug("NUI", "SetControlProperty called for:" + name);
240 Instance.SetPropertyValue(controlPtr, name, propertyValue);
244 public static CustomViewRegistry Instance
248 if (instance == null)
250 instance = new CustomViewRegistry();
257 /// Function which registers a view and all it's scriptable properties with DALi's type registry.
258 /// Means the View can be created / configured from a JSON script.
260 /// The function uses introspection to scan a Views C# properties, then selects the ones with
261 ///[ScriptableProperty] attribute to be registered.
262 /// Example of a Spin view registering itself
265 /// ViewRegistry registers control type with DALi type registery
266 /// also uses introspection to find any properties that need to be registered with type registry
267 /// ViewRegistry.Instance.Register(CreateInstance, typeof(Spin) );
271 public void Register(Func<CustomView> createFunction, System.Type viewType)
273 // add the mapping between the view name and it's create function
274 _constructorMap.Add(viewType.ToString(), createFunction);
276 // Call into DALi C++ to register the control with the type registry
277 TypeRegistration.RegisterControl(viewType.ToString(), _createCallback);
279 // Cycle through each property in the class
280 foreach (System.Reflection.PropertyInfo propertyInfo in viewType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public))
283 if (propertyInfo.CanRead)
286 IEnumerable<Attribute> ie_attrs = propertyInfo.GetCustomAttributes<Attribute>();
287 List<Attribute> li_attrs = new List<Attribute>(ie_attrs);
288 System.Attribute[] attrs = li_attrs.ToArray();
290 foreach (System.Attribute attr in attrs)
292 // If the Scriptable attribute exists, then register it with the type registry.
293 if (attr is ScriptableProperty)
296 Tizen.Log.Debug("NUI", "Got a DALi JSON scriptable property = " + propertyInfo.Name + ", of type " + propertyInfo.PropertyType.Name);
298 // first get the attribute type, ( default, or animatable)
299 ScriptableProperty scriptableProp = attr as ScriptableProperty;
301 // 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)
302 int propertyIndex = _propertyRangeManager.GetPropertyIndex(viewType.ToString(), viewType, scriptableProp.type);
304 // get the enum for the property type... E.g. registering a string property returns Tizen.NUI.PropertyType.String
305 Tizen.NUI.PropertyType propertyType = GetDaliPropertyType(propertyInfo.PropertyType.Name);
307 // Example RegisterProperty("spin","maxValue", 50001, FLOAT, set, get );
308 // Native call to register the property
309 TypeRegistration.RegisterProperty(viewType.ToString(), propertyInfo.Name, propertyIndex, propertyType, _setPropertyCallback, _getPropertyCallback);
313 Tizen.Log.Debug("NUI", "property name = " + propertyInfo.Name);
320 /// Get a property value from a View
323 private IntPtr GetPropertyValue(IntPtr refObjectPtr, string propertyName)
325 // Get the C# control that maps to the C++ control
326 View view = Registry.GetManagedBaseHandleFromRefObject(refObjectPtr) as View;
329 // call the get property function
330 System.Object val = view.GetType().GetProperty(propertyName).GetAccessors()[0].Invoke(view, null);
332 PropertyValue value = PropertyValue.CreateFromObject(val);
334 return (IntPtr)PropertyValue.getCPtr(value);
343 /// Set a property value on a View
346 private void SetPropertyValue(IntPtr refObjectPtr, string propertyName, IntPtr propertyValuePtr)
348 // Get the C# control that maps to the C++ control
350 Tizen.Log.Debug("NUI", "SetPropertyValue refObjectPtr = {0:X}" + refObjectPtr);
352 PropertyValue propValue = new PropertyValue(propertyValuePtr, false);
354 // Get the C# control that maps to the C++ control
355 View view = Registry.GetManagedBaseHandleFromRefObject(refObjectPtr) as View;
358 System.Reflection.PropertyInfo propertyInfo = view.GetType().GetProperty(propertyName);
360 // We know the property name, we know it's type, we just need to convert from a DALi property value to native C# type
361 System.Type type = propertyInfo.PropertyType;
364 if (type.Equals(typeof(Int32)))
367 ok = propValue.Get(out value);
370 propertyInfo.SetValue(view, value);
373 else if (type.Equals(typeof(bool)))
376 ok = propValue.Get(out value);
379 propertyInfo.SetValue(view, value);
382 else if (type.Equals(typeof(float)))
385 ok = propValue.Get(out value);
388 propertyInfo.SetValue(view, value);
391 else if (type.Equals(typeof(string)))
394 ok = propValue.Get(out value);
397 propertyInfo.SetValue(view, value);
400 else if (type.Equals(typeof(Vector2)))
402 Vector2 value = new Vector2();
403 ok = propValue.Get(value);
406 propertyInfo.SetValue(view, value);
409 else if (type.Equals(typeof(Vector3)))
411 Vector3 value = new Vector3();
412 ok = propValue.Get(value);
415 propertyInfo.SetValue(view, value);
418 else if (type.Equals(typeof(Vector4)))
420 Vector4 value = new Vector4();
421 ok = propValue.Get(value);
425 propertyInfo.SetValue(view, value);
428 else if (type.Equals(typeof(Position)))
430 Position value = new Position();
431 ok = propValue.Get(value);
434 propertyInfo.SetValue(view, value);
437 else if (type.Equals(typeof(Size)))
439 Size value = new Size();
440 ok = propValue.Get(value);
443 propertyInfo.SetValue(view, new Size(value.Width, value.Height, value.Depth));
446 else if (type.Equals(typeof(Color)))
448 // Colors are stored as Vector4's in DALi
449 Color value = new Color();
450 ok = propValue.Get(value);
453 propertyInfo.SetValue(view, (Color)value);
458 throw new global::System.InvalidOperationException("SetPropertyValue Unimplemented type for Property Value");
462 throw new global::System.InvalidOperationException("SetPropertyValue propValue.Get failed");
467 throw new global::System.InvalidOperationException("failed to find the control to write a property to: cptr = " + refObjectPtr);