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.Register(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++ control the dictionary allows us to find which C# control (View) it belongs to
144 private Dictionary<IntPtr, Dali.View> _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, View>();
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) 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);
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 ();
218 return newControl.GetPtrfromActor(); // return pointer to handle
222 throw new global::System.InvalidOperationException("C# View not registererd with ViewRegistry"+ controlName );
228 /// Store the mapping between this instance of control (View) and native part.
230 /// <param name="view"> The instance of control (View)</param>
231 public static void RegisterView( View view )
233 // We store a pointer to the RefObject for the control
234 RefObject refObj = view.GetObjectPtr();
235 IntPtr refCptr = (IntPtr) RefObject.getCPtr(refObj);
237 //Console.WriteLine ("________Storing ref object cptr in control map Hex: {0:X}", refCptr);
238 if ( !Instance._controlMap.ContainsKey(refCptr) )
240 Instance._controlMap.Add(refCptr, view );
246 private static IntPtr GetProperty( IntPtr controlPtr, IntPtr propertyName )
248 string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (propertyName);
249 return Instance.GetPropertyValue ( controlPtr, name);
252 private static void SetProperty( IntPtr controlPtr, IntPtr propertyName, IntPtr propertyValue )
254 string name = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (propertyName);
255 //Console.WriteLine ( SetControlProperty called for:" + name );
256 Instance.SetPropertyValue ( controlPtr, name, propertyValue);
260 public static ViewRegistry Instance
266 instance = new ViewRegistry();
272 public static View GetViewFromActor( Actor actor )
274 // we store a dictionary of ref-obects (C++ land) to custom views (C# land)
277 RefObject refObj = actor.GetObjectPtr ();
278 IntPtr refObjectPtr = (IntPtr) RefObject.getCPtr(refObj);
280 if ( Instance._controlMap.TryGetValue ( refObjectPtr, out view) )
292 /// Function which registers a view and all it's scriptable properties with DALi's type registry.
293 /// Means the View can be created / configured from a JSON script.
295 /// The function uses introspection to scan a Views C# properties, then selects the ones with
296 ///[ScriptableProperty] attribute to be registered.
297 /// Example of a Spin view registering itself
300 /// ViewRegistry registers control type with DALi type registery
301 /// also uses introspection to find any properties that need to be registered with type registry
302 /// ViewRegistry.Instance.Register(CreateInstance, typeof(Spin) );
306 public void Register(Func< CustomView > createFunction, System.Type viewType )
308 // add the mapping between the view name and it's create function
309 _constructorMap.Add (viewType.Name, createFunction);
311 // Call into DALi C++ to register the control with the type registry
312 TypeRegistration.RegisterControl( viewType.Name, _createCallback );
314 // Cycle through each property in the class
315 foreach (System.Reflection.PropertyInfo propertyInfo in viewType.GetProperties())
318 if ( propertyInfo.CanRead )
321 System.Attribute[] attrs = System.Attribute.GetCustomAttributes(propertyInfo);
322 foreach (System.Attribute attr in attrs)
324 // If the Scriptable attribute exists, then register it with the type registry.
325 if (attr is ScriptableProperty)
327 //Console.WriteLine ("Got a DALi JSON scriptable property = " + propertyInfo.Name +", of type " + propertyInfo.PropertyType.Name);
329 // first get the attribute type, ( default, or animatable)
330 ScriptableProperty scriptableProp = attr as ScriptableProperty;
332 // 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)
333 int propertyIndex = _propertyRangeManager.GetPropertyIndex( viewType.Name, viewType, scriptableProp.type );
335 // get the enum for the property type... E.g. registering a string property returns Dali.PropertyType.String
336 Dali.Property.Type propertyType = GetDaliPropertyType( propertyInfo.PropertyType.Name );
338 // Example RegisterProperty("spin","maxValue", 50001, FLOAT, set, get );
339 // Native call to register the property
340 TypeRegistration.RegisterProperty (viewType.Name, propertyInfo.Name , propertyIndex, propertyType, _setPropertyCallback, _getPropertyCallback);
343 // Console.WriteLine ("property name = " + propertyInfo.Name);
349 /// Get a property value from a View
352 private IntPtr GetPropertyValue ( IntPtr controlPtr, string propertyName)
354 // Get the C# control that maps to the C++ control
357 BaseHandle baseHandle = new BaseHandle (controlPtr, false);
359 RefObject refObj = baseHandle.GetObjectPtr ();
361 IntPtr refObjectPtr = (IntPtr) RefObject.getCPtr(refObj);
363 if ( _controlMap.TryGetValue ( refObjectPtr, out view) )
365 // call the get property function
366 System.Object val = view.GetType ().GetProperty (propertyName).GetAccessors () [0].Invoke (view, null);
368 Property.Value value = Property.Value.CreateFromObject (val);
370 return (IntPtr)Property.Value.getCPtr (value);
379 /// Set a property value on a View
382 private void SetPropertyValue ( IntPtr controlPtr, string propertyName, IntPtr propertyValuePtr)
384 // Get the C# control that maps to the C++ control
387 //Console.WriteLine ("SetPropertyValue refObjectPtr = {0:X}", controlPtr);
389 Property.Value propValue = new Property.Value (propertyValuePtr, false);
391 if ( _controlMap.TryGetValue ( controlPtr, out view) )
394 System.Reflection.PropertyInfo propertyInfo = view.GetType().GetProperty(propertyName);
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
397 System.Type type = propertyInfo.PropertyType;
400 if ( type.Equals (typeof(Int32)) )
403 ok = propValue.Get( ref value );
406 propertyInfo.SetValue (view, value);
409 else if ( type.Equals (typeof(bool)) )
412 ok = propValue.Get( ref value );
415 propertyInfo.SetValue (view, value);
418 else if ( type.Equals (typeof(float)) )
421 ok = propValue.Get( ref value );
424 propertyInfo.SetValue (view, value);
427 else if ( type.Equals (typeof(string)) )
430 ok = propValue.Get( out value );
433 propertyInfo.SetValue (view, value);
436 else if ( type.Equals (typeof(Vector2)) )
438 Vector2 value = new Vector2 ();
439 ok = propValue.Get( value );
442 propertyInfo.SetValue (view, value);
445 else if ( type.Equals (typeof(Vector3)) )
447 Vector3 value = new Vector3 ();
448 ok = propValue.Get( value );
451 propertyInfo.SetValue (view, value);
454 else if ( type.Equals (typeof(Vector4)) )
456 Vector4 value = new Vector4 ();
457 ok = propValue.Get( value );
461 propertyInfo.SetValue (view, value);
464 else if ( type.Equals (typeof(Position)) )
466 Position value = new Position ();
467 ok = propValue.Get( value );
470 propertyInfo.SetValue (view, value);
473 else if ( type.Equals (typeof(Size)) )
475 // DALi sizes are Vector3
476 Vector3 value = new Vector3();
477 ok = propValue.Get( value );
480 propertyInfo.SetValue(view, new Size(value.X,value.Y));
483 else if ( type.Equals (typeof(Color)) )
485 // Colors are stored as Vector4's in DALi
486 Vector4 value = new Vector4();
487 ok = propValue.Get( value );
490 propertyInfo.SetValue (view, (Color)value);
495 throw new global::System.InvalidOperationException("SetPropertyValue Unimplemented type for Property Value");
499 throw new global::System.InvalidOperationException("SetPropertyValue propValue.Get failed");
504 throw new global::System.InvalidOperationException("failed to find the control to write a property to: cptr = " + controlPtr);