2 using System.Reflection;
4 using System.Runtime.InteropServices;
5 using System.Collections.Generic;
6 using Tizen.NUI.BaseComponents;
7 using System.ComponentModel;
12 /// Adds this attribute to any property belonging to a view (control) you want to be scriptable from JSON.
17 /// class MyView : public CustomView
19 /// [ScriptableProperty()]
20 /// public int MyProperty
24 /// return _myProperty;
28 /// _myProperty = value;
33 /// Internally the following occurs for property registration ( this only occurs once per Type, not per Instance):
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).
41 /// The DALi C# example:
43 /// class MyView : public CustomView
46 /// [ScriptableProperty()]
47 /// public double Hours
49 /// get { return seconds / 3600; }
50 /// set { seconds = value * 3600; }
54 /// Equivalent code in DALi C++:
56 /// class MyControl : public Control
62 /// HOURS = Control::CONTROL_PROPERTY_END_INDEX + 1
67 /// in MyControl-impl.cpp
69 /// DALI_TYPE_REGISTRATION_BEGIN( Toolkit::MyControl, Toolkit::Control, Create );
70 /// DALI_PROPERTY_REGISTRATION( Toolkit, MyControl, "Hours", INTEGER, DISABLED )
71 /// DALI_TYPE_REGISTRATION_END()
75 /// <since_tizen> 3 </since_tizen>
76 [AttributeUsage(AttributeTargets.Property)]
77 public class ScriptableProperty : System.Attribute
80 /// <since_tizen> 3 </since_tizen>
81 [Obsolete("Deprecated in API9, Will be removed in API11, Please use Type")]
82 [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "<Pending>")]
83 public readonly ScriptableType type;
85 /// <since_tizen> 3 </since_tizen>
86 public ScriptableProperty(ScriptableType type = ScriptableType.Default)
92 /// Rhe enum of ScriptableType
94 /// <since_tizen> 3 </since_tizen>
95 public enum ScriptableType
98 /// Read Writable, non-animatable property, event thread only.
100 /// <since_tizen> 3 </since_tizen>
101 Default, // Read Writable, non-animatable property, event thread only
102 // Animatable // Animatable property, Currently disabled, UK
106 /// ScriptableType. Read Writable, non-animatable property, event thread only.
108 [EditorBrowsable(EditorBrowsableState.Never)]
109 public ScriptableType Type => type;
113 /// View the Registry singleton.
114 /// Used for registering controls and any scriptable properties they have (see ScriptableProperty).
116 /// Internal Design from C# to C++
118 /// - Each custom C# view should have it's static constructor called before any JSON file is loaded.
119 /// Static constructors for a class will only run once ( they are run per control type, not per instance).
120 /// Example of running a static constructor:
121 /// System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (typeof(Spin).TypeHandle);
122 /// Inside the static constructor the control should register it's type with the ViewRegistry
127 /// ViewRegistry.Instance.Register(CreateInstance, typeof(Spin) );
130 /// The control should also provide a CreateInstance function, which gets passed to the ViewRegistry.
131 /// // Eventually it will be called if DALi Builderfinds a Spin control in a JSON file.
132 /// static CustomView CreateInstance()
134 /// return new Spin();
139 /// The DALi C++ equivalent of this is
141 /// TypeRegistration mType( typeid(Toolkit::Spin), typeid(Toolkit::Control), CreateInstance );
146 /// <since_tizen> 3 </since_tizen>
147 public sealed class CustomViewRegistry
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 { "PropertyArray", PropertyType.Array },
168 { "PropertyMap", PropertyType.Map },
169 // { "Matrix3", PropertyType.MATRIX3 }, commented out until we need to use Matrices from JSON
170 // { "Matrix", PropertyType.MATRIX },
174 /// ViewRegistry is a singleton.
176 private static CustomViewRegistry instance = null;
178 private CreateControlDelegate _createCallback;
179 private SetPropertyDelegate _setPropertyCallback;
180 private GetPropertyDelegate _getPropertyCallback;
181 private PropertyRangeManager _propertyRangeManager;
184 /// Maps the name of a custom view to a create instance function
185 /// For example, given a string "Spin", we can get a function used to create the Spin View.
187 private Dictionary<String, Func<CustomView>> _constructorMap;
189 private CustomViewRegistry()
191 _createCallback = new CreateControlDelegate(CreateControl);
192 _getPropertyCallback = new GetPropertyDelegate(GetProperty);
193 _setPropertyCallback = new SetPropertyDelegate(SetProperty);
195 _constructorMap = new Dictionary<string, Func<CustomView>>();
196 _propertyRangeManager = new PropertyRangeManager();
199 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
200 private delegate IntPtr CreateControlDelegate(IntPtr cPtrControlName);
202 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
203 private delegate IntPtr GetPropertyDelegate(IntPtr controlPtr, IntPtr propertyName);
205 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
206 private delegate void SetPropertyDelegate(IntPtr controlPtr, IntPtr propertyName, IntPtr propertyValue);
208 /// <since_tizen> 3 </since_tizen>
209 public static CustomViewRegistry Instance
213 if (instance == null)
215 instance = new CustomViewRegistry();
222 /// The function which registers a view and all it's scriptable properties with DALi's type registry.
223 /// Means the view can be created or configured from a JSON script.
225 /// The function uses introspection to scan a views C# properties, then selects the ones with
226 ///[ScriptableProperty] attribute to be registered.
227 /// Example of a Spin view registering itself:
230 /// ViewRegistry registers control type with DALi type registery
231 /// also uses introspection to find any properties that need to be registered with type registry
232 /// ViewRegistry.Instance.Register(CreateInstance, typeof(Spin) );
236 /// <exception cref="ArgumentNullException"> Thrown when viewType is null. </exception>
237 /// <since_tizen> 3 </since_tizen>
238 public void Register(Func<CustomView> createFunction, System.Type viewType)
240 if (null == viewType)
242 throw new ArgumentNullException(nameof(viewType));
245 // add the mapping between the view name and it's create function
246 _constructorMap.Add(viewType.ToString(), createFunction);
248 // Call into DALi C++ to register the control with the type registry
249 TypeRegistration.RegisterControl(viewType.ToString(), _createCallback);
251 // Cycle through each property in the class
252 foreach (System.Reflection.PropertyInfo propertyInfo in viewType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public))
254 if (propertyInfo.CanRead)
256 IEnumerable<Attribute> ie_attrs = propertyInfo.GetCustomAttributes<Attribute>();
257 List<Attribute> li_attrs = new List<Attribute>(ie_attrs);
258 System.Attribute[] attrs = li_attrs.ToArray();
260 foreach (System.Attribute attr in attrs)
262 // If the Scriptable attribute exists, then register it with the type registry.
263 if (attr is ScriptableProperty)
265 NUILog.Debug("Got a DALi JSON scriptable property = " + propertyInfo.Name + ", of type " + propertyInfo.PropertyType.Name);
267 // first get the attribute type, ( default, or animatable)
268 ScriptableProperty scriptableProp = attr as ScriptableProperty;
270 // 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)
271 int propertyIndex = _propertyRangeManager.GetPropertyIndex(viewType.ToString(), viewType, scriptableProp.type);
273 // get the enum for the property type... E.g. registering a string property returns Tizen.NUI.PropertyType.String
274 Tizen.NUI.PropertyType propertyType = GetDaliPropertyType(propertyInfo.PropertyType.Name);
276 // Example RegisterProperty("spin","maxValue", 50001, FLOAT, set, get );
277 // Native call to register the property
278 TypeRegistration.RegisterProperty(viewType.ToString(), propertyInfo.Name, propertyIndex, propertyType, _setPropertyCallback, _getPropertyCallback);
281 NUILog.Debug("property name = " + propertyInfo.Name);
287 /// Called directly from DALi C++ type registry to create a control (view) using no marshalling.
289 /// <returns>Pointer to the control (views) handle.</returns>
290 /// <param name="cPtrControlName">C pointer to the control (view) name.</param>
291 private static IntPtr CreateControl(IntPtr cPtrControlName)
293 string controlName = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(cPtrControlName);
295 NUILog.Debug("Create controlled called from C++ create a " + controlName);
297 Func<CustomView> controlConstructor;
299 // find the control constructor
300 if (Instance._constructorMap.TryGetValue(controlName, out controlConstructor))
302 // Create the control
303 CustomView newControl = controlConstructor();
304 if (newControl != null)
306 return newControl.GetPtrfromView(); // return pointer to handle
315 throw new global::System.InvalidOperationException("C# View not registererd with ViewRegistry" + controlName);
319 private static IntPtr GetProperty(IntPtr controlPtr, IntPtr propertyName)
321 string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(propertyName);
322 return Instance.GetPropertyValue(controlPtr, name);
325 private static void SetProperty(IntPtr controlPtr, IntPtr propertyName, IntPtr propertyValue)
327 string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(propertyName);
329 NUILog.Debug("SetControlProperty called for:" + name);
331 Instance.SetPropertyValue(controlPtr, name, propertyValue);
334 private Tizen.NUI.PropertyType GetDaliPropertyType(string cSharpTypeName)
336 Tizen.NUI.PropertyType daliType;
337 if (_daliPropertyTypeLookup.TryGetValue(cSharpTypeName, out daliType))
339 NUILog.Debug("mapped " + cSharpTypeName + " to dAli type " + daliType);
345 NUILog.Debug("Failed to find a mapping between C# property" + cSharpTypeName + " and DALi type");
347 return PropertyType.None;
352 /// Gets a property value from a view.
354 private IntPtr GetPropertyValue(IntPtr refObjectPtr, string propertyName)
356 // Get the C# control that maps to the C++ control
357 View view = Registry.GetManagedBaseHandleFromRefObject(refObjectPtr) as View;
360 // call the get property function
361 System.Object val = view.GetType().GetProperty(propertyName).GetAccessors()[0].Invoke(view, null);
363 PropertyValue value = PropertyValue.CreateFromObject(val);
364 IntPtr ptr = (IntPtr)PropertyValue.getCPtr(value);
376 /// Sets a property value on a view.
378 private void SetPropertyValue(IntPtr refObjectPtr, string propertyName, IntPtr propertyValuePtr)
380 // Get the C# control that maps to the C++ control
381 NUILog.Debug("SetPropertyValue refObjectPtr = {0:X}" + refObjectPtr);
383 PropertyValue propValue = new PropertyValue(propertyValuePtr, false);
385 // Get the C# control that maps to the C++ control
386 View view = Registry.GetManagedBaseHandleFromRefObject(refObjectPtr) as View;
389 System.Reflection.PropertyInfo propertyInfo = view.GetType().GetProperty(propertyName);
390 // We know the property name, we know it's type, we just need to convert from a DALi property value to native C# type
391 System.Type type = propertyInfo.PropertyType;
394 if (type.Equals(typeof(Int32)))
397 ok = propValue.Get(out value);
400 propertyInfo.SetValue(view, value);
403 else if (type.Equals(typeof(bool)))
406 ok = propValue.Get(out value);
409 propertyInfo.SetValue(view, value);
412 else if (type.Equals(typeof(float)))
415 ok = propValue.Get(out value);
418 propertyInfo.SetValue(view, value);
421 else if (type.Equals(typeof(string)))
424 ok = propValue.Get(out value);
427 propertyInfo.SetValue(view, value);
430 else if (type.Equals(typeof(Vector2)))
432 Vector2 value = new Vector2();
433 ok = propValue.Get(value);
436 propertyInfo.SetValue(view, value);
440 else if (type.Equals(typeof(Vector3)))
442 Vector3 value = new Vector3();
443 ok = propValue.Get(value);
446 propertyInfo.SetValue(view, value);
450 else if (type.Equals(typeof(Vector4)))
452 Vector4 value = new Vector4();
453 ok = propValue.Get(value);
457 propertyInfo.SetValue(view, value);
461 else if (type.Equals(typeof(Position)))
463 Position value = new Position();
464 ok = propValue.Get(value);
467 propertyInfo.SetValue(view, value);
471 else if (type.Equals(typeof(Size)))
473 Size value = new Size();
474 ok = propValue.Get(value);
477 Size sz = new Size(value.Width, value.Height, value.Depth);
478 propertyInfo.SetValue(view, sz);
483 else if (type.Equals(typeof(Color)))
485 // Colors are stored as Vector4's in DALi
486 Color value = new Color();
487 ok = propValue.Get(value);
490 propertyInfo.SetValue(view, (Color)value);
494 else if (type.Equals(typeof(PropertyMap)))
496 PropertyMap map = new PropertyMap();
497 ok = propValue.Get(map);
500 propertyInfo.SetValue(view, map);
504 else if (type.Equals(typeof(PropertyArray)))
506 PropertyArray array = new PropertyArray();
507 ok = propValue.Get(array);
510 propertyInfo.SetValue(view, array);
516 throw new global::System.InvalidOperationException("SetPropertyValue Unimplemented type for Property Value for " + type.FullName);
520 throw new global::System.InvalidOperationException("SetPropertyValue propValue.Get failed");
525 throw new global::System.InvalidOperationException("failed to find the control to write a property to: cptr = " + refObjectPtr);