2 using System.Runtime.InteropServices;
3 using System.Collections.Generic;
8 /// Add this attribute to any property belonging to a View (control) you want to be scriptable from JSON
13 /// class MyView : public CustomView
15 /// [ScriptableProperty()]
16 /// public int MyProperty
20 /// return _myProperty;
24 /// _myProperty = value;
29 /// Internally the following occurs for property registration ( this only occurs once per Type, not per Instance):
31 /// - The controls static constructor should call ViewRegistry.Register() (only called once for the lifecycle of the app)
32 /// - Within Register() the code will introspect the Controls properties, looking for the ScriptableProperty() attribute
33 /// - For every property with the ScriptableProperty() attribute, TypeRegistration.RegisterProperty is called.
34 /// - TypeRegistration.RegisterProperty calls in to DALi C++ Code Dali::CSharpTypeRegistry::RegisterProperty()
35 /// - 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).
37 /// The DALi C# example
39 /// class MyView : public CustomView
42 /// [ScriptableProperty()]
43 /// public double Hours
45 /// get { return seconds / 3600; }
46 /// set { seconds = value * 3600; }
50 /// Equivalent code in DALi C++:
52 /// class MyControl : public Control
58 /// HOURS = Control::CONTROL_PROPERTY_END_INDEX + 1
63 /// in MyControl-impl.cpp
65 /// DALI_TYPE_REGISTRATION_BEGIN( Toolkit::MyControl, Toolkit::Control, Create );
66 /// DALI_PROPERTY_REGISTRATION( Toolkit, MyControl, "Hours", INTEGER, DISABLED )
67 /// DALI_TYPE_REGISTRATION_END()
71 public class ScriptableProperty : System.Attribute
73 public enum ScriptableType
75 Default, // Read Writable, non-animatable property, event thread only
76 // Animatable // Animatable property, Currently disabled, UK
78 public readonly ScriptableType type;
80 public ScriptableProperty(ScriptableType type = ScriptableType.Default )
87 /// View Registry singleton.
88 /// Used for registering controls and any scriptable properties they have ( see ScriptableProperty )
90 /// Internal Design from C# to C++
92 /// - Each custom C# view should have it's static constructor called before any JSON file is loaded.
93 /// Static constructors for a class will only run once ( they are run per control type, not per instance).
94 /// Example of running a static constructor:
95 /// System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (typeof(Spin).TypeHandle);
96 /// Inside the static constructor the control should register it's type with the ViewRegistry
101 /// ViewRegistry.Instance.RegisterControl("Spin", CreateInstance, typeof(Spin) );
104 /// The control should also provide a CreateInstance function, which gets passed to the ViewRegistry
105 /// // Eventually it will be called if DALi Builderfinds a Spin control in a JSON file
106 /// static CustomView CreateInstance()
108 /// return new Spin();
113 /// The DALi C++ equivalent of this is
115 /// TypeRegistration mType( typeid(Toolkit::Spin), typeid(Toolkit::Control), CreateInstance );
120 public sealed class ViewRegistry
123 /// ViewRegistry is a singleton
125 private static ViewRegistry instance = null;
127 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
128 delegate IntPtr CreateControlDelegate( IntPtr cPtrControlName );
130 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
131 delegate IntPtr GetPropertyDelegate( IntPtr controlPtr, IntPtr propertyName );
133 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
134 delegate void SetPropertyDelegate( IntPtr controlPtr, IntPtr propertyName, IntPtr propertyValue );
136 private CreateControlDelegate _createCallback;
137 private SetPropertyDelegate _setPropertyCallback;
138 private GetPropertyDelegate _getPropertyCallback;
139 private PropertyRangeManager _propertyRangeManager;
142 /// Given a C++ custom control the dictionary allows us to find what CustomView it belongs to
144 private Dictionary<IntPtr, Dali.CustomView> _controlMap;
147 // Maps the name of a custom view to a create instance function
148 /// E.g. given a string "Spin", we can get a function used to create the Spin View.
150 private Dictionary<String, Func< CustomView > > _constructorMap;
153 /// Lookup table to match C# types to DALi types, used for the automatic property registration
155 private static readonly Dictionary<string, Dali.Property.Type> _daliPropertyTypeLookup
156 = new Dictionary< string, Dali.Property.Type >
158 { "float", Property.Type.FLOAT },
159 { "int", Property.Type.INTEGER },
160 { "Int32", Property.Type.INTEGER },
161 { "Boolean", Property.Type.BOOLEAN },
162 { "string", Property.Type.STRING },
163 { "Vector2", Property.Type.VECTOR2 },
164 { "Vector3", Property.Type.VECTOR3 },
165 { "Vector4", Property.Type.VECTOR4 },
166 { "Size", Property.Type.VECTOR2 },
167 { "Position",Property.Type.VECTOR3 },
168 { "Color", Property.Type.VECTOR4 },
169 // { "Matrix3", Property.Type.MATRIX3 }, commented out until we need to use Matrices from JSON
170 // { "Matrix", Property.Type.MATRIX },
174 public ViewRegistry()
176 _createCallback = new CreateControlDelegate( CreateControl );
177 _getPropertyCallback = new GetPropertyDelegate (GetProperty);
178 _setPropertyCallback = new SetPropertyDelegate (SetProperty);
180 _controlMap = new Dictionary<IntPtr, CustomView>();
181 _constructorMap = new Dictionary<string, Func<CustomView>>();
182 _propertyRangeManager = new PropertyRangeManager();
186 private Dali.Property.Type GetDaliPropertyType( string cSharpTypeName )
188 Dali.Property.Type daliType;
189 if ( _daliPropertyTypeLookup.TryGetValue (cSharpTypeName, out daliType) )
191 //Console.WriteLine("mapped "+ cSharpTypeName + " to dAli type " +daliType );
196 // Console.WriteLine("Failed to find a mapping between C# property" + cSharpTypeName +" and DALi type");
197 return Property.Type.NONE;
202 /// Called directly from DALi C++ type registry to create a control (View) uses 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);
209 // Console.WriteLine ("Create controlled called from C++ create a " + controlName);
211 Func< CustomView > controlConstructor;
213 // find the control constructor
214 if ( Instance._constructorMap.TryGetValue (controlName, out controlConstructor) )
216 // Create the control
217 CustomView newControl = controlConstructor ();
219 // Store the mapping between this instance of the custom control and native part
220 // We store a pointer to the RefObject for the control
221 IntPtr cPtr = newControl.GetPtrfromActor();
222 RefObject refObj = newControl.GetObjectPtr ();
223 IntPtr refCptr = (IntPtr) RefObject.getCPtr(refObj);
225 //Console.WriteLine ("________Storing ref object cptr in control map Hex: {0:X}", refCptr);
226 Instance._controlMap.Add (refCptr , newControl );
228 return cPtr; // return pointer to handle
232 throw new global::System.InvalidOperationException("C# View not registererd with ViewRegistry"+ controlName );
237 private static IntPtr GetProperty( IntPtr controlPtr, IntPtr propertyName )
239 string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (propertyName);
240 return Instance.GetPropertyValue ( controlPtr, name);
243 private static void SetProperty( IntPtr controlPtr, IntPtr propertyName, IntPtr propertyValue )
245 string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (propertyName);
246 //Console.WriteLine ( SetControlProperty called for:" + name );
247 Instance.SetPropertyValue ( controlPtr, name, propertyValue);
251 public static ViewRegistry Instance
257 instance = new ViewRegistry();
263 public static CustomView GetCustomViewFromActor( Actor actor )
265 // we store a dictionary of ref-obects (C++ land) to custom views (C# land)
266 Dali.CustomView view;
268 RefObject refObj = actor.GetObjectPtr ();
269 IntPtr refObjectPtr = (IntPtr) RefObject.getCPtr(refObj);
271 if ( Instance._controlMap.TryGetValue ( refObjectPtr, out view) )
274 // call the get property function
286 /// Function which registers a view and all it's scriptable properties with DALi's type registry.
287 /// Means the View can be created / configured from a JSON script.
289 /// The function uses introspection to scan a Views C# properties, then selects the ones with
290 ///[ScriptableProperty] attribute to be registered.
291 /// Example of a Spin view registering itself
294 /// ViewRegistry registers control type with DALi type registery
295 /// also uses introspection to find any properties that need to be registered with type registry
296 /// ViewRegistry.Instance.Register("Spin", CreateInstance, typeof(Spin) );
300 public void Register(string viewName, Func< CustomView > createFunction, System.Type viewType )
302 // add the mapping between the view name and it's create function
303 _constructorMap.Add (viewName, createFunction);
305 // Call into DALi C++ to register the control with the type registry
306 TypeRegistration.RegisterControl( viewName, _createCallback );
308 // Cycle through each property in the class
309 foreach (System.Reflection.PropertyInfo propertyInfo in viewType.GetProperties())
312 if ( propertyInfo.CanRead )
315 System.Attribute[] attrs = System.Attribute.GetCustomAttributes(propertyInfo);
316 foreach (System.Attribute attr in attrs)
318 // If the Scriptable attribute exists, then register it with the type registry.
319 if (attr is ScriptableProperty)
321 //Console.WriteLine ("Got a DALi JSON scriptable property = " + propertyInfo.Name +", of type " + propertyInfo.PropertyType.Name);
323 // first get the attribute type, ( default, or animatable)
324 ScriptableProperty scriptableProp = attr as ScriptableProperty;
326 // 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)
327 int propertyIndex = _propertyRangeManager.GetPropertyIndex( viewName, viewType, scriptableProp.type );
329 // get the enum for the property type... E.g. registering a string property returns Dali.PropertyType.String
330 Dali.Property.Type propertyType = GetDaliPropertyType( propertyInfo.PropertyType.Name );
332 // Example RegisterProperty("spin","maxValue", 50001, FLOAT, set, get );
333 // Native call to register the property
334 TypeRegistration.RegisterProperty (viewName, propertyInfo.Name , propertyIndex, propertyType, _setPropertyCallback, _getPropertyCallback);
337 // Console.WriteLine ("property name = " + propertyInfo.Name);
343 /// Get a property value from a View
346 private IntPtr GetPropertyValue ( IntPtr controlPtr, string propertyName)
348 // Get the C# control that maps to the C++ control
349 Dali.CustomView view;
351 BaseHandle baseHandle = new BaseHandle (controlPtr, false);
353 RefObject refObj = baseHandle.GetObjectPtr ();
355 IntPtr refObjectPtr = (IntPtr) RefObject.getCPtr(refObj);
357 if ( _controlMap.TryGetValue ( refObjectPtr, out view) )
360 // call the get property function
361 System.Object val = view.GetType ().GetProperty (propertyName).GetAccessors () [0].Invoke (view, null);
363 Property.Value value = Property.Value.CreateFromObject (val);
365 return (IntPtr)Property.Value.getCPtr (value);
374 /// Set a property value on a View
377 private void SetPropertyValue ( IntPtr controlPtr, string propertyName, IntPtr propertyValuePtr)
379 // Get the C# control that maps to the C++ control
380 Dali.CustomView view;
382 //Console.WriteLine ("SetPropertyValue refObjectPtr = {0:X}", controlPtr);
384 Property.Value propValue = new Property.Value (propertyValuePtr, false);
386 if ( _controlMap.TryGetValue ( controlPtr, out view) )
389 System.Reflection.PropertyInfo propertyInfo = view.GetType().GetProperty(propertyName);
391 // We know the property name, we know it's type, we just need to convert from a DALi property value to native C# type
392 System.Type type = propertyInfo.PropertyType;
395 if ( type.Equals (typeof(Int32)) )
398 ok = propValue.Get( ref value );
401 propertyInfo.SetValue (view, value);
404 else if ( type.Equals (typeof(bool)) )
407 ok = propValue.Get( ref value );
410 propertyInfo.SetValue (view, value);
413 else if ( type.Equals (typeof(float)) )
416 ok = propValue.Get( ref value );
419 propertyInfo.SetValue (view, value);
422 else if ( type.Equals (typeof(string)) )
425 ok = propValue.Get( out value );
428 propertyInfo.SetValue (view, value);
431 else if ( type.Equals (typeof(Vector2)) )
433 Vector2 value = new Vector2 ();
434 ok = propValue.Get( value );
437 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);
449 else if ( type.Equals (typeof(Vector4)) )
451 Vector4 value = new Vector4 ();
452 ok = propValue.Get( value );
456 propertyInfo.SetValue (view, value);
459 else if ( type.Equals (typeof(Position)) )
461 Position value = new Position ();
462 ok = propValue.Get( value );
465 propertyInfo.SetValue (view, value);
468 else if ( type.Equals (typeof(Size)) )
470 // DALi sizes are Vector3
471 Vector3 value = new Vector3();
472 ok = propValue.Get( value );
475 propertyInfo.SetValue(view, new Size(value.X,value.Y));
478 else if ( type.Equals (typeof(Color)) )
480 // Colors are stored as Vector4's in DALi
481 Vector4 value = new Vector4();
482 ok = propValue.Get( value );
485 propertyInfo.SetValue (view, (Color)value);
490 throw new global::System.InvalidOperationException("SetPropertyValue Unimplemented type for Property Value");
494 throw new global::System.InvalidOperationException("SetPropertyValue propValue.Get failed");
499 throw new global::System.InvalidOperationException("failed to find the control to write a property to: cptr = " + controlPtr);